From b4a3b98856fdf3813b1dc9003f478ce7495b13bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 03:45:17 +0800 Subject: [PATCH 001/612] =?UTF-8?q?=E6=9B=BF=E6=8D=A2go=20module=EF=BC=8C?= =?UTF-8?q?=E7=A1=AE=E4=BF=9Dimport=E6=97=B6=E4=BD=BF=E7=94=A8fork?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 3 ++- go.sum | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f7f08496f..deaae6f80 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/Mrs4s/go-cqhttp go 1.17 +replace github.com/Mrs4s/go-cqhttp => github.com/fzls/qq_robot_go v1.0.0-beta7-fix2 + replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2.0 require ( @@ -44,7 +46,6 @@ require ( github.com/tidwall/match v1.0.3 // indirect github.com/tidwall/pretty v1.1.0 // indirect github.com/willf/bitset v1.2.0 // indirect - golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect diff --git a/go.sum b/go.sum index 753408f31..8b5625bef 100644 --- a/go.sum +++ b/go.sum @@ -136,8 +136,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= From 518815eb9d861e99138608b68e8010879cbdc53a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 03:45:29 +0800 Subject: [PATCH 002/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 295 ++---------------------------------------------------- 1 file changed, 7 insertions(+), 288 deletions(-) diff --git a/README.md b/README.md index cc8eb8a5a..17d2a5187 100644 --- a/README.md +++ b/README.md @@ -1,290 +1,9 @@ -

- - go-cqhttp - -

+# qq机器人 +基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 实现的qq机器人,在其上额外增加了一个直接本地处理消息并根据配置自动回复的逻辑, 具体改动可见 *qq_robot* 目录 -
+# 使用说明 +## 配置机器人 +请参考 [go-cqhttp的文档](https://docs.go-cqhttp.org/) -# go-cqhttp - -_✨ 基于 [Mirai](https://github.com/mamoe/mirai) 以及 [MiraiGo](https://github.com/Mrs4s/MiraiGo) 的 [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md) Golang 原生实现 ✨_ - - -
- -

- - license - - - release - - - - cqhttp - - - action - - - GoReportCard - -

- -

- 文档 - · - 下载 - · - 开始使用 - · - 参与贡献 -

- - -## 兼容性 -go-cqhttp 兼容 [OneBot-v11](https://github.com/howmanybots/onebot/tree/master/v11/specs) 绝大多数内容,并在其基础上做了一些扩展,详情请看 go-cqhttp 的文档。 - -### 接口 - -- [x] HTTP API -- [x] 反向 HTTP POST -- [x] 正向 WebSocket -- [x] 反向 WebSocket - -### 拓展支持 - -> 拓展 API 可前往 [文档](docs/cqhttp.md) 查看 - -- [x] HTTP POST 多点上报 -- [x] 反向 WS 多点连接 -- [x] 修改群名 -- [x] 消息撤回事件 -- [x] 解析/发送 回复消息 -- [x] 解析/发送 合并转发 -- [x] 使用代理请求网络图片 - -### 实现 - -
-已实现 CQ 码 - -#### 符合 OneBot 标准的 CQ 码 - -| CQ 码 | 功能 | -| ------------ | --------------------------- | -| [CQ:face] | [QQ 表情] | -| [CQ:record] | [语音] | -| [CQ:video] | [短视频] | -| [CQ:at] | [@某人] | -| [CQ:share] | [链接分享] | -| [CQ:music] | [音乐分享] [音乐自定义分享] | -| [CQ:reply] | [回复] | -| [CQ:forward] | [合并转发] | -| [CQ:node] | [合并转发节点] | -| [CQ:xml] | [XML 消息] | -| [CQ:json] | [JSON 消息] | - -[qq 表情]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#qq-%E8%A1%A8%E6%83%85 -[语音]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E8%AF%AD%E9%9F%B3 -[短视频]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E7%9F%AD%E8%A7%86%E9%A2%91 -[@某人]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E6%9F%90%E4%BA%BA -[链接分享]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E9%93%BE%E6%8E%A5%E5%88%86%E4%BA%AB -[音乐分享]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E9%9F%B3%E4%B9%90%E5%88%86%E4%BA%AB- -[音乐自定义分享]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E9%9F%B3%E4%B9%90%E8%87%AA%E5%AE%9A%E4%B9%89%E5%88%86%E4%BA%AB- -[回复]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E5%9B%9E%E5%A4%8D -[合并转发]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91- -[合并转发节点]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E8%8A%82%E7%82%B9- -[xml 消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#xml-%E6%B6%88%E6%81%AF -[json 消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#json-%E6%B6%88%E6%81%AF - -#### 拓展 CQ 码及与 OneBot 标准有略微差异的 CQ 码 - -| 拓展 CQ 码 | 功能 | -| -------------- | --------------------------------- | -| [CQ:image] | [图片] | -| [CQ:redbag] | [红包] | -| [CQ:poke] | [戳一戳] | -| [CQ:gift] | [礼物] | -| [CQ:node] | [合并转发消息节点] | -| [CQ:cardimage] | [一种 xml 的图片消息(装逼大图)] | -| [CQ:tts] | [文本转语音] | - -[图片]: https://docs.go-cqhttp.org/cqcode/#%E5%9B%BE%E7%89%87 -[红包]: https://docs.go-cqhttp.org/cqcode/#%E7%BA%A2%E5%8C%85 -[戳一戳]: https://docs.go-cqhttp.org/cqcode/#%E6%88%B3%E4%B8%80%E6%88%B3 -[礼物]: https://docs.go-cqhttp.org/cqcode/#%E7%A4%BC%E7%89%A9 -[合并转发消息节点]: https://docs.go-cqhttp.org/cqcode/#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E6%B6%88%E6%81%AF%E8%8A%82%E7%82%B9 -[一种 xml 的图片消息(装逼大图)]: https://docs.go-cqhttp.org/cqcode/#cardimage -[文本转语音]: https://docs.go-cqhttp.org/cqcode/#%E6%96%87%E6%9C%AC%E8%BD%AC%E8%AF%AD%E9%9F%B3 - -
- -
-已实现 API - -#### 符合 OneBot 标准的 API - -| API | 功能 | -| ------------------------ | ---------------------- | -| /send_private_msg | [发送私聊消息] | -| /send_group_msg | [发送群消息] | -| /send_msg | [发送消息] | -| /delete_msg | [撤回信息] | -| /set_group_kick | [群组踢人] | -| /set_group_ban | [群组单人禁言] | -| /set_group_whole_ban | [群组全员禁言] | -| /set_group_admin | [群组设置管理员] | -| /set_group_card | [设置群名片(群备注)] | -| /set_group_name | [设置群名] | -| /set_group_leave | [退出群组] | -| /set_group_special_title | [设置群组专属头衔] | -| /set_friend_add_request | [处理加好友请求] | -| /set_group_add_request | [处理加群请求/邀请] | -| /get_login_info | [获取登录号信息] | -| /get_stranger_info | [获取陌生人信息] | -| /get_friend_list | [获取好友列表] | -| /get_group_info | [获取群信息] | -| /get_group_list | [获取群列表] | -| /get_group_member_info | [获取群成员信息] | -| /get_group_member_list | [获取群成员列表] | -| /get_group_honor_info | [获取群荣誉信息] | -| /can_send_image | [检查是否可以发送图片] | -| /can_send_record | [检查是否可以发送语音] | -| /get_version_info | [获取版本信息] | -| /set_restart | [重启 go-cqhttp] | -| /.handle_quick_operation | [对事件执行快速操作] | - -[发送私聊消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_private_msg-%E5%8F%91%E9%80%81%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF -[发送群消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_group_msg-%E5%8F%91%E9%80%81%E7%BE%A4%E6%B6%88%E6%81%AF -[发送消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_msg-%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF -[撤回信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#delete_msg-%E6%92%A4%E5%9B%9E%E6%B6%88%E6%81%AF -[群组踢人]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_kick-%E7%BE%A4%E7%BB%84%E8%B8%A2%E4%BA%BA -[群组单人禁言]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_ban-%E7%BE%A4%E7%BB%84%E5%8D%95%E4%BA%BA%E7%A6%81%E8%A8%80 -[群组全员禁言]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_whole_ban-%E7%BE%A4%E7%BB%84%E5%85%A8%E5%91%98%E7%A6%81%E8%A8%80 -[群组设置管理员]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_admin-%E7%BE%A4%E7%BB%84%E8%AE%BE%E7%BD%AE%E7%AE%A1%E7%90%86%E5%91%98 -[设置群名片(群备注)]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_card-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D%E7%89%87%E7%BE%A4%E5%A4%87%E6%B3%A8 -[设置群名]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_name-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D -[退出群组]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_leave-%E9%80%80%E5%87%BA%E7%BE%A4%E7%BB%84 -[设置群组专属头衔]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_special_title-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E7%BB%84%E4%B8%93%E5%B1%9E%E5%A4%B4%E8%A1%94 -[处理加好友请求]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_friend_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82 -[处理加群请求/邀请]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7 -[获取登录号信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_login_info-%E8%8E%B7%E5%8F%96%E7%99%BB%E5%BD%95%E5%8F%B7%E4%BF%A1%E6%81%AF -[获取陌生人信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_stranger_info-%E8%8E%B7%E5%8F%96%E9%99%8C%E7%94%9F%E4%BA%BA%E4%BF%A1%E6%81%AF -[获取好友列表]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_friend_list-%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B%E5%88%97%E8%A1%A8 -[获取群信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E4%BF%A1%E6%81%AF -[获取群列表]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%88%97%E8%A1%A8 -[获取群成员信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF -[获取群成员列表]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8 -[获取群荣誉信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_honor_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E8%8D%A3%E8%AA%89%E4%BF%A1%E6%81%AF -[检查是否可以发送图片]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_image-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E5%9B%BE%E7%89%87 -[检查是否可以发送语音]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_record-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E8%AF%AD%E9%9F%B3 -[获取版本信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_version_info-%E8%8E%B7%E5%8F%96%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF -[重启 go-cqhttp]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_restart-%E9%87%8D%E5%90%AF-onebot-%E5%AE%9E%E7%8E%B0 -[对事件执行快速操作]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/hidden.md#handle_quick_operation-%E5%AF%B9%E4%BA%8B%E4%BB%B6%E6%89%A7%E8%A1%8C%E5%BF%AB%E9%80%9F%E6%93%8D%E4%BD%9C - -#### 拓展 API 及与 OneBot 标准有略微差异的 API - -| 拓展 API | 功能 | -| --------------------------- | ---------------------- | -| /set_group_portrait | [设置群头像] | -| /get_image | [获取图片信息] | -| /get_msg | [获取消息] | -| /get_forward_msg | [获取合并转发内容] | -| /send_group_forward_msg | [发送合并转发(群)] | -| /.get_word_slices | [获取中文分词] | -| /.ocr_image | [图片 OCR] | -| /get_group_system_msg | [获取群系统消息] | -| /get_group_file_system_info | [获取群文件系统信息] | -| /get_group_root_files | [获取群根目录文件列表] | -| /get_group_files_by_folder | [获取群子目录文件列表] | -| /get_group_file_url | [获取群文件资源链接] | -| /get_status | [获取状态] | - -[设置群头像]: https://docs.go-cqhttp.org/api/#%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%A4%B4%E5%83%8F -[获取图片信息]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E5%9B%BE%E7%89%87%E4%BF%A1%E6%81%AF -[获取消息]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E6%B6%88%E6%81%AF -[获取合并转发内容]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E5%86%85%E5%AE%B9 -[发送合并转发(群)]: https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4 -[获取中文分词]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D-%E9%9A%90%E8%97%8F-api -[图片 ocr]: https://docs.go-cqhttp.org/api/#%E5%9B%BE%E7%89%87-ocr -[获取群系统消息]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E7%B3%BB%E7%BB%9F%E6%B6%88%E6%81%AF -[获取群文件系统信息]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BF%A1%E6%81%AF -[获取群根目录文件列表]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%A0%B9%E7%9B%AE%E5%BD%95%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8 -[获取群子目录文件列表]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%AD%90%E7%9B%AE%E5%BD%95%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8 -[获取群文件资源链接]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%96%87%E4%BB%B6%E8%B5%84%E6%BA%90%E9%93%BE%E6%8E%A5 -[获取状态]: https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%8A%B6%E6%80%81 - -
- -
-已实现 Event - -#### 符合 OneBot 标准的 Event(部分 Event 比 OneBot 标准多上报几个字段,不影响使用) - -| 事件类型 | Event | -| -------- | ---------------- | -| 消息事件 | [私聊信息] | -| 消息事件 | [群消息] | -| 通知事件 | [群文件上传] | -| 通知事件 | [群管理员变动] | -| 通知事件 | [群成员减少] | -| 通知事件 | [群成员增加] | -| 通知事件 | [群禁言] | -| 通知事件 | [好友添加] | -| 通知事件 | [群消息撤回] | -| 通知事件 | [好友消息撤回] | -| 通知事件 | [群内戳一戳] | -| 通知事件 | [群红包运气王] | -| 通知事件 | [群成员荣誉变更] | -| 请求事件 | [加好友请求] | -| 请求事件 | [加群请求/邀请] | - -[私聊信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF -[群消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#%E7%BE%A4%E6%B6%88%E6%81%AF -[群文件上传]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0 -[群管理员变动]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E7%AE%A1%E7%90%86%E5%91%98%E5%8F%98%E5%8A%A8 -[群成员减少]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E5%87%8F%E5%B0%91 -[群成员增加]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E5%A2%9E%E5%8A%A0 -[群禁言]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E7%A6%81%E8%A8%80 -[好友添加]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E5%A5%BD%E5%8F%8B%E6%B7%BB%E5%8A%A0 -[群消息撤回]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E -[好友消息撤回]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E5%A5%BD%E5%8F%8B%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E -[群内戳一戳]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E5%86%85%E6%88%B3%E4%B8%80%E6%88%B3 -[群红包运气王]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E7%BA%A2%E5%8C%85%E8%BF%90%E6%B0%94%E7%8E%8B -[群成员荣誉变更]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E8%8D%A3%E8%AA%89%E5%8F%98%E6%9B%B4 -[加好友请求]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/request.md#%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82 -[加群请求/邀请]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/request.md#%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7 - -#### 拓展 Event - -| 事件类型 | 拓展 Event | -| -------- | ---------------- | -| 通知事件 | [好友戳一戳] | -| 通知事件 | [群内戳一戳] | -| 通知事件 | [群成员名片更新] | -| 通知事件 | [接收到离线文件] | - -[好友戳一戳]: https://docs.go-cqhttp.org/event/#%E5%A5%BD%E5%8F%8B%E6%88%B3%E4%B8%80%E6%88%B3 -[群内戳一戳]: https://docs.go-cqhttp.org/event/#%E7%BE%A4%E5%86%85%E6%88%B3%E4%B8%80%E6%88%B3 -[群成员名片更新]: https://docs.go-cqhttp.org/event/#%E7%BE%A4%E6%88%90%E5%91%98%E5%90%8D%E7%89%87%E6%9B%B4%E6%96%B0 -[接收到离线文件]: https://docs.go-cqhttp.org/event/#%E6%8E%A5%E6%94%B6%E5%88%B0%E7%A6%BB%E7%BA%BF%E6%96%87%E4%BB%B6 - -
- -## 关于 ISSUE - -以下 ISSUE 会被直接关闭 - -- 提交 BUG 不使用 Template -- 询问已知问题 -- 提问找不到重点 -- 重复提问 - -> 请注意, 开发者并没有义务回复您的问题. 您应该具备基本的提问技巧。 -> 有关如何提问,请阅读[《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md) - -## 性能 - -在关闭数据库的情况下, 加载 25 个好友 128 个群运行 24 小时后内存使用为 15MB 左右. 开启数据库后内存使用将根据消息量增加 10-20MB, 如果系统内存小于 128M 建议关闭数据库使用. +## 配置自动回复逻辑 +请打开config.toml,按照注释和现有例子,自行调整配置 \ No newline at end of file From 77aca5a318e1d4c063ede34a1610abcca7eb8ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 03:46:00 +0800 Subject: [PATCH 003/612] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BF=AB=E9=80=9F=E5=90=AF=E5=8A=A8=E7=9A=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=EF=BC=8C=E6=96=B9=E4=BE=BF=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=BF=AB=E9=80=9F=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/config/config.go | 1 + global/config/default_config.yml | 2 ++ main.go | 3 +++ 3 files changed, 6 insertions(+) diff --git a/global/config/config.go b/global/config/config.go index d40375888..9505e63f2 100644 --- a/global/config/config.go +++ b/global/config/config.go @@ -40,6 +40,7 @@ type Config struct { Interval int `yaml:"interval"` } UseSSOAddress bool `yaml:"use-sso-address"` + FastStart bool `yaml:"fast-start"` } `yaml:"account"` Heartbeat struct { diff --git a/global/config/default_config.yml b/global/config/default_config.yml index ca7affdda..4fb52e804 100644 --- a/global/config/default_config.yml +++ b/global/config/default_config.yml @@ -14,6 +14,8 @@ account: # 账号相关 # 注意, 此设置可能导致在海外服务器上连接情况更差 use-sso-address: true + fast-start: false # 是否快速启动 + heartbeat: # 心跳频率, 单位秒 # -1 为关闭心跳 diff --git a/main.go b/main.go index ba33a80a1..76f843a77 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,9 @@ func main() { // 通过-c 参数替换 配置文件路径 config.DefaultConfigFile = *c conf = config.Get() + if conf.Account.FastStart { + isFastStart = true + } if *debug { conf.Output.Debug = true } From 4c32fa0a9a1dc715fdfba00ca4daa662c3a06c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 03:47:34 +0800 Subject: [PATCH 004/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E7=9A=84=E6=9C=BA=E5=99=A8=E4=BA=BA?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E7=9A=84=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 7 +++++++ qq_robot/bot.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ qq_robot/util.go | 45 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 qq_robot/bot.go create mode 100644 qq_robot/util.go diff --git a/main.go b/main.go index 76f843a77..e829b5a9b 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global/config" "github.com/Mrs4s/go-cqhttp/global/terminal" "github.com/Mrs4s/go-cqhttp/global/update" + "github.com/Mrs4s/go-cqhttp/qq_robot" "github.com/Mrs4s/go-cqhttp/server" "github.com/Mrs4s/MiraiGo/binary" @@ -377,6 +378,7 @@ func main() { coolq.RemoveReplyAt = conf.Message.RemoveReplyAt coolq.ExtraReplyData = conf.Message.ExtraReplyData coolq.SkipMimeScan = conf.Message.SkipMimeScan + for _, m := range conf.Servers { if h, ok := m["http"]; ok { hc := new(config.HTTPServer) @@ -422,6 +424,11 @@ func main() { log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") + log.Info("启动本地的机器人处理程序,用于直接收发消息和自动回复") + robot := qq_robot.NewQQRobot(bot) + robot.RegisterHandlers() + robot.Demo() + go checkUpdate() <-global.SetupMainSignalHandler() diff --git a/qq_robot/bot.go b/qq_robot/bot.go new file mode 100644 index 000000000..389808e0a --- /dev/null +++ b/qq_robot/bot.go @@ -0,0 +1,52 @@ +package qq_robot + +import ( + "github.com/Mrs4s/MiraiGo/client" + "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/go-cqhttp/coolq" +) + +type QQRobot struct { + cqBot *coolq.CQBot +} + +func NewQQRobot(cqRobot *coolq.CQBot) *QQRobot { + r := &QQRobot{ + cqBot: cqRobot, + } + + return r +} + +func (r *QQRobot) RegisterHandlers() { + //r.cqBot.Client.OnPrivateMessage(rprivateMessageEvent) + r.cqBot.Client.OnGroupMessage(r.OnGroupMessage) + //r.cqBot.Client.OnSelfPrivateMessage(rprivateMessageEvent) + //r.cqBot.Client.OnSelfGroupMessage(rgroupMessageEvent) + //r.cqBot.Client.OnTempMessage(rtempMessageEvent) + //r.cqBot.Client.OnGroupMuted(rgroupMutedEvent) + //r.cqBot.Client.OnGroupMessageRecalled(rgroupRecallEvent) + //r.cqBot.Client.OnGroupNotify(rgroupNotifyEvent) + //r.cqBot.Client.OnFriendNotify(rfriendNotifyEvent) + //r.cqBot.Client.OnMemberSpecialTitleUpdated(rmemberTitleUpdatedEvent) + //r.cqBot.Client.OnFriendMessageRecalled(rfriendRecallEvent) + //r.cqBot.Client.OnReceivedOfflineFile(rofflineFileEvent) + //r.cqBot.Client.OnJoinGroup(rjoinGroupEvent) + //r.cqBot.Client.OnLeaveGroup(rleaveGroupEvent) + //r.cqBot.Client.OnGroupMemberJoined(rmemberJoinEvent) + //r.cqBot.Client.OnGroupMemberLeaved(rmemberLeaveEvent) + //r.cqBot.Client.OnGroupMemberPermissionChanged(rmemberPermissionChangedEvent) + //r.cqBot.Client.OnGroupMemberCardUpdated(rmemberCardUpdatedEvent) + //r.cqBot.Client.OnNewFriendRequest(rfriendRequestEvent) + //r.cqBot.Client.OnNewFriendAdded(rfriendAddedEvent) + //r.cqBot.Client.OnGroupInvited(rgroupInvitedEvent) + //r.cqBot.Client.OnUserWantJoinGroup(rgroupJoinReqEvent) + //r.cqBot.Client.OnOtherClientStatusChanged(rotherClientStatusChangedEvent) + //r.cqBot.Client.OnGroupDigest(rgroupEssenceMsg) +} + +func (r *QQRobot) Demo() { +} + +func (r *QQRobot) OnGroupMessage(client *client.QQClient, m *message.GroupMessage) { +} diff --git a/qq_robot/util.go b/qq_robot/util.go new file mode 100644 index 000000000..0808f8ab9 --- /dev/null +++ b/qq_robot/util.go @@ -0,0 +1,45 @@ +package qq_robot + +import ( + "crypto/md5" + "encoding/hex" + "path" + "runtime" + + "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/go-cqhttp/coolq" + "github.com/Mrs4s/go-cqhttp/global" + log "github.com/sirupsen/logrus" +) + +const ( + maxImageSize = 1024 * 1024 * 30 // 30MB +) + +func (r *QQRobot) tryAppendImageByUrl(m *message.SendingMessage, imageUrl string) { + image, err := r._makeLocalImage(imageUrl) + if err != nil { + log.Errorf("_makeLocalImage err=%v", err) + return + } + + m.Append(image) +} + +// modified based on makeImageOrVideoElem +func (r *QQRobot) _makeLocalImage(imageUrl string) (message.IMessageElement, error) { + hash := md5.Sum([]byte(imageUrl)) + cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash[:])+".cache") + maxSize := func() int64 { + return maxImageSize + }() + exist := global.PathExists(cacheFile) + if exist { + goto hasCacheFile + } + if err := global.DownloadFileMultiThreading(imageUrl, cacheFile, maxSize, runtime.NumCPU(), nil); err != nil { + return nil, err + } +hasCacheFile: + return &coolq.LocalImageElement{File: cacheFile}, nil +} From a9d4ce4b279ed4fb98784f2b1e0a400de69ed050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 04:46:55 +0800 Subject: [PATCH 005/612] =?UTF-8?q?=E7=A7=BB=E6=A4=8Dqq=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=20-=20logger=E5=92=8Ccolor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 6 ++++++ go.sum | 41 ++++++++++++++++++++++++++++++++++++++++- qq_robot/color.go | 7 +++++++ qq_robot/log.go | 19 +++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 qq_robot/color.go create mode 100644 qq_robot/log.go diff --git a/go.mod b/go.mod index deaae6f80..b34755006 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,9 @@ require ( github.com/Microsoft/go-winio v0.5.0 github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82 github.com/dustin/go-humanize v1.0.0 + github.com/fzls/logger v1.0.1 github.com/gabriel-vasile/mimetype v1.3.1 + github.com/gookit/color v1.4.2 github.com/gorilla/websocket v1.4.2 github.com/guonaihong/gout v0.2.4 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 @@ -46,6 +48,10 @@ require ( github.com/tidwall/match v1.0.3 // indirect github.com/tidwall/pretty v1.1.0 // indirect github.com/willf/bitset v1.2.0 // indirect + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + go.uber.org/atomic v1.6.0 // indirect + go.uber.org/multierr v1.5.0 // indirect + go.uber.org/zap v1.15.0 // indirect golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect diff --git a/go.sum b/go.sum index 8b5625bef..8e8db0769 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= @@ -18,6 +19,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fzls/logger v1.0.1 h1:UTBiEYod+5By25wg2fNGeEYMXZ/+5kEJTbs1hU/H8xg= +github.com/fzls/logger v1.0.1/go.mod h1:elehpMQ+FfNfBzRUrEfx3idkTcRQ4NJZ+UMvDyQ50Xg= github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -54,9 +57,12 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk= +github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/guonaihong/gout v0.2.4 h1:BlWpWWay/Q1LkyIwupEWBZE3PMl4xzzAgMHw+OrZxBs= @@ -69,8 +75,14 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= @@ -102,6 +114,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/segmentio/asm v1.0.0 h1:GMF7/2eLkte13LERSOmPI766AJXJDRlsqqiN8T7Mfmk= github.com/segmentio/asm v1.0.0/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -113,6 +126,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= @@ -131,7 +145,18 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4= github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -139,12 +164,16 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8= @@ -165,6 +194,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -183,6 +213,11 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -204,8 +239,10 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -219,6 +256,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= diff --git a/qq_robot/color.go b/qq_robot/color.go new file mode 100644 index 000000000..1c9dbd37e --- /dev/null +++ b/qq_robot/color.go @@ -0,0 +1,7 @@ +package qq_robot + +import "github.com/gookit/color" + +func bold(c color.Color) color.Style { + return color.Style{color.Bold, c} +} diff --git a/qq_robot/log.go b/qq_robot/log.go new file mode 100644 index 000000000..42b53aa40 --- /dev/null +++ b/qq_robot/log.go @@ -0,0 +1,19 @@ +package qq_robot + +import ( + "fmt" + + mylogger "github.com/fzls/logger" +) + +var logger *mylogger.SugaredLogger + +func init() { + // 初始化日志 + var err error + logger, err = mylogger.NewLogger("logs", "qq_robot", mylogger.DebugLevel) + if err != nil { + fmt.Printf("new logger err=%v\n", err) + return + } +} From c24e132155d17d2ee5c85e18d9e16aeababd0a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 04:47:27 +0800 Subject: [PATCH 006/612] =?UTF-8?q?=E7=A7=BB=E6=A4=8Dqq=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=20-=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + README.md | 3 +- qq_robot/config.go | 268 +++++++++++++++++++++++++++++++++++ qq_robot/default_config.toml | 115 +++++++++++++++ 4 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 qq_robot/config.go create mode 100644 qq_robot/default_config.toml diff --git a/.gitignore b/.gitignore index d2ee21bf5..9d0533416 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ vendor/ .vscode config.hjson config.yml +config.toml session.token device.json data/ diff --git a/README.md b/README.md index 17d2a5187..29af7e495 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,5 @@ 请参考 [go-cqhttp的文档](https://docs.go-cqhttp.org/) ## 配置自动回复逻辑 -请打开config.toml,按照注释和现有例子,自行调整配置 \ No newline at end of file +复制qq_robot/default_config.toml到qq_robot.exe所在目录,并重命名为config.toml +然后打开qq_robot/config.go和config.toml,按照注释,自行调整配置,并添加各种规则 \ No newline at end of file diff --git a/qq_robot/config.go b/qq_robot/config.go new file mode 100644 index 000000000..e409a63dd --- /dev/null +++ b/qq_robot/config.go @@ -0,0 +1,268 @@ +package qq_robot + +import ( + "fmt" + "os" + "regexp" +) + +const TencentAiApi = "https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat" + +const ( + TemplateArgs_WorkTime = "$work_time$" // 本次工作时长 + TemplateArgs_FoodName = "$food_name$" // 食物名字 + TemplateArgs_FoodPage = "$food_page$" // 食物页码 + TemplateArgs_CurrentPeriodName = "$current_period_name$" // 当前时间段的名称 + TemplateArgs_MuteTime = "$mute_time$" // 禁言时间 + TemplateArgs_CD = "$cd$" // CD + TemplateArgs_GitVersion = "$git_version$" // 代码版本,若对应规则配置了changelog的链接,则会将这个变量替换为解析出的最新的版本号,如https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD + TemplateArgs_UpdateMessage = "$update_message$" // 最新更新信息,若对应规则配置了changelog的链接,则会将这个变量替换为解析出的最新的更新信息,如https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD +) + +type NotifyConfig struct { + Name string `toml:"name"` // 操作名称 + NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 + Message string `toml:"message"` // 通知的消息 +} + +type RobotConfig struct { + Debug bool `toml:"debug"` // 是否是调试模式 + OnStart NotifyConfig `toml:"on_start"` // 机器人上线时的操作 + OnStop NotifyConfig `toml:"on_stop"` // 机器人下线时的操作,参数:$work_time$=本次工作时长 + MaxRetryTimes int `toml:"max_retry_times"` // 单条消息处理失败后,最多重试次数 + MaxContinueEmptyLines int `toml:"max_continue_empty_lines"` // 最大允许的连续空行数目,为0则不限制 + TencentAiAppId string `toml:"tencent_ai_app_id"` // 腾讯ai开放平台的应用ID + TencentAiAppKey string `toml:"tencent_ai_app_key"` // 腾讯ai开放平台的应用秘钥 + ChatAnswerNotFoundMessage string `toml:"chat_answer_not_found_message"` // 聊天结果未找到时的提示语 + PersonalMessageNotSupportedMessage string `toml:"personal_message_not_supported_message"` // 不支持私聊时的提示语 # 本QQ是机器人,基本不会登录该QQ人工查看消息,如果有事,请私聊大号~ + PersonalMessageNotSupportedImage string `toml:"personal_message_not_supported_image"` // 不支持私聊时的图片 + PprofListenAddr string `toml:"pprof_listen_addr"` // pprof http监听地址 + EnableSellCard bool `toml:"enable_sell_card"` // 是否启用卖卡功能 + SellCardEndTime string `toml:"sell_card_end_time"` // 本次卖卡过期时间 %Y-%m-%d +} + +type RuleType string + +//const ( +// RuleType_AutoReply = "自动回复" +// RuleType_Command = "机器人指令" +// RuleType_AtSomeOne = "AT某人" +// RuleType_Food = "深夜美食" +// RuleType_Test = "测试" +//) + +const RuleTypeMaxApplyCount_Infinite = -1 + +type RuleTypeConfig struct { + Type RuleType `toml:"type"` // 规则类别 + MaxApplyCount int32 `toml:"type_max_apply_count"` // 同一条消息最多应用该类型的规则的数目,-1表示不限制 +} + +type GroupTypeConfig struct { + Type string `toml:"type"` // 群类别 + GroupIds []int64 `toml:"group_ids"` // 归属该类别的群组id列表 +} + +type ActionType string + +const ( + ActionType_Guide ActionType = "guide" + ActionType_Command ActionType = "command" + ActionType_Food ActionType = "food" + ActionType_Restart ActionType = "restart" + ActionType_AiChat ActionType = "ai_chat" + ActionType_SendUpdateMessage ActionType = "send_update_message" + ActionType_Repeater ActionType = "repeater" +) + +var ( + CommandRegex_AddWhiteList = regexp.MustCompile(`\s*AddWhiteList\s+(?P.+?)\s+(?P\d+)`) + CommandRegex_RuleNameList = regexp.MustCompile(`RuleNameList`) + CommandRegex_BuyCard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) + CommandRegex_QueryCard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) +) + +type RuleConfig struct { + Name string `toml:"name"` // 规则名称 + Type RuleType `toml:"type"` // 规则类别 + RawGroupIds []int64 `toml:"group_ids"` // 适用的QQ群ID列表 + GroupTypes []string `toml:"group_types"` // 适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + GroupIds map[int64]struct{} `toml:"-"` // + RawKeywords []string `toml:"keywords"` // 适用的关键词列表 + KeywordRegexes []*regexp.Regexp `toml:"-"` // 适用的关键词的正则表达式列表 + RawExcludeKeywords []string `toml:"exclude_keywords"` // 需要过滤的关键词列表 + ExcludeKeywordRegexes []*regexp.Regexp `toml:"-"` // 需要过滤的关键词的正则表达式列表 + AtQQs []int64 `toml:"at_qqs"` // 需要判定at的qq列表 + ExcludeQQs []int64 `toml:"exclude_qqs"` // 排除的QQ列表 + ExcludeAdmin bool `toml:"exclude_admin"` // 是否排除管理员 + Action ActionType `toml:"action"` // 动作 + SendOnJoin bool `toml:"send_on_join"` // 是否在入群时发送 + AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 当触发该规则时,需要at的qq列表 + AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 当触发该规则时,是否需要@全体成员 + GuideContent string `toml:"guide_content"` // 内容 + ImageUrl string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 + RandomImageUrls []string `toml:"random_image_urls"` // 若配置,则从中随机一个作为图片发送,同时ImageUrl配置会被覆盖 + CD int64 `toml:"cd"` // cd时长(秒),0表示不设定,若设定,在cd内触发规则时,若设置了cd内回复内容,则回复该内容,否则视为未触发 + GuideContentInCD string `toml:"guide_content_in_cd"` // cd内触发规则时的回复内容 + ForwardToQQs []int64 `toml:"forward_to_qqs"` // 将消息转发到该QQ列表 + ForwardToGroups []int64 `toml:"forward_to_groups"` // 将消息转发到该QQ群列表 + RepeatToGroups []int64 `toml:"repeat_to_groups"` // 将消息复读到该QQ群列表 + RepeatToGroupTypes []string `toml:"repeat_to_group_types"` // 复读适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + FoodSiteUrlList []string `toml:"food_site_url_list"` // 美食图片来源网站列表 + FoodDescription string `toml:"food_description"` // 美食描述,参数:$food_name$=食物名字 + RevokeMessage bool `toml:"revoke_message"` // 是否撤回该条消息 + MuteTime int64 `toml:"mute_time"` // 禁言时间,为0则表示不禁言(单位为秒) + ParseMuteTime bool `toml:"parse_mute_time"` // 是否从消息从解析想要被禁言的时间 + TimePeriods []TimePeriod `toml:"time_periods"` // 适用该规则的时间段(前者包含,后者不包含) + TriggerRuleCount int64 `toml:"trigger_rule_count"` // TriggerRuleDuration内触发的规则数目是否超过该数目 + TriggerRuleDuration int64 `toml:"trigger_rule_duration"` // 判定恶意触发机器人规则的时间周期(秒) + GitChangelogPage string `toml:"git_changelog_page"` // 某git仓库的changelog的url,若设定,则将请求这个网页,从中解析出最新的版本号和更新信息,并替换到GuideContent中的$git_version$和$update_message$ + GuideContentHasPermission string `toml:"guide_content_has_permission"` // 当有权限触发该指令时的回复 + GuideContentHasNoPermission string `toml:"guide_content_has_no_permission"` // 当无权限触发该指令时的回复 +} + +type TimePeriod struct { + // 以下任意字段不设置则不检查 + StartSecond int `toml:"start_second"` // 起始的秒(包含),0-59 + EndSecond int `toml:"end_second"` // 截止的秒(包含),0-59 + StartMinute int `toml:"start_minute"` // 起始的分钟(包含),0-59 + EndMinute int `toml:"end_minute"` // 截止的分钟(包含),0-59 + StartHour int `toml:"start_hour"` // 起始的小时(包含),0-23 + EndHour int `toml:"end_hour"` // 截止的小时(包含),0-23 + StartWeekDay int `toml:"start_weekday"` // 起始的小时(包含),1-7表示周一到周日 + EndWeekDay int `toml:"end_weekday"` // 截止的小时(包含),1-7表示周一到周日 +} + +type MiscConfig struct { + Fireworks FireworksConfig `toml:"fireworks"` + Ocr OcrConfig `toml:"ocr"` +} + +type FireworksConfig struct { + Enable bool `toml:"enable"` + Tips string `toml:"tips"` + Image string `toml:"image"` + NotifyGroups []int64 `toml:"notify_groups"` +} + +type OcrConfig struct { + Enable bool `toml:"enable"` +} + +type NotifyUpdateConfig struct { + CheckInterval int64 `toml:"check_interval"` // 检查更新的间隔(秒) + Rules []NotifyUpdateRule `toml:"rules"` // 检查规则 +} + +type NotifyUpdateRule struct { + Name string `toml:"name"` // 名称 + NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 + NotifyGroupTypes []string `toml:"notify_group_types"` // 通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + Message string `toml:"message"` // 通知的消息,参数:$git_version$=最新版本, $update_message$=更新信息 + ImageUrl string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 + GitChangelogPage string `toml:"git_changelog_page"` // git仓库的changelog的url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ + AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 需要at的qq列表 + AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 是否需要@全体成员 +} + +type Config struct { + Robot RobotConfig `toml:"robot"` + Rules []RuleConfig `toml:"rules"` + RuleTypeConfigs []RuleTypeConfig `toml:"rule_types"` + GroupTypeConfigs []GroupTypeConfig `toml:"rule_type_configs"` + Misc MiscConfig `toml:"misc"` + NotifyUpdate NotifyUpdateConfig `toml:"notify_update"` +} + +func (c *Config) Init() { + for idx := range c.Rules { + rule := &c.Rules[idx] + + for _, keyword := range rule.RawKeywords { + rule.KeywordRegexes = append(rule.KeywordRegexes, regexp.MustCompile(keyword)) + } + for _, keyword := range rule.RawExcludeKeywords { + rule.ExcludeKeywordRegexes = append(rule.ExcludeKeywordRegexes, regexp.MustCompile(keyword)) + } + + rule.GroupIds = map[int64]struct{}{} + for _, groupId := range rule.RawGroupIds { + rule.GroupIds[groupId] = struct{}{} + } + for _, groupType := range rule.GroupTypes { + for _, groupTypeCfg := range c.GroupTypeConfigs { + if groupTypeCfg.Type != groupType { + continue + } + + for _, groupId := range groupTypeCfg.GroupIds { + rule.GroupIds[groupId] = struct{}{} + } + } + } + + rule.RepeatToGroups = c.mergeGroupTypesIntoGroups(rule.RepeatToGroups, rule.RepeatToGroupTypes) + } + + for idx := range c.NotifyUpdate.Rules { + rule := &c.NotifyUpdate.Rules[idx] + + rule.NotifyGroups = c.mergeGroupTypesIntoGroups(rule.NotifyGroups, rule.NotifyGroupTypes) + } + + if err := c.check(); err != nil { + logger.Errorf("Check failed, err=%v", err) + os.Exit(-1) + } +} + +func (c *Config) mergeGroupTypesIntoGroups(groups []int64, groupTypes []string) (merged []int64) { + merged = append(merged, groups...) + + for _, groupType := range groupTypes { + for _, groupTypeCfg := range c.GroupTypeConfigs { + if groupTypeCfg.Type != groupType { + continue + } + + for _, groupId := range groupTypeCfg.GroupIds { + if InRangeInt64(groupId, merged) { + continue + } + merged = append(merged, groupId) + } + } + } + + return merged +} + +func InRangeInt64(target int64, list []int64) bool { + for _, value := range list { + if value == target { + return true + } + } + + return false +} + +func (c *Config) check() error { + for _, rule := range c.Rules { + if rule.Type == "" { + return fmt.Errorf("rule=%v type=%v type not set", rule.Name, rule.Type) + } + exists := false + for _, ruleType := range c.RuleTypeConfigs { + if rule.Type == ruleType.Type { + exists = true + break + } + } + if !exists { + return fmt.Errorf("rule=%v type=%v not valid", rule.Name, rule.Type) + } + } + + return nil +} diff --git a/qq_robot/default_config.toml b/qq_robot/default_config.toml new file mode 100644 index 000000000..09069994f --- /dev/null +++ b/qq_robot/default_config.toml @@ -0,0 +1,115 @@ +### ------------机器人配置------------------ +[robot] +# 调试模式 +debug = false +# 单条消息处理失败后,最多重试次数 +# 先设置一次,避免出现异常多次发放消息,做个保底 +max_retry_times = 0 +# 最大允许的连续空行数目,为0则不限制 +max_continue_empty_lines = 10000 +# 腾讯ai开放平台的应用ID +tencent_ai_app_id = "" +# 腾讯ai开放平台的应用秘钥 +tencent_ai_app_key = "" +# 聊天结果未找到时的提示语 +chat_answer_not_found_message = "我还小,听不懂你这些骚话(〃'▽'〃)" +# 不支持私聊时的提示语 +personal_message_not_supported_message = "" +# 不支持私聊时的图片 +personal_message_not_supported_image = "" + +# 机器人上线时的操作 +[robot.on_start] +# 操作名称 +name = "机器人上线" +# 通知的群 +notify_groups = [123456] +# 通知的消息 +message = """你们的小可爱我开始上岗啦●ヽ(゚∀゚)ノ● """ + +# 机器人下线时的操作 +[robot.on_stop] +# 操作名称 +name = "机器人下线" +# 通知的群 +notify_groups = [123456] +# 通知的消息,参数:$work_time$=本次工作时长 +message = """又是辛勤工作的一天呢,今天一共工作了$work_time$,累坏啦o(╥﹏╥)o 下班回家睡觉觉去啦(。◕ˇ∀ˇ◕),大家不要太想我哦。◕ᴗ◕。""" + +[misc] + +### ------------规则类型配置------------------ +[[rule_types]] +# 规则类别 +type = "自动回复" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = 1 + +[[rule_types]] +# 规则类别 +type = "机器人指令" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = -1 + +[[rule_types]] +# 规则类别 +type = "AT某人" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = -1 + +[[rule_types]] +# 规则类别 +type = "深夜美食" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = 1 + +[[rule_types]] +# 规则类别 +type = "重要通知" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = -1 + +[[rule_types]] +# 规则类别 +type = "测试" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = -1 + +### ------------规则类型配置------------------ +[[rule_type_configs]] +# 群类别 +type = "小号" +# 归属该类别的群组id列表 +group_ids = [123456, 789101] + + +[[rules]] +# 规则名称 +name = "测试规则" +# 规则类别 +type = "自动回复" +# 适用的QQ群ID列表 +group_ids = [123456] +# 适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 +group_types = ["小号"] +# 适用的关键词列表 +keywords = [] +# 排除的一些关键词列表 +exclude_keywords = [] +# 排除的QQ列表 +exclude_qqs = [] +# 动作 +action = "guide" +# 内容 +guide_content = """测试回复消息 +""" +# 图片URL,若有,则会额外附加图片 +image_url = "" +# 是否撤回该条消息 +revoke_message = true +# 禁言时间,为0则表示不禁言(单位为秒) +mute_time = 600 +# TriggerRuleDuration内触发的规则数目是否超过该数目 +trigger_rule_count = 5 +# 判定恶意触发机器人规则的时间周期(秒) +trigger_rule_duration = 60 From cda4b7ac6a6591a490e012e38bfbc7053a0f3588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 04:57:54 +0800 Subject: [PATCH 007/612] =?UTF-8?q?=E7=A7=BB=E6=A4=8Dqq=E6=9C=BA=E5=99=A8?= =?UTF-8?q?=E4=BA=BA=20-=20=E6=96=87=E4=BB=B6=E4=BF=A1=E6=81=AF=E5=92=8C?= =?UTF-8?q?=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 1 + qq_robot/icon.ico | Bin 0 -> 270398 bytes qq_robot/qq_robot.exe.manifest | 17 +++++++++++++ resource.syso | Bin 0 -> 271795 bytes versioninfo.json | 43 +++++++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 qq_robot/icon.ico create mode 100644 qq_robot/qq_robot.exe.manifest create mode 100644 resource.syso create mode 100644 versioninfo.json diff --git a/main.go b/main.go index e829b5a9b..da975f97e 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,4 @@ +//go:generate goversioninfo -64 package main import ( diff --git a/qq_robot/icon.ico b/qq_robot/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7aaefd3ffce88990dfcd15ff8da08e624d204582 GIT binary patch literal 270398 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

{#M9jDO1i{8zSp+Eb(t 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 H`5yTHIrJZZ literal 0 HcmV?d00001 diff --git a/qq_robot/qq_robot.exe.manifest b/qq_robot/qq_robot.exe.manifest new file mode 100644 index 000000000..5d5574b5d --- /dev/null +++ b/qq_robot/qq_robot.exe.manifest @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/resource.syso b/resource.syso new file mode 100644 index 0000000000000000000000000000000000000000..a157a9886896f19f284acce49ee02a66678afbe5 GIT binary patch literal 271795 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 c5&O4iR9>%!qm^4E(Vf^zL0W* Date: Sat, 2 Oct 2021 04:58:03 +0800 Subject: [PATCH 008/612] =?UTF-8?q?=E5=BF=BD=E7=95=A5exe=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 9d0533416..e2a255ffa 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ session.token device.json data/ logs/ + +*.exe \ No newline at end of file From 9d2561e064d5e51d870814eea0941445fe3297e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 05:00:29 +0800 Subject: [PATCH 009/612] =?UTF-8?q?=E5=BF=BD=E7=95=A5exe=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=A7=BB=E6=A4=8Dqq=E6=9C=BA=E5=99=A8=E4=BA=BA=20-=20=E8=A7=84?= =?UTF-8?q?=E5=88=99=E5=92=8C=E9=A3=9F=E8=B0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/rule.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 qq_robot/rule.go diff --git a/qq_robot/rule.go b/qq_robot/rule.go new file mode 100644 index 000000000..5a2ef6207 --- /dev/null +++ b/qq_robot/rule.go @@ -0,0 +1,66 @@ +package qq_robot + +import ( + "math/rand" + "strings" +) + +// 2020/06/01 2:42 by fzls + +var MAX_FOOD_PAGE_MAP = map[string]int64{ + "www.xinshipu.com": 30, + "home.meishichina.com": 40, +} + +type FoodImage struct { + Name string + Url string +} + +type Rule struct { + Config RuleConfig + ProcessedMessages map[int64]struct{} + SiteToFoodPage map[string]int64 // 最新食谱页面的页码数 + SiteToFetchedPage map[string]map[int64]struct{} // 当前已经抓取过的食谱页码 + CachedFoodImages map[FoodImage]struct{} // 目前缓存的食物图片信息集合 + SentFoodImages map[string]struct{} // 目前已发送过的食物的图片url的集合 +} + +func (r *Rule) UpdateFoodPage(foodSiteUrl string) { + if r.SiteToFetchedPage[foodSiteUrl] == nil { + r.SiteToFetchedPage[foodSiteUrl] = map[int64]struct{}{} + } + fetchedPage := r.SiteToFetchedPage[foodSiteUrl] + + var MAX_FOOD_PAGE int64 = 30 + for site, maxFoodPage := range MAX_FOOD_PAGE_MAP { + if strings.Contains(foodSiteUrl, site) { + MAX_FOOD_PAGE = maxFoodPage + } + } + + if len(fetchedPage) == int(MAX_FOOD_PAGE) { + return + } + + for { + foodPage := 1 + rand.Int63n(MAX_FOOD_PAGE) + if _, fetched := fetchedPage[foodPage]; !fetched { + fetchedPage[foodPage] = struct{}{} + r.SiteToFoodPage[foodSiteUrl] = foodPage + logger.Debugf("rule=%v UpdateFoodPage to %v", r.Config.Name, foodPage) + return + } + } +} + +func NewRule(config RuleConfig) *Rule { + return &Rule{ + Config: config, + ProcessedMessages: map[int64]struct{}{}, + SiteToFoodPage: map[string]int64{}, + SiteToFetchedPage: map[string]map[int64]struct{}{}, + CachedFoodImages: map[FoodImage]struct{}{}, + SentFoodImages: map[string]struct{}{}, + } +} From e21acaa9a256a328e6048d1f400abc4d9a33f0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 09:04:46 +0800 Subject: [PATCH 010/612] =?UTF-8?q?=E5=BF=BD=E7=95=A5exe=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=A7=BB=E6=A4=8Dqq=E6=9C=BA=E5=99=A8=E4=BA=BA=20-=20=E6=A0=B8?= =?UTF-8?q?=E5=BF=83=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 1 + main.go | 3 +- qq_robot/ai.go | 102 +++++ qq_robot/bot.go | 52 --- qq_robot/check_update.go | 210 ++++++++++ qq_robot/command.go | 94 +++++ qq_robot/config.go | 17 +- qq_robot/default_config.toml | 2 + qq_robot/food.go | 175 +++++++++ qq_robot/mute.go | 132 +++++++ qq_robot/qq_robot.go | 742 +++++++++++++++++++++++++++++++++++ qq_robot/util.go | 79 ++++ 12 files changed, 1555 insertions(+), 54 deletions(-) create mode 100644 qq_robot/ai.go delete mode 100644 qq_robot/bot.go create mode 100644 qq_robot/check_update.go create mode 100644 qq_robot/command.go create mode 100644 qq_robot/food.go create mode 100644 qq_robot/mute.go create mode 100644 qq_robot/qq_robot.go diff --git a/go.mod b/go.mod index b34755006..334d3ac18 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f + github.com/BurntSushi/toml v0.3.1 github.com/Microsoft/go-winio v0.5.0 github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82 github.com/dustin/go-humanize v1.0.0 diff --git a/main.go b/main.go index da975f97e..f503d25c1 100644 --- a/main.go +++ b/main.go @@ -428,7 +428,8 @@ func main() { log.Info("启动本地的机器人处理程序,用于直接收发消息和自动回复") robot := qq_robot.NewQQRobot(bot) robot.RegisterHandlers() - robot.Demo() + robot.Start() + defer robot.Stop() go checkUpdate() diff --git a/qq_robot/ai.go b/qq_robot/ai.go new file mode 100644 index 000000000..7b656252e --- /dev/null +++ b/qq_robot/ai.go @@ -0,0 +1,102 @@ +package qq_robot + +import ( + "crypto/md5" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "math/rand" + "net/url" + "sort" + "strconv" + "strings" + "time" +) + +// 2021/10/02 5:20 by fzls +type AiChatResponse struct { + RetCode int `json:"ret"` + Message string `json:"msg"` + Data AiChatResponseData `json:"data"` +} + +type AiChatResponseData struct { + Session string `json:"session"` + Answer string `json:"answer"` +} + +// 使用腾讯ai开放平台的智能闲聊接口 https://ai.qq.com/doc/nlpchat.shtml +func (r *QQRobot) aiChat(targetQQ int64, chatText string) (responseText string) { + cfg := r.Config.Robot + + params := url.Values{} + params.Set("app_id", cfg.TencentAiAppId) + params.Set("time_stamp", strconv.FormatInt(time.Now().Unix(), 10)) + params.Set("nonce_str", strconv.FormatInt(rand.Int63(), 10)) + params.Set("session", strconv.FormatInt(targetQQ, 10)) + params.Set("question", chatText) + + params.Set("sign", MakeSign(params, cfg.TencentAiAppKey)) + + resp, err := r.HttpClient.PostForm(TencentAiApi, params) + if err != nil { + logger.Debugf("aiChat(qq=%v, text=%v) params=%v post err=%v", targetQQ, chatText, params.Encode(), err) + return + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + logger.Debugf("aiChat(qq=%v, text=%v) params=%v status code=%v", targetQQ, chatText, params.Encode(), resp.StatusCode) + return + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + logger.Debugf("aiChat(qq=%v, text=%v) params=%v read body err=%v", targetQQ, chatText, params.Encode(), err) + return + } + + var result AiChatResponse + err = json.Unmarshal(body, &result) + if err != nil { + logger.Debugf("aiChat(qq=%v, text=%v) params=%v unmarshal err=%v body=%v", targetQQ, chatText, params.Encode(), err, body) + return + } + if result.RetCode != 0 { + logger.Debugf("aiChat(qq=%v, text=%v) params=%v retcode!=0, result=%v", targetQQ, chatText, params.Encode(), result) + return result.Message + } + + return result.Data.Answer +} + +func MakeSign(params url.Values, appKey string) string { + // 将请求参数对按key进行字典升序排序,得到有序的参数对列表N + keys := make([]string, 0, len(params)) + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + + // 将列表N中的参数对按URL键值对的格式拼接成字符串,得到字符串T(如:key1=value1&key2=value2),URL键值拼接过程value部分需要URL编码,URL编码算法用大写字母,例如%E8,而不是小写%e8 + var kvs []string + for _, k := range keys { + vs := params[k] + for _, v := range vs { + kvs = append(kvs, fmt.Sprintf("%v=%v", k, url.QueryEscape(v))) + } + } + + // 将应用密钥以app_key为键名,组成URL键值拼接到字符串T末尾,得到字符串S(如:key1=value1&key2=value2&app_key=密钥) + kvs = append(kvs, fmt.Sprintf("%v=%v", "app_key", appKey)) + + strToSign := strings.Join(kvs, "&") + + // 对拼接的数据库进行md5摘要,即可得sign签名 + w := md5.New() + io.WriteString(w, strToSign) + sign := fmt.Sprintf("%X", w.Sum(nil)) + + return sign +} diff --git a/qq_robot/bot.go b/qq_robot/bot.go deleted file mode 100644 index 389808e0a..000000000 --- a/qq_robot/bot.go +++ /dev/null @@ -1,52 +0,0 @@ -package qq_robot - -import ( - "github.com/Mrs4s/MiraiGo/client" - "github.com/Mrs4s/MiraiGo/message" - "github.com/Mrs4s/go-cqhttp/coolq" -) - -type QQRobot struct { - cqBot *coolq.CQBot -} - -func NewQQRobot(cqRobot *coolq.CQBot) *QQRobot { - r := &QQRobot{ - cqBot: cqRobot, - } - - return r -} - -func (r *QQRobot) RegisterHandlers() { - //r.cqBot.Client.OnPrivateMessage(rprivateMessageEvent) - r.cqBot.Client.OnGroupMessage(r.OnGroupMessage) - //r.cqBot.Client.OnSelfPrivateMessage(rprivateMessageEvent) - //r.cqBot.Client.OnSelfGroupMessage(rgroupMessageEvent) - //r.cqBot.Client.OnTempMessage(rtempMessageEvent) - //r.cqBot.Client.OnGroupMuted(rgroupMutedEvent) - //r.cqBot.Client.OnGroupMessageRecalled(rgroupRecallEvent) - //r.cqBot.Client.OnGroupNotify(rgroupNotifyEvent) - //r.cqBot.Client.OnFriendNotify(rfriendNotifyEvent) - //r.cqBot.Client.OnMemberSpecialTitleUpdated(rmemberTitleUpdatedEvent) - //r.cqBot.Client.OnFriendMessageRecalled(rfriendRecallEvent) - //r.cqBot.Client.OnReceivedOfflineFile(rofflineFileEvent) - //r.cqBot.Client.OnJoinGroup(rjoinGroupEvent) - //r.cqBot.Client.OnLeaveGroup(rleaveGroupEvent) - //r.cqBot.Client.OnGroupMemberJoined(rmemberJoinEvent) - //r.cqBot.Client.OnGroupMemberLeaved(rmemberLeaveEvent) - //r.cqBot.Client.OnGroupMemberPermissionChanged(rmemberPermissionChangedEvent) - //r.cqBot.Client.OnGroupMemberCardUpdated(rmemberCardUpdatedEvent) - //r.cqBot.Client.OnNewFriendRequest(rfriendRequestEvent) - //r.cqBot.Client.OnNewFriendAdded(rfriendAddedEvent) - //r.cqBot.Client.OnGroupInvited(rgroupInvitedEvent) - //r.cqBot.Client.OnUserWantJoinGroup(rgroupJoinReqEvent) - //r.cqBot.Client.OnOtherClientStatusChanged(rotherClientStatusChangedEvent) - //r.cqBot.Client.OnGroupDigest(rgroupEssenceMsg) -} - -func (r *QQRobot) Demo() { -} - -func (r *QQRobot) OnGroupMessage(client *client.QQClient, m *message.GroupMessage) { -} diff --git a/qq_robot/check_update.go b/qq_robot/check_update.go new file mode 100644 index 000000000..eeab76d79 --- /dev/null +++ b/qq_robot/check_update.go @@ -0,0 +1,210 @@ +package qq_robot + +import ( + "fmt" + "io/ioutil" + "regexp" + "sort" + "strconv" + "strings" + "time" + + "github.com/Mrs4s/MiraiGo/message" + "github.com/gookit/color" +) + +// 2021/10/02 5:21 by fzls + +func (r *QQRobot) checkUpdates() { + for _, rule := range r.Config.NotifyUpdate.Rules { + lastVersion := r.CheckUpdateVersionMap[rule.Name] + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogPage) + if version_less(lastVersion, latestVersion) { + // 版本有更新 + r.CheckUpdateVersionMap[rule.Name] = latestVersion + + // 排除因网络连接不好而未能在启动时正确获取版本号的情况 + if lastVersion == VersionNone { + continue + } + replies := r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) + nowStr := r.currentTime() + for _, groupId := range rule.NotifyGroups { + rspId := r.cqBot.SendGroupMessage(groupId, replies) + if rspId == -1 { + logger.Errorf("【%v Failed】 %v groupId=%v replies=%v err=%v", rule.Name, nowStr, groupId, replies, rspId) + continue + } + logger.Infof("【%v】 %v groupId=%v replies=%v", rule.Name, nowStr, groupId, replies) + } + logger.Infof("check update %v, from %v to %v", rule.Name, lastVersion, latestVersion) + } + } +} + +func (r *QQRobot) manualTriggerUpdateMessage(groupId int64) (replies *message.SendingMessage) { + for _, rule := range r.Config.NotifyUpdate.Rules { + inRange := false + for _, group := range rule.NotifyGroups { + if groupId == group { + inRange = true + break + } + } + if !inRange { + continue + } + + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogPage) + + replies = r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) + logger.Infof("manualTriggerUpdateMessage %v, version=%v", rule.Name, latestVersion) + + return replies + } + + return nil +} + +func (r *QQRobot) makeNotifyUpdatesReplies(rule NotifyUpdateRule, latestVersion string, updateMessage string) *message.SendingMessage { + // 通知相关群组 + replies := message.NewSendingMessage() + + // 在消息开头处理需要@的人 + if rule.AtAllOnTrigger { + replies.Append(message.AtAll()) + } + for _, atOnTrigger := range rule.AtQQsOnTrigger { + replies.Append(message.NewAt(atOnTrigger)) + } + msg := strings.ReplaceAll(rule.Message, TemplateArgs_GitVersion, latestVersion) + msg = strings.ReplaceAll(msg, TemplateArgs_UpdateMessage, updateMessage) + replies.Append(message.NewText(msg)) + // 如配置了图片url,则额外发送图片 + if rule.ImageUrl != "" { + r.tryAppendImageByUrl(replies, rule.ImageUrl) + } + return replies +} + +func (r *QQRobot) initCheckUpdateVersionMap() { + if len(r.Config.NotifyUpdate.Rules) == 0 { + return + } + logger.Infof(bold(color.Yellow).Render(fmt.Sprintf("将以%v的间隔定期检查配置的项目的版本更新情况", time.Second*time.Duration(r.Config.NotifyUpdate.CheckInterval)))) + for _, rule := range r.Config.NotifyUpdate.Rules { + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogPage) + r.CheckUpdateVersionMap[rule.Name] = latestVersion + logger.Infof(bold(color.Yellow).Render(fmt.Sprintf("项目[%v]当前的最新版本为%v, 更新信息如下:\n%v", rule.Name, latestVersion, updateMessage))) + } +} + +var regGitVersion = regexp.MustCompile(`([vV][0-9.]+)(\s+\d+\.\d+\.\d+)`) +var regUpdateInfo = regexp.MustCompile(`(更新公告)\s*

    ((\s|\S)+?)
`) +var regUpdateMessages = regexp.MustCompile("
  • (.+?)
  • ") + +var VersionNone = "v0.0.0" + +// github的镜像站 +var GITHUB_MIRROR_SITES = []string{ + "github.com.cnpmjs.org", + "hub.fastgit.org", +} + +func (r *QQRobot) getLatestGitVersion(gitChangelogPage string) (latestVersion string, updateMessage string) { + var urls []string + urls = append(urls, gitChangelogPage) + for _, mirrorSite := range GITHUB_MIRROR_SITES { + urls = append(urls, strings.ReplaceAll(gitChangelogPage, "github.com", mirrorSite)) + } + + for _, url := range urls { + latestVersion, updateMessage = r._getLatestGitVersion(url) + if latestVersion != VersionNone { + return + } + } + + return +} + +func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) { + resp, err := r.HttpClient.Get(gitChangelogPage) + if err != nil { + logger.Errorf("getLatestGitVersion gitChangelogPage=%v err=%v", gitChangelogPage, err) + return VersionNone, "" + } + defer resp.Body.Close() + + // 获取网页内容 + bytesData, err := ioutil.ReadAll(resp.Body) + if err != nil { + logger.Errorf("getLatestGitVersion gitChangelogPage=%v err=%v", gitChangelogPage, err) + return VersionNone, "" + } + + htmlText := string(bytesData) + + // 解析版本信息 + matches := regGitVersion.FindAllStringSubmatch(htmlText, -1) + if len(matches) == 0 { + logger.Errorf("getLatestGitVersion gitChangelogPage=%v can not find any match, html text=%v", gitChangelogPage, err, htmlText) + return VersionNone, "" + } + + var versions []string + for _, match := range matches { + versions = append(versions, match[1]) + } + + sort.Slice(versions, func(i, j int) bool { + return !version_less(versions[i], versions[j]) + }) + + // 解析更新信息 + var updateMessage string + if match := regUpdateInfo.FindStringSubmatch(htmlText); match != nil { + messagesText := match[2] + if matches := regUpdateMessages.FindAllStringSubmatch(messagesText, -1); matches != nil { + var messages []string + for idx, match := range matches { + messages = append(messages, fmt.Sprintf("%v. %v", idx+1, match[1])) + } + updateMessage = strings.Join(messages, "\n") + } + } + + return versions[0], updateMessage +} + +func version_less(version_left, version_right string) bool { + return version_less_int_list(version_to_version_int_list(version_left), version_to_version_int_list(version_right)) +} + +func version_less_int_list(version_int_list_left, version_int_list_right []int64) bool { + length := len(version_int_list_left) + if len(version_int_list_right) < length { + length = len(version_int_list_right) + } + for idx := 0; idx < length; idx++ { + if version_int_list_left[idx] != version_int_list_right[idx] { + return version_int_list_left[idx] < version_int_list_right[idx] + } + } + + return len(version_int_list_left) < len(version_int_list_right) +} + +// v3.2.2 => [3, 2, 2], 3.2.2 => [3, 2, 2] +func version_to_version_int_list(version string) []int64 { + // 移除v + if version[0] == 'v' { + version = version[1:] + } + var versionIntList []int64 + for _, subVersion := range strings.Split(version, ".") { + subVersionInt, _ := strconv.ParseInt(subVersion, 10, 64) + versionIntList = append(versionIntList, subVersionInt) + } + return versionIntList +} diff --git a/qq_robot/command.go b/qq_robot/command.go new file mode 100644 index 000000000..ed53f2607 --- /dev/null +++ b/qq_robot/command.go @@ -0,0 +1,94 @@ +package qq_robot + +import ( + "encoding/json" + "fmt" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/Mrs4s/MiraiGo/message" +) + +// 2021/10/02 5:25 by fzls +func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (err error, msg string, extraReplies []message.IMessageElement) { + var match []string + if match = CommandRegex_AddWhiteList.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_AddWhiteList.SubexpNames()) { + // full_match|ruleName|qq + ruleName := match[1] + qq, _ := strconv.ParseInt(match[2], 10, 64) + for _, rule := range r.Rules { + if ruleName != rule.Config.Name { + continue + } + rule.Config.ExcludeQQs = append(rule.Config.ExcludeQQs, qq) + logger.Info("【Command】", commandStr) + + if len(msg) != 0 { + msg += " | " + } + msg += fmt.Sprintf("已将【%v】加入到规则【%v】的白名单", qq, ruleName) + } + } else if match = CommandRegex_RuleNameList.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_RuleNameList.SubexpNames()) { + for _, rule := range r.Rules { + if _, ok := rule.Config.GroupIds[m.GroupCode]; !ok { + continue + } + + if len(msg) == 0 { + msg += "规则集合:" + } + msg += ", " + rule.Config.Name + } + } else if match = CommandRegex_BuyCard.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_BuyCard.SubexpNames()) { + now := time.Now() + endTime, _ := time.Parse("2006-01-02", r.Config.Robot.SellCardEndTime) + if !r.Config.Robot.EnableSellCard || now.After(endTime) { + return nil, "目前尚未启用卖卡功能哦", nil + } + + qq := match[1] + cardIndex := match[2] + + logger.Infof("开始调用卖卡脚本~") + cmd := exec.Command("python", "sell_cards.py", + "--run_remote", + "--target_qq", qq, + "--card_index", cardIndex, + ) + cmd.Dir = "D:\\_codes\\Python\\djc_helper_public" + out, err := cmd.Output() + + if err != nil { + return err, "", nil + } + + json.Unmarshal(out, &msg) + + if strings.Contains(msg, "成功发送以下卡片") { + image, err := r._makeLocalImage("https://z3.ax1x.com/2020/12/16/r1yWZT.png") + if err == nil { + extraReplies = append(extraReplies, image) + } + } + } else if match = CommandRegex_QueryCard.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_QueryCard.SubexpNames()) { + logger.Infof("开始查询卡片信息~") + cmd := exec.Command("python", "sell_cards.py", + "--run_remote", + "--query", + ) + cmd.Dir = "D:\\_codes\\Python\\djc_helper_public" + out, err := cmd.Output() + + if err != nil { + return err, "", nil + } + + json.Unmarshal(out, &msg) + } else { + return fmt.Errorf("没有找到该指令哦"), "", nil + } + + return nil, msg, extraReplies +} diff --git a/qq_robot/config.go b/qq_robot/config.go index e409a63dd..a295ac9d3 100644 --- a/qq_robot/config.go +++ b/qq_robot/config.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "regexp" + + "github.com/BurntSushi/toml" ) const TencentAiApi = "https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat" @@ -26,6 +28,7 @@ type NotifyConfig struct { } type RobotConfig struct { + Timeout int64 `toml:"timeout"` // http请求超时 Debug bool `toml:"debug"` // 是否是调试模式 OnStart NotifyConfig `toml:"on_start"` // 机器人上线时的操作 OnStop NotifyConfig `toml:"on_stop"` // 机器人下线时的操作,参数:$work_time$=本次工作时长 @@ -69,7 +72,6 @@ const ( ActionType_Guide ActionType = "guide" ActionType_Command ActionType = "command" ActionType_Food ActionType = "food" - ActionType_Restart ActionType = "restart" ActionType_AiChat ActionType = "ai_chat" ActionType_SendUpdateMessage ActionType = "send_update_message" ActionType_Repeater ActionType = "repeater" @@ -174,6 +176,19 @@ type Config struct { NotifyUpdate NotifyUpdateConfig `toml:"notify_update"` } +func LoadConfig() Config { + // 读取配置 + var config Config + _, err := toml.DecodeFile("config.toml", &config) + if err != nil { + logger.Fatalf("load toml file fail, err=%v", err) + } + config.Init() + // logger.Debugf("%#v", config) + + return config +} + func (c *Config) Init() { for idx := range c.Rules { rule := &c.Rules[idx] diff --git a/qq_robot/default_config.toml b/qq_robot/default_config.toml index 09069994f..72ad35778 100644 --- a/qq_robot/default_config.toml +++ b/qq_robot/default_config.toml @@ -1,5 +1,7 @@ ### ------------机器人配置------------------ [robot] +# http请求超时 +timeout = 15 # 调试模式 debug = false # 单条消息处理失败后,最多重试次数 diff --git a/qq_robot/food.go b/qq_robot/food.go new file mode 100644 index 000000000..2c21dbad9 --- /dev/null +++ b/qq_robot/food.go @@ -0,0 +1,175 @@ +package qq_robot + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "math/rand" + "regexp" + "strconv" + "strings" + + "github.com/Mrs4s/MiraiGo/message" +) + +// 2021/10/02 5:25 by fzls + +var ErrNoNewFood = fmt.Errorf("tryFetchMoreFoodImages failed, cannot get new food image that has never been sent") + +var FoodImageRegex = regexp.MustCompile(` MAX_MUTE_TIME { + return MAX_MUTE_TIME + } + + return muteTime +} + +var muteRegexMap = map[int64]*regexp.Regexp{ + 1: regexp.MustCompile(`(\d+(\.\d*)?).*秒`), + 60: regexp.MustCompile(`(\d+(\.\d*)?).*分钟`), + 60 * 15: regexp.MustCompile(`(\d+(\.\d*)?).*刻钟`), + 60 * 60: regexp.MustCompile(`(\d+(\.\d*)?).*小时`), + 60 * 60 * 24: regexp.MustCompile(`(\d+(\.\d*)?).*天`), + 60 * 60 * 24 * 7: regexp.MustCompile(`(\d+(\.\d*)?).*周`), + 60 * 60 * 24 * 30: regexp.MustCompile(`(\d+(\.\d*)?).*月`), + 60 * 60 * 24 * 30 * 4: regexp.MustCompile(`(\d+(\.\d*)?).*季度`), + 60 * 60 * 24 * 365: regexp.MustCompile(`(\d+(\.\d*)?).*年`), +} + +func parseMuteTime(messageChain []message.IMessageElement) int64 { + for _, msg := range messageChain { + if msgVal, ok := msg.(*message.TextElement); ok { + // 处理中文数字 + text := convertChineseNumber(msgVal.Content) + for baseMuteTime, muteRegex := range muteRegexMap { + if match := muteRegex.FindStringSubmatch(text); match != nil { + return int64(float64(baseMuteTime) * parseFloat64(match[1])) + } + } + } + } + + return 0 +} + +var traditionalChineseToSimplifiedChinese = map[string]string{ + "壹": "一", "贰": "二", "叁": "三", "肆": "四", "伍": "五", "陆": "六", "柒": "七", "捌": "八", "玖": "九", "拾": "十", "佰": "百", "仟": "千", +} + +var chineseDigitToArabicDigitMap = map[string]string{ + "零": "0", + "一": "1", "二": "2", "三": "3", "四": "4", "五": "5", "六": "6", "七": "7", "八": "8", "九": "9", +} + +var chineseNumberUnits = []string{"十", "百", "千", "万"} +var arbicNumberUnits = []string{"0", "00", "000", "0000"} + +// 并不正确的简单中文转阿拉伯数字,如多个单位(十百千)混用的情况会给出错误的结果 +func convertChineseNumber(originText string) string { + converted := originText + // 首先繁体转简体 + for traditional, simplified := range traditionalChineseToSimplifiedChinese { + if strings.Contains(converted, traditional) { + converted = strings.ReplaceAll(converted, traditional, simplified) + } + } + // 然后替换数字 + for chineseDigit, arabicDigit := range chineseDigitToArabicDigitMap { + if strings.Contains(converted, chineseDigit) { + converted = strings.ReplaceAll(converted, chineseDigit, arabicDigit) + } + } + // 最后将必要的单位转为0,todo:这个算法是错误的,只能处理一些简单的情况。处理中间一个零代表多个0的情况,如五千零三 + for idx, unit := range chineseNumberUnits { + if !strings.Contains(converted, unit) { + continue + } + + var replacedWith string + + utf8converted := []rune(converted) + runeUnit := []rune(unit)[0] + + // 判断是否是首个单位 + unitIndex := 0 + for runeIdx, runeChar := range utf8converted { + if runeChar == runeUnit { + unitIndex = runeIdx + continue + } + } + + // 判断这个单位右侧是否是一个阿拉伯数字 + if unitIndex+1 < len(utf8converted) && isDigit(byte(utf8converted[unitIndex+1])) { + // 根据右侧数字数目,替换0 + rightDigitCount := 0 + for runeIdx := unitIndex + 1; runeIdx < len(utf8converted); runeIdx++ { + if isDigit(byte(utf8converted[runeIdx])) { + rightDigitCount++ + } + } + zeroCount := len(arbicNumberUnits[idx]) - rightDigitCount + replacedWith = strings.Repeat("0", zeroCount) + } else { + // 否则,转为对应数目的0 + replacedWith = arbicNumberUnits[idx] + } + + // 如果左边没有其他东西,则左边加个1,如十一 => 11 + if unitIndex == 0 { + replacedWith = "1" + replacedWith + } + + converted = strings.ReplaceAll(converted, unit, replacedWith) + } + return converted +} + +func isDigit(char byte) bool { + return '0' <= char && char <= '9' +} + +func parseFloat64(str string) float64 { + f, _ := strconv.ParseFloat(str, 64) + return f +} diff --git a/qq_robot/qq_robot.go b/qq_robot/qq_robot.go new file mode 100644 index 000000000..2231ac963 --- /dev/null +++ b/qq_robot/qq_robot.go @@ -0,0 +1,742 @@ +package qq_robot + +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + "net/http" + "os/exec" + "sort" + "strconv" + "strings" + "time" + + "github.com/Mrs4s/MiraiGo/client" + "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/go-cqhttp/coolq" + "github.com/gookit/color" +) + +type MessageKey struct { + MessageId int64 + SenderQQ int64 + GroupId int64 +} + +func makeMessageKey(messageId, senderQQ, groupId int64) MessageKey { + return MessageKey{ + MessageId: messageId, + SenderQQ: senderQQ, + GroupId: groupId, + } +} + +type QQRobot struct { + cqBot *coolq.CQBot + + Config Config // 配置 + StartTime time.Time + + Rules []*Rule + RuleTypeToMessageIdToRuleApplyCount map[RuleType]map[MessageKey]int32 // 消息类型 => 消息Key => 该消息被当前类型规则处理的次数 + GroupToMemberToTriggerRuleTimes map[int64]map[int64][]int64 // group => qq => list of 触发规则的时间戳 + GroupToRuleNameToLastSuccessTriggerTimestamp map[int64]map[string]int64 // group => rulename => 上次在cd外成功触发的时间戳 + + HttpClient http.Client + + CheckUpdateVersionMap map[string]string // 配置的检查更新名称=>最近的版本号,如"DNF蚊子腿小助手更新"=>"v4.2.2" + + quitCtx context.Context + quitFunc context.CancelFunc +} + +func NewQQRobot(cqRobot *coolq.CQBot) *QQRobot { + config := LoadConfig() + + r := &QQRobot{ + cqBot: cqRobot, + + Config: config, + RuleTypeToMessageIdToRuleApplyCount: map[RuleType]map[MessageKey]int32{}, + GroupToMemberToTriggerRuleTimes: map[int64]map[int64][]int64{}, + GroupToRuleNameToLastSuccessTriggerTimestamp: map[int64]map[string]int64{}, + HttpClient: http.Client{Timeout: time.Duration(config.Robot.Timeout) * time.Second}, + CheckUpdateVersionMap: map[string]string{}, + } + for _, config := range config.Rules { + r.Rules = append(r.Rules, NewRule(config)) + } + return r +} +func (r *QQRobot) Start() { + r.StartTime = time.Now() + r.quitCtx, r.quitFunc = context.WithCancel(context.Background()) + + r.notify(r.Config.Robot.OnStart) + go r.ticker() +} + +func (r *QQRobot) Stop() { + r.notify(r.Config.Robot.OnStop) + r.quitFunc() +} + +func (r *QQRobot) notify(cfg NotifyConfig) { + msgTemplate := cfg.Message + if cfg.Name == "机器人下线" { + msgTemplate = strings.ReplaceAll(msgTemplate, TemplateArgs_WorkTime, time.Since(r.StartTime).String()) + } + if r.Config.Robot.Debug { + logger.Debug("debug mode, do not notify", cfg.Name, cfg.NotifyGroups, msgTemplate) + return + } + msg := message.NewSendingMessage() + msg.Append(message.NewText(msgTemplate)) + nowStr := r.currentTime() + for _, groupId := range cfg.NotifyGroups { + if r.cqBot.Client.FindGroup(groupId) == nil { + // 不在该群里,跳过 + continue + } + retCode := r.cqBot.SendGroupMessage(groupId, msg) + if retCode == -1 { + logger.Errorf("【%v Failed】 %v groupId=%v message=%v err=%v", cfg.Name, nowStr, groupId, msg, retCode) + return + } + logger.Infof("【%v】 %v groupId=%v message=%v", cfg.Name, nowStr, groupId, msg) + } + logger.Infof("robot on %v finished", cfg.Name) +} + +func (r *QQRobot) ticker() { + if r.Config.NotifyUpdate.CheckInterval <= 0 { + return + } + + checkUpdateTicker := time.NewTicker(time.Second * time.Duration(r.Config.NotifyUpdate.CheckInterval)) + defer checkUpdateTicker.Stop() + + r.initCheckUpdateVersionMap() + + for { + // 开始监听 + select { + case <-checkUpdateTicker.C: + r.checkUpdates() + case <-r.quitCtx.Done(): + return + } + } +} + +func (r *QQRobot) RegisterHandlers() { + // TODO: re: 添加其他事件的处理 @2021-10-02 05:35:37 + + r.cqBot.Client.OnGroupMessage(r.OnGroupMessage) + r.cqBot.Client.OnPrivateMessage(r.OnPrivateMessage) + //r.cqBot.Client.OnSelfPrivateMessage(rprivateMessageEvent) + //r.cqBot.Client.OnSelfGroupMessage(rgroupMessageEvent) + r.cqBot.Client.OnTempMessage(r.OnTempMessage) + //r.cqBot.Client.OnGroupMuted(rgroupMutedEvent) + //r.cqBot.Client.OnGroupMessageRecalled(rgroupRecallEvent) + //r.cqBot.Client.OnGroupNotify(rgroupNotifyEvent) + //r.cqBot.Client.OnFriendNotify(rfriendNotifyEvent) + //r.cqBot.Client.OnMemberSpecialTitleUpdated(rmemberTitleUpdatedEvent) + //r.cqBot.Client.OnFriendMessageRecalled(rfriendRecallEvent) + //r.cqBot.Client.OnReceivedOfflineFile(rofflineFileEvent) + //r.cqBot.Client.OnJoinGroup(rjoinGroupEvent) + //r.cqBot.Client.OnLeaveGroup(rleaveGroupEvent) + r.cqBot.Client.OnGroupMemberJoined(r.OnGroupMemberJoined) + //r.cqBot.Client.OnGroupMemberLeaved(rmemberLeaveEvent) + //r.cqBot.Client.OnGroupMemberPermissionChanged(rmemberPermissionChangedEvent) + //r.cqBot.Client.OnGroupMemberCardUpdated(rmemberCardUpdatedEvent) + //r.cqBot.Client.OnNewFriendRequest(rfriendRequestEvent) + //r.cqBot.Client.OnNewFriendAdded(rfriendAddedEvent) + //r.cqBot.Client.OnGroupInvited(rgroupInvitedEvent) + //r.cqBot.Client.OnUserWantJoinGroup(rgroupJoinReqEvent) + //r.cqBot.Client.OnOtherClientStatusChanged(rotherClientStatusChangedEvent) + //r.cqBot.Client.OnGroupDigest(rgroupEssenceMsg) +} + +func (r *QQRobot) OnGroupMessage(client *client.QQClient, m *message.GroupMessage) { + for _, rule := range r.Rules { + if err := r.applyGroupRule(m, rule); err != nil { + return + } + } +} + +func (r *QQRobot) OnGroupMemberJoined(client *client.QQClient, m *client.MemberJoinGroupEvent) { + for _, rule := range r.Rules { + if err := r.onMemberJoin(m, rule); err != nil { + return + } + } +} + +func (r *QQRobot) OnPrivateMessage(client *client.QQClient, m *message.PrivateMessage) { + r.onPrivateOrTempMessage(m.Sender.Uin, 0, 0, m) +} + +func (r *QQRobot) OnTempMessage(client *client.QQClient, m *client.TempMessageEvent) { + r.onPrivateOrTempMessage(0, m.Message.GroupCode, m.Message.Sender.Uin, m) +} + +func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { + config := rule.Config + nowStr := r.currentTime() + nowUnix := time.Now().Unix() + + groupId := m.GroupCode + senderUin := m.Sender.Uin + senderName := m.Sender.Nickname + + senderInfo, err := r.cqBot.Client.GetMemberInfo(groupId, senderUin) + isAdmin := false + if err == nil { + isAdmin = isMemberAdmin(senderInfo.Permission) + } + + if _, ok := config.GroupIds[groupId]; !ok { + return nil + } + + // 获取消息id,以及判断匹配了关键词 + var source int64 + var atGivenUsers bool + var hitKeyWords bool + var hitKeyWordString string + var triggerTooOften bool + + source = int64(m.Id) + + for _, msg := range m.Elements { + if atMsg, ok := msg.(*message.AtElement); ok { + for _, target := range config.AtQQs { + if atMsg.Target == target { + atGivenUsers = true + break + } + } + } + if msg.Type() == message.Text || msg.Type() == message.Image || msg.Type() == message.LightApp { + if !hitKeyWords { + OuterLoop: + for _, keywordRegex := range config.KeywordRegexes { + var text string + switch msgVal := msg.(type) { + case *message.TextElement: + text = msgVal.Content + case *message.LightAppElement: + text = msgVal.Content + case *message.GroupImageElement: + text = fmt.Sprintf("%v\n%v", msgVal.Url, r.ocr(msgVal)) + } + if keywordRegex.MatchString(text) { + for _, excludeKeywordRegex := range config.ExcludeKeywordRegexes { + if excludeKeywordRegex.MatchString(text) { + continue OuterLoop + } + } + + hitKeyWords = true + hitKeyWordString = keywordRegex.String() + break + } + } + } + } + } + if config.TriggerRuleCount != 0 && config.TriggerRuleDuration != 0 && r.GroupToMemberToTriggerRuleTimes[groupId] != nil { + // 计算是否这个QQ在滥用机器人功能 + var triggerCount int64 + checkStartTime := time.Now().Unix() - config.TriggerRuleDuration + triggerTimes := r.GroupToMemberToTriggerRuleTimes[groupId][senderUin] + startIdx := sort.Search(len(triggerTimes), func(i int) bool { + return triggerTimes[i] >= checkStartTime + }) + for idx := startIdx; idx < len(triggerTimes); idx++ { + if triggerTimes[idx] >= checkStartTime { + triggerCount++ + } + } + if triggerCount >= config.TriggerRuleCount { + triggerTooOften = true + } + } + + // 是否已经回复 + maybeKilledWrongPerson := false // 误杀 + if _, replied := rule.ProcessedMessages[source]; replied { + maybeKilledWrongPerson = true + logger.Warnf("【似乎消息混了,不过没办法,继续处理吧-。-】", nowStr, config.Name, p(m)) + } + // 是否不需要回复 + if len(config.KeywordRegexes) != 0 && !hitKeyWords || + len(config.AtQQs) != 0 && !atGivenUsers || + config.TriggerRuleCount != 0 && config.TriggerRuleDuration != 0 && !triggerTooOften || + !hitKeyWords && !atGivenUsers && !triggerTooOften { + return nil + } + + // 判断是否在规定的时间段内 + if len(config.TimePeriods) != 0 { + valid := false + now := time.Now() + for _, tp := range config.TimePeriods { + if now.Second() < tp.StartSecond { + continue + } + if tp.EndSecond != 0 && now.Second() > tp.EndSecond { + continue + } + if now.Minute() < tp.StartMinute { + continue + } + if tp.EndMinute != 0 && now.Minute() > tp.EndMinute { + continue + } + if now.Hour() < tp.StartHour { + continue + } + if tp.EndHour != 0 && now.Hour() > tp.EndHour { + continue + } + var weekday int + if now.Weekday() == time.Sunday { + weekday = 7 + } else { + weekday = int(now.Weekday()) + } + if tp.StartWeekDay != 0 && weekday < tp.StartWeekDay { + continue + } + if tp.EndWeekDay != 0 && weekday > tp.EndWeekDay { + continue + } + + valid = true + break + } + if !valid { + return nil + } + } + + // 判断是否是排除的用户列表 + for _, exclude_qq := range config.ExcludeQQs { + if exclude_qq == senderUin { + logger.Info("【ExcludedQQ】", nowStr, config.Name, p(m)) + return nil + } + } + if config.ExcludeAdmin && isAdmin { + logger.Info("【ExcludedAdmin】", nowStr, config.Name, p(m)) + return nil + } + + messageApplyCount := r.RuleTypeToMessageIdToRuleApplyCount[config.Type] + if messageApplyCount == nil { + messageApplyCount = map[MessageKey]int32{} + r.RuleTypeToMessageIdToRuleApplyCount[config.Type] = messageApplyCount + } + ruleTypeConfig := RuleTypeConfig{} + for _, cfg := range r.Config.RuleTypeConfigs { + if cfg.Type == config.Type { + ruleTypeConfig = cfg + break + } + } + messageKey := makeMessageKey(source, senderUin, groupId) + if ruleTypeConfig.MaxApplyCount != RuleTypeMaxApplyCount_Infinite && messageApplyCount[messageKey] >= ruleTypeConfig.MaxApplyCount { + return nil + } + + guideContent := config.GuideContent + // 判断是否在cd内触发了规则 + if config.CD != 0 && r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId] != nil { + lastTriggerTime := r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId][config.Name] + if nowUnix < lastTriggerTime+config.CD { + if len(config.GuideContentInCD) == 0 { + // 未设置cd内回复内容,则视为未触发 + logger.Info("【InCD】", nowStr, config.Name, p(m)) + return nil + } else { + // 替换回复内容为cd回复内容 + guideContent = strings.ReplaceAll(config.GuideContentInCD, TemplateArgs_CD, strconv.FormatInt(config.CD, 10)) + } + } + } + + // 记录这个QQ触发规则的时间戳 + if r.GroupToMemberToTriggerRuleTimes[groupId] == nil { + r.GroupToMemberToTriggerRuleTimes[groupId] = map[int64][]int64{} + } + r.GroupToMemberToTriggerRuleTimes[groupId][senderUin] = append(r.GroupToMemberToTriggerRuleTimes[groupId][senderUin], time.Now().Unix()) + + // 记录这个规则触发的时间戳 + if r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId] == nil { + r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId] = map[string]int64{} + } + r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId][config.Name] = nowUnix + + // ok + + // 回复消息 + + muteTime := config.MuteTime + if config.ParseMuteTime { + // 解析消息内容,判定禁言时间 + muteTime = parseMuteTime(m.Elements) + if muteTime == 0 { + // 其实是不符合禁言套餐规则,直接返回 + return nil + } + } + muteTime = truncatMuteTime(muteTime) + + // 自动回复关键词 + replies := message.NewSendingMessage() + + // 在消息开头处理需要@的人 + if config.AtAllOnTrigger { + replies.Append(message.AtAll()) + } + for _, atOnTrigger := range config.AtQQsOnTrigger { + replies.Append(message.NewAt(atOnTrigger)) + } + + switch config.Action { + case ActionType_Guide: + guideContent = strings.ReplaceAll(guideContent, TemplateArgs_MuteTime, strconv.FormatInt(muteTime, 10)) + if config.GitChangelogPage != "" { + latestVersion, updateMessage := r.getLatestGitVersion(config.GitChangelogPage) + guideContent = strings.ReplaceAll(guideContent, TemplateArgs_GitVersion, latestVersion) + guideContent = strings.ReplaceAll(guideContent, TemplateArgs_UpdateMessage, updateMessage) + } + if len(guideContent) != 0 { + replies.Append(message.NewText(guideContent)) + } + case ActionType_Command: + for _, msg := range m.Elements { + msgVal, ok := msg.(*message.TextElement) + if !ok { + continue + } + + for _, keywordRegex := range config.KeywordRegexes { + if keywordRegex.MatchString(msgVal.Content) { + commandStr := keywordRegex.ReplaceAllString(msgVal.Content, "") + err, msg, extraReplies := r.processCommand(commandStr, m) + if err != nil { + replies.Append(message.NewText(guideContent)) + replies.Elements = append(replies.Elements, extraReplies...) + errMsg := err.Error() + if e, ok := err.(*exec.ExitError); ok { + errMsg += fmt.Sprintf("\n%v", string(e.Stderr)) + } + logger.Errorf("执行指令出错, rule=%v err=%v", config.Name, errMsg) + continue + } + replies.Append(message.NewText(fmt.Sprintf("指令【%v】执行成功\n结果为【%v】\n", commandStr, msg))) + replies.Elements = append(replies.Elements, extraReplies...) + } + } + } + case ActionType_Food: + extraReplies, err := r.createFoodMessage(rule) + replies.Elements = append(replies.Elements, extraReplies.Elements...) + if err != nil { + logger.Errorf("createFoodMessage, rule=%v err=%v", config.Name, err) + replies.Append(message.NewText(guideContent)) + } + case ActionType_AiChat: + var chatText string + for _, msg := range m.Elements { + if msgVal, ok := msg.(*message.TextElement); ok { + chatText += msgVal.Content + } + } + reply := r.aiChat(senderUin, chatText) + // 特殊替换一下 + if notFoundMessage := r.Config.Robot.ChatAnswerNotFoundMessage; len(notFoundMessage) != 0 { + reply = strings.ReplaceAll(reply, "chat answer not found", notFoundMessage) + } + if reply != "" { + replies.Append(message.NewText(reply)) + } + case ActionType_SendUpdateMessage: + if isAdmin { + // 手动触发更新通知 + if res := r.manualTriggerUpdateMessage(groupId); res != nil { + replies.Elements = append(replies.Elements, res.Elements...) + } else { + replies.Append(message.NewText("当前群组没有配置检查更新哦~")) + } + } else { + replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) + } + case ActionType_Repeater: + if isAdmin { + // 复读内容到指定的群组 + // 移除首行(首行设定为关键词) + for idx := 0; idx < len(m.Elements); idx++ { + msg := m.Elements[idx] + + if msgVal, ok := msg.(*message.TextElement); ok { + lines := strings.Split(msgVal.Content, "\n") + if len(lines) > 0 { + lines[0] = fmt.Sprintf("下面由无情的复读机为您转播来自 %v 的消息:\n------------------------------", senderName) + } + msgVal.Content = strings.Join(lines, "\n") + break + } + } + for idx, repeatMessages := range r.getForwardMessagesList(m, true) { + if idx == 0 { + // 第一条转发的消息加上 @all + repeatMessages.Elements = append([]message.IMessageElement{message.AtAll()}, repeatMessages.Elements...) + } + + for _, repeatToGroup := range config.RepeatToGroups { + forwardRspId := r.cqBot.SendGroupMessage(repeatToGroup, repeatMessages) + if forwardRspId == -1 { + logger.Error(fmt.Sprintf("【RepeatToGroup(%v) Failed】", repeatToGroup), nowStr, config.Name, repeatMessages, forwardRspId) + continue + } + logger.Info(fmt.Sprintf("【RepeatToGroup(%v)】", repeatToGroup), nowStr, config.Name, repeatMessages, forwardRspId) + } + } + } else { + replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) + } + default: + if hitKeyWords { + replies.Append(message.NewText(fmt.Sprintf( + "提问前请先看群文件中【常见问题解答】与【手动安装运行环境教程】文档,如果看完仍旧不能解疑,欢迎提问。\n" + + "但是如果是文档中已回答的问题,时间有限,恕不回答.\n" + + "来自自动回复机器人~"))) + } + } + + // 如配置了图片url,则额外发送图片 + imageUrl := config.ImageUrl + if len(config.RandomImageUrls) != 0 { + randIdx := rand.Intn(len(config.RandomImageUrls)) + imageUrl = config.RandomImageUrls[randIdx] + } + if imageUrl != "" { + r.tryAppendImageByUrl(replies, imageUrl) + } + + if maybeKilledWrongPerson { + replies.Append(message.NewText("似乎前面有人代替你被误杀了。但是,正义的铁拳虽然会乱锤,却不会错过正确的人。宁可错杀三千,不可放过一人!(手动眼部红光特效)")) + r.tryAppendImageByUrl(replies, "https://s3.ax1x.com/2021/03/17/66NRi9.gif") + } + + if len(replies.Elements) != 0 { + keyWord := fmt.Sprintf("hitKeyWordString=%v", hitKeyWordString) + + // 补充reply信息 + replies.Elements = append([]message.IMessageElement{message.NewReply(m)}, replies.Elements...) + + rspId := r.cqBot.SendGroupMessage(groupId, replies) + if rspId == -1 { + logger.Error("【ReplyFail】", nowStr, config.Name, keyWord, p(m), rspId) + return err + } + logger.Info(color.Style{color.Bold, color.Green}.Renderln("【OK】", nowStr, config.Name, keyWord, p(m), source, replies, rspId)) + } + + if config.RevokeMessage { + err := r.cqBot.Client.RecallGroupMessage(groupId, m.Id, m.InternalId) + if err != nil { + logger.Error("【RevokeMessage Fail】", nowStr, config.Name, p(m), err) + } else { + logger.Info("【RevokeMessage OK】", nowStr, config.Name, p(m), source, replies) + } + } + if muteTime != 0 { + if senderInfo != nil { + err := senderInfo.Mute(uint32(muteTime)) + if err != nil { + logger.Error("【Mute Fail】", nowStr, config.Name, p(m), err) + } else { + logger.Info("【Mute OK】", nowStr, config.Name, p(m), source) + } + } else { + logger.Info("【Mute Fail Not Found】", nowStr, config.Name, p(m), source) + } + } + + // 处理转发 + if needForward := len(config.ForwardToQQs) != 0 || len(config.ForwardToGroups) != 0; needForward { + for _, forwardMessages := range r.getForwardMessagesList(m, false) { + for _, forwardToQQ := range config.ForwardToQQs { + forwardRspId := r.cqBot.SendPrivateMessage(forwardToQQ, 0, forwardMessages) + if forwardRspId == -1 { + logger.Error(fmt.Sprintf("【ForwardToQQ(%v) Failed】", forwardToQQ), nowStr, config.Name, forwardMessages, forwardRspId) + continue + } + logger.Info(fmt.Sprintf("【ForwardToQQ(%v)】", forwardToQQ), nowStr, config.Name, forwardMessages, forwardRspId) + } + for _, forwardToGroup := range config.ForwardToGroups { + forwardRspId := r.cqBot.SendGroupMessage(forwardToGroup, forwardMessages) + if forwardRspId == -1 { + logger.Error(fmt.Sprintf("【ForwardToGroup(%v) Failed】", forwardToGroup), nowStr, config.Name, forwardMessages, forwardRspId) + continue + } + logger.Info(fmt.Sprintf("【ForwardToGroup(%v)】", forwardToGroup), nowStr, config.Name, forwardMessages, forwardRspId) + } + } + } + + messageApplyCount[messageKey]++ + rule.ProcessedMessages[source] = struct{}{} + + return nil +} + +func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool) []*message.SendingMessage { + var forwardMessagesList []*message.SendingMessage + + messages := m.Elements + + for len(messages) != 0 { + forwardMessages := message.NewSendingMessage() + leftMessages := message.NewSendingMessage() + + msgSize := 0 + for _, msg := range messages { + switch msgVal := msg.(type) { + case *message.TextElement: + forwardMessages.Elements = append(forwardMessages.Elements, splitPlainMessage(msgVal.Content)...) + case *message.GroupImageElement: + r.tryAppendImageByUrl(forwardMessages, msgVal.Url) + case *message.AtElement: + if msgVal.Target != 0 { + forwardMessages.Append(message.NewText(fmt.Sprintf("@%v(%v)", msgVal.Display, msgVal.Target))) + } else { + forwardMessages.Append(message.NewText("@全体成员(转发)")) + } + case *message.FaceElement, *message.LightAppElement: + forwardMessages.Append(msg) + default: + jsonBytes, _ := json.Marshal(msg) + forwardMessages.Elements = append(forwardMessages.Elements, splitPlainMessage(fmt.Sprintf("%v\n", string(jsonBytes)))...) + } + + // 如果加了该消息后会超出单个消息大小,则先放入待定队列 + jsonBytes, _ := json.Marshal(forwardMessages.Elements[len(forwardMessages.Elements)-1]) + msgSize += len(jsonBytes) + // 需要确保每次至少转发一条消息 + if len(forwardMessages.Elements) > 1 && msgSize > maxMessageJsonSize { + forwardMessages.Elements = forwardMessages.Elements[:len(forwardMessages.Elements)-1] + msgSize -= len(jsonBytes) + leftMessages.Append(msg) + } + } + + if !forRepeat { + forwardMessages.Append(message.NewText(fmt.Sprintf(""+ + "\n"+ + "------------------------------\n"+ + "转发自 群[%v:%v] QQ[%v:%v] 时间[%v]", + m.GroupName, m.GroupCode, + m.Sender.Nickname, m.Sender.Uin, + r.currentTime(), + ))) + } + + forwardMessagesList = append(forwardMessagesList, forwardMessages) + messages = leftMessages.Elements + } + + return forwardMessagesList +} + +func (r *QQRobot) onMemberJoin(m *client.MemberJoinGroupEvent, rule *Rule) error { + config := rule.Config + nowStr := r.currentTime() + + groupId := m.Group.Code + newMemberUin := m.Member.Uin + + if _, ok := config.GroupIds[groupId]; !ok { + return nil + } + + // 判断该规则是否处理入群消息 + if !rule.Config.SendOnJoin { + return nil + } + + // ok + + // 回复消息 + replies := message.NewSendingMessage() + + // at该新入群成员 + replies.Append(message.NewAt(newMemberUin)) + + // 发送指引信息 + replies.Append(message.NewText(config.GuideContent)) + + // 如配置了图片url,则额外发送图片 + if config.ImageUrl != "" { + r.tryAppendImageByUrl(replies, config.ImageUrl) + } + + if len(replies.Elements) != 0 { + rspId := r.cqBot.SendGroupMessage(groupId, replies) + if rspId == -1 { + logger.Error("【ReplyFail】", nowStr, p(m), rspId) + return fmt.Errorf("reply fail, rspId=%v", rspId) + } + logger.Info("【OK】", nowStr, p(m.Group), 0, (replies), rspId) + } + + if muteTime := config.MuteTime; muteTime != 0 { + err := m.Member.Mute(uint32(muteTime)) + if err != nil { + logger.Error("【Mute On Join Fail】", nowStr, config.Name, p(m), err) + } else { + logger.Info("【Mute On Join OK】", nowStr, config.Name, p(m), (replies)) + } + } + + return nil +} + +func (r *QQRobot) onPrivateOrTempMessage(senderFriendUin int64, tempGroupId int64, tempUin int64, m interface{}) { + // 回复消息 + replies := message.NewSendingMessage() + + // 发送指引信息 + cfg := r.Config.Robot + if cfg.PersonalMessageNotSupportedMessage != "" { + replies.Append(message.NewText(cfg.PersonalMessageNotSupportedMessage)) + } + + if cfg.PersonalMessageNotSupportedImage != "" { + r.tryAppendImageByUrl(replies, cfg.PersonalMessageNotSupportedImage) + } + + if len(replies.Elements) == 0 { + return + } + + var rspId int32 + if senderFriendUin != 0 { + rspId = r.cqBot.SendPrivateMessage(senderFriendUin, 0, replies) + } else { + rspId = r.cqBot.SendPrivateMessage(tempUin, tempGroupId, replies) + } + + if rspId == -1 { + logger.Error("【ReplyFail】", p(m), rspId) + return + } + logger.Info("【OK】", p(m), 0, replies, rspId) +} diff --git a/qq_robot/util.go b/qq_robot/util.go index 0808f8ab9..bd3117ea2 100644 --- a/qq_robot/util.go +++ b/qq_robot/util.go @@ -3,9 +3,13 @@ package qq_robot import ( "crypto/md5" "encoding/hex" + "encoding/json" "path" "runtime" + "strings" + "time" + "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" @@ -43,3 +47,78 @@ func (r *QQRobot) _makeLocalImage(imageUrl string) (message.IMessageElement, err hasCacheFile: return &coolq.LocalImageElement{File: cacheFile}, nil } + +func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) string { + ocrResult, err := r.cqBot.Client.ImageOcr(groupImageElement) + if err != nil { + logger.Errorf("ocr出错了,image=%+v,err=%v", groupImageElement, err) + return "" + } + + resultBuffer := strings.Builder{} + for _, textDetection := range ocrResult.Texts { + resultBuffer.WriteString(textDetection.Text) + } + return resultBuffer.String() +} + +func (r *QQRobot) currentTime() string { + return r.formatTime(time.Now()) +} + +func (r *QQRobot) formatTime(t time.Time) string { + return t.Format("2006-01-02 15:04:05.999") +} + +func getCurrentPeriodName() string { + hour := time.Now().Hour() + if hour == 23 || 0 <= hour && hour < 5 { + return "深夜" + } else if 5 <= hour && hour < 11 { + return "早上" + } else if 11 <= hour && hour < 13 { + return "中午" + } else if 13 <= hour && hour < 17 { + return "下午" + } else { + return "晚上" + } +} + +// 是否是管理员 +func isMemberAdmin(permission client.MemberPermission) bool { + return permission == client.Owner || permission == client.Administrator +} + +// 单条消息发送的大小有限制,所以需要分成多段来发 +const maxMessageJsonSize = 400 + +func splitPlainMessage(content string) []message.IMessageElement { + if len(content) <= maxMessageJsonSize { + return []message.IMessageElement{message.NewText(content)} + } + + var splittedMessage []message.IMessageElement + + var part string + remainingText := content + for len(remainingText) != 0 { + partSize := 0 + for byteIdx, runeValue := range remainingText { + if partSize+byteIdx > maxMessageJsonSize { + break + } + partSize += len(string(runeValue)) + } + + part, remainingText = remainingText[:partSize], remainingText[partSize:] + splittedMessage = append(splittedMessage, message.NewText(part)) + } + + return splittedMessage +} + +func p(v interface{}) string { + bytes, _ := json.Marshal(v) + return string(bytes) +} From 47c63ba14d75c6105f8b403033dd1ebd876ca582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 09:04:58 +0800 Subject: [PATCH 011/612] =?UTF-8?q?=E8=B0=83=E6=95=B4readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 29af7e495..c43fc1d84 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,17 @@ # qq机器人 + 基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 实现的qq机器人,在其上额外增加了一个直接本地处理消息并根据配置自动回复的逻辑, 具体改动可见 *qq_robot* 目录 # 使用说明 + ## 配置机器人 + 请参考 [go-cqhttp的文档](https://docs.go-cqhttp.org/) ## 配置自动回复逻辑 -复制qq_robot/default_config.toml到qq_robot.exe所在目录,并重命名为config.toml -然后打开qq_robot/config.go和config.toml,按照注释,自行调整配置,并添加各种规则 \ No newline at end of file + +复制qq_robot/default_config.toml到qq_robot.exe所在目录,并重命名为config.toml 然后打开qq_robot/config.go和config.toml,按照注释,自行调整配置,并添加各种规则 + +# TODO + +[ ] 看看是否有更多事件值得接入,目前仅处理了群聊、私聊和加群这三个事件 \ No newline at end of file From 841929fc25d99f1d3180abeb0324ae7466f8c10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 09:15:09 +0800 Subject: [PATCH 012/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BC=A0=E5=85=A5?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 2 +- qq_robot/config.go | 4 ++-- qq_robot/qq_robot.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index f503d25c1..8816022d5 100644 --- a/main.go +++ b/main.go @@ -426,7 +426,7 @@ func main() { log.Info("アトリは、高性能ですから!") log.Info("启动本地的机器人处理程序,用于直接收发消息和自动回复") - robot := qq_robot.NewQQRobot(bot) + robot := qq_robot.NewQQRobot(bot, "config.toml") robot.RegisterHandlers() robot.Start() defer robot.Stop() diff --git a/qq_robot/config.go b/qq_robot/config.go index a295ac9d3..cd070fd61 100644 --- a/qq_robot/config.go +++ b/qq_robot/config.go @@ -176,10 +176,10 @@ type Config struct { NotifyUpdate NotifyUpdateConfig `toml:"notify_update"` } -func LoadConfig() Config { +func LoadConfig(configPath string) Config { // 读取配置 var config Config - _, err := toml.DecodeFile("config.toml", &config) + _, err := toml.DecodeFile(configPath, &config) if err != nil { logger.Fatalf("load toml file fail, err=%v", err) } diff --git a/qq_robot/qq_robot.go b/qq_robot/qq_robot.go index 2231ac963..77e5b38a5 100644 --- a/qq_robot/qq_robot.go +++ b/qq_robot/qq_robot.go @@ -51,8 +51,8 @@ type QQRobot struct { quitFunc context.CancelFunc } -func NewQQRobot(cqRobot *coolq.CQBot) *QQRobot { - config := LoadConfig() +func NewQQRobot(cqRobot *coolq.CQBot, configPath string) *QQRobot { + config := LoadConfig(configPath) r := &QQRobot{ cqBot: cqRobot, From ad5035cca3dd56045d667c2e1883fa5ec189a15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 09:15:21 +0800 Subject: [PATCH 013/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/qq_robot_test.go | 113 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 qq_robot/qq_robot_test.go diff --git a/qq_robot/qq_robot_test.go b/qq_robot/qq_robot_test.go new file mode 100644 index 000000000..48319a0ee --- /dev/null +++ b/qq_robot/qq_robot_test.go @@ -0,0 +1,113 @@ +package qq_robot + +import ( + "fmt" + "net/url" + "reflect" + "testing" + + mylogger "github.com/fzls/logger" +) + +// 2020/10/30 17:39 by fzls + +func Test_convertChineseNumber(t *testing.T) { + tests := []struct { + name string + args string + want string + }{ + // 正确的例子 + {name: "", args: "一", want: "1"}, + {name: "", args: "十", want: "10"}, + {name: "", args: "十一", want: "11"}, + {name: "", args: "二十", want: "20"}, + {name: "", args: "二十一", want: "21"}, + {name: "", args: "二百", want: "200"}, + {name: "", args: "三百零四", want: "304"}, + {name: "", args: "五百六十七", want: "567"}, + {name: "", args: "八千九百一十二", want: "8912"}, + {name: "", args: "八千一十二", want: "8012"}, + {name: "", args: "八千零三", want: "8003"}, + {name: "", args: "八千零二十五", want: "8025"}, + {name: "", args: "给我来个八千零二十五秒禁言套餐", want: "给我来个8025秒禁言套餐"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := convertChineseNumber(tt.args); got != tt.want { + t.Errorf("convertChineseNumber(%v) = %v, want %v", tt.args, got, tt.want) + } + }) + } +} + +func Test_getLatestGitVersion(t *testing.T) { + // 初始化日志 + var err error + logger, err = mylogger.NewLogger("logs", "qq_robot", mylogger.DebugLevel) + if err != nil { + fmt.Printf("new logger err=%v\n", err) + return + } + + qqRobot := NewQQRobot(nil, "../config.toml") + version, updateMessage := qqRobot.getLatestGitVersion("https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD") + t.Logf("version=%v, updateMessage如下:\n%v", version, updateMessage) +} + +func Test_version_to_version_int_list(t *testing.T) { + tests := []struct { + version string + want []int64 + }{ + {version: "1.2.3", want: []int64{1, 2, 3}}, + {version: "v1.2.3", want: []int64{1, 2, 3}}, + {version: "v1.12.3", want: []int64{1, 12, 3}}, + {version: "v1.0.3", want: []int64{1, 0, 3}}, + } + for _, tt := range tests { + t.Run(tt.version, func(t *testing.T) { + if got := version_to_version_int_list(tt.version); !reflect.DeepEqual(got, tt.want) { + t.Errorf("version_to_version_int_list(%v) = %v, want %v", tt.version, got, tt.want) + } + }) + } +} + +func Test_version_less(t *testing.T) { + tests := []struct { + version_left string + version_right string + want bool + }{ + {version_left: "v1.0.0", version_right: "v1.0.0", want: false}, + {version_left: "v1.0.0", version_right: "v1.0.1", want: true}, + {version_left: "v1.0.0", version_right: "v1.1.0", want: true}, + {version_left: "v1.0.0", version_right: "v2.0.0", want: true}, + {version_left: "v2.0.0", version_right: "v1.0.0", want: false}, + {version_left: "v2.0.0", version_right: "v10.0.0", want: true}, + {version_left: "v3.2.9.1", version_right: "v4.0.0", want: true}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%v-%v", tt.version_left, tt.version_right), func(t *testing.T) { + if got := version_less(tt.version_left, tt.version_right); got != tt.want { + t.Errorf("version_less(%v, %v) = %v, want %v", tt.version_left, tt.version_right, got, tt.want) + } + }) + } +} + +func TestMakeSign(t *testing.T) { + params := url.Values{} + params.Set("app_id", "2159831370") + params.Set("time_stamp", "1605690895") + params.Set("nonce_str", "0.21999282737760117") + params.Set("session", "123456789") + params.Set("question", "月球是什么?") + appKey := "cHQ286kZhNKlpwbj" + + want := "562D5D6303E61FB9EE590CB20AC29218" + if got := MakeSign(params, appKey); got != want { + t.Errorf("MakeSign() = %v, want %v", got, want) + } +} From 99a93523363c238edc01041521e4cab98c2656cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 09:17:14 +0800 Subject: [PATCH 014/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E8=87=AA=E7=94=A8=E7=9A=84=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/_build.bat | 1 + qq_robot/_start.bat | 7 +++++++ ...\250\346\234\272\345\231\250\344\272\272.lnk" | Bin 0 -> 1253 bytes 3 files changed, 8 insertions(+) create mode 100644 qq_robot/_build.bat create mode 100644 qq_robot/_start.bat create mode 100644 "qq_robot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" diff --git a/qq_robot/_build.bat b/qq_robot/_build.bat new file mode 100644 index 000000000..b69379e1e --- /dev/null +++ b/qq_robot/_build.bat @@ -0,0 +1 @@ +go build -o qq_robot.exe . diff --git a/qq_robot/_start.bat b/qq_robot/_start.bat new file mode 100644 index 000000000..7c4d81e8a --- /dev/null +++ b/qq_robot/_start.bat @@ -0,0 +1,7 @@ +@ECHO OFF + +:: 修改console默认encoding为utf8,避免中文乱码 +CHCP 65001 + +echo 启动机器人 +start "qq_robot" C:\Windows\System32\cmd.exe /k "D:\_codes\Python\dnf_calc\robot\qq_robot.exe" diff --git "a/qq_robot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" "b/qq_robot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" new file mode 100644 index 0000000000000000000000000000000000000000..44b44b0fa963a1b2b1ac5d1de79d4ed7198a943e GIT binary patch literal 1253 zcmbtUT}YE*6n?g5Xn)Q!GqW5kK^M-cb&()QiW-GmjI?6m-#&5DrnQCJP1sckdeKFo zP&Ul+A~hsJl#F!|btVu?O?SnOJm@9PY``*Okpv7`? zH*!k~9Z{#4QiVMAVceG8qer#N<7{d3q8JI#VezrIa`3>oEvLs!pWDtYl=|3JRXg@< z8P#yX11DP01~08CI2&a(^;uvKczueyARjoohS$_5yg6ful$%wym~bvk!Hxj=d{E#< z0eBs$sFIzf*IR#FGN8fjG%J23ny**|!kgbKxkj4wGfJ!tXQF|8PMFE-CnR6MMx+zc zPT30xSyo~fv4Q9b1^vSLeE!kO(-&YvfA`C=TA^b#A!MKwg>ayT(OMWyVI>Ym39z0f zT0DGmQZ}j{bvREy2QKoYp71fdTv`k;u9L^1cCe1j!dZ_x?nd=3#sR@hRx?FN#e4(7 zw#p}Iv)Xf&qowAN;gj8?a#l;Q`rv8qvKpEcPqQnci)@|QzkTfTm*SDsvQO~aZ%mqo zUoGF7OS=C)eb3g9af$yrpJB@r$bW%cd3Kw9-ZJs71RdoA-@`lly^Y3Gv^ynG24N&( zO)?F+FQYdd>C4@y%uiGu_bMP_hq2Z5cm%iMDGxVqbG z(hp5)#3&z$dKwaiD~C|0Q0TT1Yd;qQ0` Date: Sat, 2 Oct 2021 09:28:47 +0800 Subject: [PATCH 015/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/_start.bat | 2 +- ...0\346\234\272\345\231\250\344\272\272.lnk" | Bin 1253 -> 1245 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/qq_robot/_start.bat b/qq_robot/_start.bat index 7c4d81e8a..c10f286a4 100644 --- a/qq_robot/_start.bat +++ b/qq_robot/_start.bat @@ -4,4 +4,4 @@ CHCP 65001 echo 启动机器人 -start "qq_robot" C:\Windows\System32\cmd.exe /k "D:\_codes\Python\dnf_calc\robot\qq_robot.exe" +start "qq_robot" C:\Windows\System32\cmd.exe /k "D:\_codes\go\qq_robot_go\qq_robot.exe" diff --git "a/qq_robot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" "b/qq_robot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" index 44b44b0fa963a1b2b1ac5d1de79d4ed7198a943e..8a0c12240c30c17f5834297d20ca44d49b4cb345 100644 GIT binary patch delta 143 zcmaFLd6#p;WyX351{Ve^h8Tu;hGd3(h7^WWhGHNu9mtMhC>Di ZgFaBL0)rCCIx$U*VPc5i9K+Pf1OTy99rFMH delta 32 mcmcc1`IK|RWkyE%$<8eHoO%rU49N@%3`#&=@aA-;GA00<`UqYC From 4d6b742f5c223f5aa13f735c6fb60803b97a62a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 09:51:16 +0800 Subject: [PATCH 016/612] =?UTF-8?q?=E5=85=88=E5=B0=9D=E8=AF=95=E5=9B=BD?= =?UTF-8?q?=E5=86=85=E9=95=9C=E5=83=8F=EF=BC=8C=E6=9C=80=E5=90=8E=E5=B0=9D?= =?UTF-8?q?=E8=AF=95=E7=9B=B4=E6=8E=A5=E8=AE=BF=E9=97=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/check_update.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qq_robot/check_update.go b/qq_robot/check_update.go index eeab76d79..75da80371 100644 --- a/qq_robot/check_update.go +++ b/qq_robot/check_update.go @@ -113,10 +113,11 @@ var GITHUB_MIRROR_SITES = []string{ func (r *QQRobot) getLatestGitVersion(gitChangelogPage string) (latestVersion string, updateMessage string) { var urls []string - urls = append(urls, gitChangelogPage) + // 先尝试国内镜像,最后尝试直接访问 for _, mirrorSite := range GITHUB_MIRROR_SITES { urls = append(urls, strings.ReplaceAll(gitChangelogPage, "github.com", mirrorSite)) } + urls = append(urls, gitChangelogPage) for _, url := range urls { latestVersion, updateMessage = r._getLatestGitVersion(url) From e7468b2333caba72b36afc87667fd1421685e396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 09:53:51 +0800 Subject: [PATCH 017/612] =?UTF-8?q?=E6=B7=98=E5=AE=9D=E7=9A=84=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E5=BE=80=E5=90=8E=E6=94=BE=E7=82=B9=EF=BC=8C=E5=AE=B9?= =?UTF-8?q?=E6=98=93=E6=8F=90=E7=A4=BA=E8=B6=85=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/check_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qq_robot/check_update.go b/qq_robot/check_update.go index 75da80371..b01171ab1 100644 --- a/qq_robot/check_update.go +++ b/qq_robot/check_update.go @@ -107,8 +107,8 @@ var VersionNone = "v0.0.0" // github的镜像站 var GITHUB_MIRROR_SITES = []string{ - "github.com.cnpmjs.org", "hub.fastgit.org", + "github.com.cnpmjs.org", } func (r *QQRobot) getLatestGitVersion(gitChangelogPage string) (latestVersion string, updateMessage string) { From f0f526b298a85b73c33b43772d1a048aa1892912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 10:24:30 +0800 Subject: [PATCH 018/612] =?UTF-8?q?ocr=E5=8A=A0=E5=9B=9Elru=E7=BC=93?= =?UTF-8?q?=E5=AD=98=EF=BC=8C=E7=A1=AE=E4=BF=9D=E4=B8=8D=E4=BC=9A=E6=AF=8F?= =?UTF-8?q?=E4=B8=AArule=E5=88=A4=E5=AE=9A=E6=97=B6=E9=83=BD=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 1 + go.sum | 2 ++ qq_robot/qq_robot.go | 4 ++++ qq_robot/util.go | 14 +++++++++++++- 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 334d3ac18..7c7a79aec 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/gookit/color v1.4.2 github.com/gorilla/websocket v1.4.2 github.com/guonaihong/gout v0.2.4 + github.com/hashicorp/golang-lru v0.5.4 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 8e8db0769..40644a31d 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/guonaihong/gout v0.2.4 h1:BlWpWWay/Q1LkyIwupEWBZE3PMl4xzzAgMHw+OrZxBs= github.com/guonaihong/gout v0.2.4/go.mod h1:ISabiAAj0z1h3bOFUKzfRqPMvX0wmcYzIh6i4xIxMPo= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= diff --git a/qq_robot/qq_robot.go b/qq_robot/qq_robot.go index 77e5b38a5..e501904d6 100644 --- a/qq_robot/qq_robot.go +++ b/qq_robot/qq_robot.go @@ -16,6 +16,7 @@ import ( "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/go-cqhttp/coolq" "github.com/gookit/color" + lru "github.com/hashicorp/golang-lru" ) type MessageKey struct { @@ -45,6 +46,8 @@ type QQRobot struct { HttpClient http.Client + ocrCache *lru.ARCCache + CheckUpdateVersionMap map[string]string // 配置的检查更新名称=>最近的版本号,如"DNF蚊子腿小助手更新"=>"v4.2.2" quitCtx context.Context @@ -64,6 +67,7 @@ func NewQQRobot(cqRobot *coolq.CQBot, configPath string) *QQRobot { HttpClient: http.Client{Timeout: time.Duration(config.Robot.Timeout) * time.Second}, CheckUpdateVersionMap: map[string]string{}, } + r.ocrCache, _ = lru.NewARC(1024) for _, config := range config.Rules { r.Rules = append(r.Rules, NewRule(config)) } diff --git a/qq_robot/util.go b/qq_robot/util.go index bd3117ea2..acb94acbe 100644 --- a/qq_robot/util.go +++ b/qq_robot/util.go @@ -4,6 +4,7 @@ import ( "crypto/md5" "encoding/hex" "encoding/json" + "fmt" "path" "runtime" "strings" @@ -48,7 +49,18 @@ hasCacheFile: return &coolq.LocalImageElement{File: cacheFile}, nil } -func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) string { +func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) (ocrResultString string) { + image_md5 := fmt.Sprintf("%x", groupImageElement.Md5) + + cached, ok := r.ocrCache.Get(image_md5) + if ok { + return cached.(string) + } + + defer func() { + r.ocrCache.Add(image_md5, ocrResultString) + }() + ocrResult, err := r.cqBot.Client.ImageOcr(groupImageElement) if err != nil { logger.Errorf("ocr出错了,image=%+v,err=%v", groupImageElement, err) From 169f69f3eadf121c915a966c8372753dd92695ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 22:47:16 +0800 Subject: [PATCH 019/612] =?UTF-8?q?=E4=B9=8B=E5=89=8D=E7=9A=84=E8=85=BE?= =?UTF-8?q?=E8=AE=AF=E9=97=B2=E8=81=8A=E6=8E=A5=E5=8F=A3=E4=BC=BC=E4=B9=8E?= =?UTF-8?q?=E4=B8=8B=E7=BA=BF=E4=BA=86=EF=BC=8C=E6=8E=A5=E5=85=A5=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E9=97=B2=E8=81=8A=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 + go.sum | 4 ++ qq_robot/ai.go | 112 +++++++++++++------------------------------ qq_robot/config.go | 5 +- qq_robot/qq_robot.go | 5 ++ 5 files changed, 47 insertions(+), 81 deletions(-) diff --git a/go.mod b/go.mod index 7c7a79aec..f3603d248 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,8 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264 github.com/tidwall/gjson v1.8.1 github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 diff --git a/go.sum b/go.sum index 40644a31d..d6be9bcdc 100644 --- a/go.sum +++ b/go.sum @@ -133,6 +133,10 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264 h1:USf7I8ohzoqCwAcCHz5rz0hD57mmNXZgrLfxFLx1WmA= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264 h1:kGT41c6hLBFfiTjacWDpq62ePSnx1/9+MXrpMZII2Uk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= diff --git a/qq_robot/ai.go b/qq_robot/ai.go index 7b656252e..bd0232add 100644 --- a/qq_robot/ai.go +++ b/qq_robot/ai.go @@ -1,102 +1,56 @@ package qq_robot import ( - "crypto/md5" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "math/rand" - "net/url" - "sort" "strconv" "strings" - "time" -) - -// 2021/10/02 5:20 by fzls -type AiChatResponse struct { - RetCode int `json:"ret"` - Message string `json:"msg"` - Data AiChatResponseData `json:"data"` -} -type AiChatResponseData struct { - Session string `json:"session"` - Answer string `json:"answer"` -} + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" + tbp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp/v20190627" +) -// 使用腾讯ai开放平台的智能闲聊接口 https://ai.qq.com/doc/nlpchat.shtml -func (r *QQRobot) aiChat(targetQQ int64, chatText string) (responseText string) { +func (r *QQRobot) initAiChat() { cfg := r.Config.Robot - params := url.Values{} - params.Set("app_id", cfg.TencentAiAppId) - params.Set("time_stamp", strconv.FormatInt(time.Now().Unix(), 10)) - params.Set("nonce_str", strconv.FormatInt(rand.Int63(), 10)) - params.Set("session", strconv.FormatInt(targetQQ, 10)) - params.Set("question", chatText) - - params.Set("sign", MakeSign(params, cfg.TencentAiAppKey)) - - resp, err := r.HttpClient.PostForm(TencentAiApi, params) - if err != nil { - logger.Debugf("aiChat(qq=%v, text=%v) params=%v post err=%v", targetQQ, chatText, params.Encode(), err) + if cfg.TencentAiAppId == "" || cfg.TencentAiAppKey == "" || cfg.TencentAiBotId == "" { + logger.Warnf("未配置腾讯ai的appid、appkey、botid,将不初始化aichat,详情可见 https://console.cloud.tencent.com/tbp/bots") return } - defer resp.Body.Close() - if resp.StatusCode != 200 { - logger.Debugf("aiChat(qq=%v, text=%v) params=%v status code=%v", targetQQ, chatText, params.Encode(), resp.StatusCode) - return - } + credential := common.NewCredential(cfg.TencentAiAppId, cfg.TencentAiAppKey) + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = TencentAiChatApi + r.aiClient, _ = tbp.NewClient(credential, "", cpf) +} - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - logger.Debugf("aiChat(qq=%v, text=%v) params=%v read body err=%v", targetQQ, chatText, params.Encode(), err) - return +// 使用腾讯ai开放平台的 智能对话平台 TBP https://console.cloud.tencent.com/tbp/bots +func (r *QQRobot) aiChat(targetQQ int64, chatText string) (responseText string) { + if r.aiClient == nil { + return "" } - var result AiChatResponse - err = json.Unmarshal(body, &result) - if err != nil { - logger.Debugf("aiChat(qq=%v, text=%v) params=%v unmarshal err=%v body=%v", targetQQ, chatText, params.Encode(), err, body) - return - } - if result.RetCode != 0 { - logger.Debugf("aiChat(qq=%v, text=%v) params=%v retcode!=0, result=%v", targetQQ, chatText, params.Encode(), result) - return result.Message - } + cfg := r.Config.Robot - return result.Data.Answer -} + request := tbp.NewTextProcessRequest() -func MakeSign(params url.Values, appKey string) string { - // 将请求参数对按key进行字典升序排序,得到有序的参数对列表N - keys := make([]string, 0, len(params)) - for k := range params { - keys = append(keys, k) + request.BotId = common.StringPtr(cfg.TencentAiBotId) + request.BotEnv = common.StringPtr("release") + request.TerminalId = common.StringPtr(strconv.FormatInt(targetQQ, 10)) + request.InputText = common.StringPtr(chatText) + + response, err := r.aiClient.TextProcess(request) + if err != nil { + logger.Debugf("aiChat(qq=%v, text=%v) err=%v", targetQQ, chatText, err) + return } - sort.Strings(keys) - // 将列表N中的参数对按URL键值对的格式拼接成字符串,得到字符串T(如:key1=value1&key2=value2),URL键值拼接过程value部分需要URL编码,URL编码算法用大写字母,例如%E8,而不是小写%e8 - var kvs []string - for _, k := range keys { - vs := params[k] - for _, v := range vs { - kvs = append(kvs, fmt.Sprintf("%v=%v", k, url.QueryEscape(v))) + answer := strings.Builder{} + for _, resGroup := range response.Response.ResponseMessage.GroupList { + if resGroup.Content == nil { + continue } + answer.WriteString(*resGroup.Content) } - // 将应用密钥以app_key为键名,组成URL键值拼接到字符串T末尾,得到字符串S(如:key1=value1&key2=value2&app_key=密钥) - kvs = append(kvs, fmt.Sprintf("%v=%v", "app_key", appKey)) - - strToSign := strings.Join(kvs, "&") - - // 对拼接的数据库进行md5摘要,即可得sign签名 - w := md5.New() - io.WriteString(w, strToSign) - sign := fmt.Sprintf("%X", w.Sum(nil)) - - return sign + return answer.String() } diff --git a/qq_robot/config.go b/qq_robot/config.go index cd070fd61..b6ab4d4f8 100644 --- a/qq_robot/config.go +++ b/qq_robot/config.go @@ -8,7 +8,7 @@ import ( "github.com/BurntSushi/toml" ) -const TencentAiApi = "https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat" +const TencentAiChatApi = "tbp.tencentcloudapi.com" const ( TemplateArgs_WorkTime = "$work_time$" // 本次工作时长 @@ -34,8 +34,9 @@ type RobotConfig struct { OnStop NotifyConfig `toml:"on_stop"` // 机器人下线时的操作,参数:$work_time$=本次工作时长 MaxRetryTimes int `toml:"max_retry_times"` // 单条消息处理失败后,最多重试次数 MaxContinueEmptyLines int `toml:"max_continue_empty_lines"` // 最大允许的连续空行数目,为0则不限制 - TencentAiAppId string `toml:"tencent_ai_app_id"` // 腾讯ai开放平台的应用ID + TencentAiAppId string `toml:"tencent_ai_app_id"` // 腾讯ai开放平台的应用ID,具体可见 https://console.cloud.tencent.com/tbp/bots TencentAiAppKey string `toml:"tencent_ai_app_key"` // 腾讯ai开放平台的应用秘钥 + TencentAiBotId string `toml:"tencent_ai_bot_id"` // 腾讯ai开放平台的机器人BotId ChatAnswerNotFoundMessage string `toml:"chat_answer_not_found_message"` // 聊天结果未找到时的提示语 PersonalMessageNotSupportedMessage string `toml:"personal_message_not_supported_message"` // 不支持私聊时的提示语 # 本QQ是机器人,基本不会登录该QQ人工查看消息,如果有事,请私聊大号~ PersonalMessageNotSupportedImage string `toml:"personal_message_not_supported_image"` // 不支持私聊时的图片 diff --git a/qq_robot/qq_robot.go b/qq_robot/qq_robot.go index e501904d6..793c17e6f 100644 --- a/qq_robot/qq_robot.go +++ b/qq_robot/qq_robot.go @@ -17,6 +17,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/gookit/color" lru "github.com/hashicorp/golang-lru" + tbp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp/v20190627" ) type MessageKey struct { @@ -45,6 +46,7 @@ type QQRobot struct { GroupToRuleNameToLastSuccessTriggerTimestamp map[int64]map[string]int64 // group => rulename => 上次在cd外成功触发的时间戳 HttpClient http.Client + aiClient *tbp.Client ocrCache *lru.ARCCache @@ -67,6 +69,9 @@ func NewQQRobot(cqRobot *coolq.CQBot, configPath string) *QQRobot { HttpClient: http.Client{Timeout: time.Duration(config.Robot.Timeout) * time.Second}, CheckUpdateVersionMap: map[string]string{}, } + + r.initAiChat() + r.ocrCache, _ = lru.NewARC(1024) for _, config := range config.Rules { r.Rules = append(r.Rules, NewRule(config)) From f9460a3ea11f5d6e7b9e98f6848d101356826a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 2 Oct 2021 22:48:36 +0800 Subject: [PATCH 020/612] =?UTF-8?q?=E7=BC=96=E8=AF=91=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=98=BE=E7=A4=BA=E8=BF=9B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/_build.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qq_robot/_build.bat b/qq_robot/_build.bat index b69379e1e..041503b33 100644 --- a/qq_robot/_build.bat +++ b/qq_robot/_build.bat @@ -1 +1 @@ -go build -o qq_robot.exe . +go build -v -o qq_robot.exe . From 90287dfa41c9dcc27524243d09bbccfce4031a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 01:07:17 +0800 Subject: [PATCH 021/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E6=96=87=E4=BB=B6=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/{ai.go => ai_chat.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename qq_robot/{ai.go => ai_chat.go} (100%) diff --git a/qq_robot/ai.go b/qq_robot/ai_chat.go similarity index 100% rename from qq_robot/ai.go rename to qq_robot/ai_chat.go From e9ac9243835b195f72f9816e3f7d3f1ded86e511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 01:48:53 +0800 Subject: [PATCH 022/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=A6=81=E5=BF=BD=E7=95=A5=E7=9A=84=E6=9C=BA=E5=99=A8=E4=BA=BA?= =?UTF-8?q?qq=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/config.go | 1 + qq_robot/qq_robot.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/qq_robot/config.go b/qq_robot/config.go index b6ab4d4f8..e3c4c25ba 100644 --- a/qq_robot/config.go +++ b/qq_robot/config.go @@ -28,6 +28,7 @@ type NotifyConfig struct { } type RobotConfig struct { + IgnoreRobotQQs []int64 `toml:"ignore_robot_qqs"` // 机器人的qq号列表,这些号的消息将被忽略,避免多个机器人同时在群里不停循环回复 Timeout int64 `toml:"timeout"` // http请求超时 Debug bool `toml:"debug"` // 是否是调试模式 OnStart NotifyConfig `toml:"on_start"` // 机器人上线时的操作 diff --git a/qq_robot/qq_robot.go b/qq_robot/qq_robot.go index 793c17e6f..060f5c200 100644 --- a/qq_robot/qq_robot.go +++ b/qq_robot/qq_robot.go @@ -344,6 +344,12 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { logger.Info("【ExcludedAdmin】", nowStr, config.Name, p(m)) return nil } + for _, robotQQ := range r.Config.Robot.IgnoreRobotQQs { + if robotQQ == senderUin { + logger.Info("【ExcludedRobotQQ】", nowStr, config.Name, p(m)) + return nil + } + } messageApplyCount := r.RuleTypeToMessageIdToRuleApplyCount[config.Type] if messageApplyCount == nil { From 955a96efc73fe07b9344b24323cb3cc657f3fe35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 01:49:09 +0800 Subject: [PATCH 023/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=82=B9=E6=AD=8C?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/command.go | 10 +++++++ qq_robot/config.go | 1 + qq_robot/music.go | 69 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 qq_robot/music.go diff --git a/qq_robot/command.go b/qq_robot/command.go index ed53f2607..b2ad95ca5 100644 --- a/qq_robot/command.go +++ b/qq_robot/command.go @@ -86,6 +86,16 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (er } json.Unmarshal(out, &msg) + } else if match = CommandRegex_Music.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_Music.SubexpNames()) { + // full_match|musicName + musicName := match[1] + musicElem, err := r.makeMusicShareElement(musicName) + if err != nil { + return fmt.Errorf("没有找到歌曲:%v", musicName), "", nil + } + + msg = fmt.Sprintf("请欣赏歌曲:%v", musicName) + extraReplies = append(extraReplies, musicElem) } else { return fmt.Errorf("没有找到该指令哦"), "", nil } diff --git a/qq_robot/config.go b/qq_robot/config.go index e3c4c25ba..251674c0c 100644 --- a/qq_robot/config.go +++ b/qq_robot/config.go @@ -84,6 +84,7 @@ var ( CommandRegex_RuleNameList = regexp.MustCompile(`RuleNameList`) CommandRegex_BuyCard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) CommandRegex_QueryCard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) + CommandRegex_Music = regexp.MustCompile(`.*点歌\s+(?P.+)`) ) type RuleConfig struct { diff --git a/qq_robot/music.go b/qq_robot/music.go new file mode 100644 index 000000000..5951ee534 --- /dev/null +++ b/qq_robot/music.go @@ -0,0 +1,69 @@ +package qq_robot + +import ( + "errors" + "io/ioutil" + "net/http" + "net/url" + "strconv" + + "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/go-cqhttp/global" + "github.com/tidwall/gjson" +) + +func (r *QQRobot) tryAppendMusicElement(m *message.SendingMessage, musicName string) { + musicElem, err := r.makeMusicShareElement(musicName) + if err != nil { + return + } + + m.Append(musicElem) +} + +func (r *QQRobot) makeMusicShareElement(musicName string) (*message.MusicShareElement, error) { + songId := strconv.FormatInt(r.queryNeteaseMusic(musicName), 10) + + info, err := global.NeteaseMusicSongInfo(songId) + if err != nil { + return nil, err + } + if !info.Exists() { + return nil, errors.New("song not found") + } + name := info.Get("name").Str + jumpURL := "https://y.music.163.com/m/song/" + songId + musicURL := "http://music.163.com/song/media/outer/url?id=" + songId + picURL := info.Get("album.picUrl").Str + artistName := "" + if info.Get("artists.0").Exists() { + artistName = info.Get("artists.0.name").Str + } + return &message.MusicShareElement{ + MusicType: message.CloudMusic, + Title: name, + Summary: artistName, + Url: jumpURL, + PictureUrl: picURL, + MusicUrl: musicURL, + }, nil +} + +// based on https://github.com/wdvxdr1123/ZeroBot/blob/main/example/music/data.go +func (r *QQRobot) queryNeteaseMusic(musicName string) int64 { + req, err := http.NewRequest("GET", "http://music.163.com/api/search/get?type=1&s="+url.QueryEscape(musicName), nil) + if err != nil { + return 0 + } + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66") + res, err := r.HttpClient.Do(req) + if err != nil { + return 0 + } + data, err := ioutil.ReadAll(res.Body) + _ = res.Body.Close() + if err != nil { + return 0 + } + return gjson.ParseBytes(data).Get("result.songs.0.id").Int() +} From 905727ca55afa3b178573429668e6dd6099feea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 02:25:10 +0800 Subject: [PATCH 024/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8E=A5=E5=85=A5qq?= =?UTF-8?q?=E9=9F=B3=E4=B9=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/command.go | 3 +- qq_robot/music.go | 91 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/qq_robot/command.go b/qq_robot/command.go index b2ad95ca5..a56f489f1 100644 --- a/qq_robot/command.go +++ b/qq_robot/command.go @@ -89,7 +89,8 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (er } else if match = CommandRegex_Music.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_Music.SubexpNames()) { // full_match|musicName musicName := match[1] - musicElem, err := r.makeMusicShareElement(musicName) + + musicElem, err := r.makeMusicShareElement(musicName, message.QQMusic) if err != nil { return fmt.Errorf("没有找到歌曲:%v", musicName), "", nil } diff --git a/qq_robot/music.go b/qq_robot/music.go index 5951ee534..2005f5502 100644 --- a/qq_robot/music.go +++ b/qq_robot/music.go @@ -2,18 +2,18 @@ package qq_robot import ( "errors" + "fmt" "io/ioutil" "net/http" "net/url" - "strconv" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/go-cqhttp/global" "github.com/tidwall/gjson" ) -func (r *QQRobot) tryAppendMusicElement(m *message.SendingMessage, musicName string) { - musicElem, err := r.makeMusicShareElement(musicName) +func (r *QQRobot) tryAppendMusicElement(m *message.SendingMessage, musicName string, musicType int) { + musicElem, err := r.makeMusicShareElement(musicName, musicType) if err != nil { return } @@ -21,8 +21,20 @@ func (r *QQRobot) tryAppendMusicElement(m *message.SendingMessage, musicName str m.Append(musicElem) } -func (r *QQRobot) makeMusicShareElement(musicName string) (*message.MusicShareElement, error) { - songId := strconv.FormatInt(r.queryNeteaseMusic(musicName), 10) +func (r *QQRobot) makeMusicShareElement(musicName string, musicType int) (*message.MusicShareElement, error) { + switch musicType { + case message.CloudMusic: + return r.makeCloudMusicShareElement(musicName) + case message.QQMusic: + return r.makeQQMusicShareElement(musicName) + default: + return nil, fmt.Errorf("未知音乐类型=%v", musicType) + } +} + +// 基于 https://github.com/wdvxdr1123/ZeroBot/blob/main/example/music/data.go +func (r *QQRobot) makeCloudMusicShareElement(musicName string) (*message.MusicShareElement, error) { + songId := r.queryNeteaseMusic(musicName) info, err := global.NeteaseMusicSongInfo(songId) if err != nil { @@ -49,21 +61,78 @@ func (r *QQRobot) makeMusicShareElement(musicName string) (*message.MusicShareEl }, nil } -// based on https://github.com/wdvxdr1123/ZeroBot/blob/main/example/music/data.go -func (r *QQRobot) queryNeteaseMusic(musicName string) int64 { +func (r *QQRobot) queryNeteaseMusic(musicName string) string { req, err := http.NewRequest("GET", "http://music.163.com/api/search/get?type=1&s="+url.QueryEscape(musicName), nil) if err != nil { - return 0 + return "0" } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66") res, err := r.HttpClient.Do(req) if err != nil { - return 0 + return "0" } data, err := ioutil.ReadAll(res.Body) _ = res.Body.Close() if err != nil { - return 0 + return "0" + } + return gjson.ParseBytes(data).Get("result.songs.0.id").String() +} + +// 基于 https://github.com/FloatTech/ZeroBot-Plugin/blob/master/plugin_music/selecter.go +func (r *QQRobot) makeQQMusicShareElement(musicName string) (*message.MusicShareElement, error) { + songId := r.queryQQMusic(musicName) + + info, err := global.QQMusicSongInfo(songId) + if err != nil { + return nil, err + } + if !info.Get("track_info").Exists() { + return nil, errors.New("song not found") + } + name := info.Get("track_info.name").Str + mid := info.Get("track_info.mid").Str + albumMid := info.Get("track_info.album.mid").Str + pinfo, _ := global.GetBytes("http://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576") + jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + mid + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare" + purl := gjson.ParseBytes(pinfo).Get("url_mid.data.midurlinfo.0.purl").Str + preview := "http://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg" + content := info.Get("track_info.singer.0.name").Str + return &message.MusicShareElement{ + MusicType: message.QQMusic, + Title: name, + Summary: content, + Url: jumpURL, + PictureUrl: preview, + MusicUrl: purl, + }, nil +} + +func (r *QQRobot) queryQQMusic(musicName string) string { + // 搜索音乐信息 第一首歌 + h1 := http.Header{ + "User-Agent": []string{"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"}, + } + search, _ := url.Parse("https://c.y.qq.com/soso/fcgi-bin/client_search_cp") + search.RawQuery = url.Values{ + "w": []string{musicName}, + }.Encode() + res := netGet(search.String(), h1) + info := gjson.ParseBytes(res[9 : len(res)-1]).Get("data.song.list.0") + + return info.Get("songid").String() +} + +// netGet 返回请求数据 +func netGet(url string, header http.Header) []byte { + client := &http.Client{} + request, _ := http.NewRequest("GET", url, nil) + request.Header = header + res, err := client.Do(request) + if err != nil { + return nil } - return gjson.ParseBytes(data).Get("result.songs.0.id").Int() + defer res.Body.Close() + result, _ := ioutil.ReadAll(res.Body) + return result } From 7bc81247fb897867eb2908bd6f5e403309e715ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 02:36:36 +0800 Subject: [PATCH 025/612] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=90=AC=E6=AD=8C?= =?UTF-8?q?=E7=9A=84=E5=85=B3=E9=94=AE=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/command.go | 4 ++-- qq_robot/config.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qq_robot/command.go b/qq_robot/command.go index a56f489f1..c2e4b8859 100644 --- a/qq_robot/command.go +++ b/qq_robot/command.go @@ -87,8 +87,8 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (er json.Unmarshal(out, &msg) } else if match = CommandRegex_Music.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_Music.SubexpNames()) { - // full_match|musicName - musicName := match[1] + // full_match|听歌关键词|musicName + musicName := match[2] musicElem, err := r.makeMusicShareElement(musicName, message.QQMusic) if err != nil { diff --git a/qq_robot/config.go b/qq_robot/config.go index 251674c0c..e86be5006 100644 --- a/qq_robot/config.go +++ b/qq_robot/config.go @@ -84,7 +84,7 @@ var ( CommandRegex_RuleNameList = regexp.MustCompile(`RuleNameList`) CommandRegex_BuyCard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) CommandRegex_QueryCard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) - CommandRegex_Music = regexp.MustCompile(`.*点歌\s+(?P.+)`) + CommandRegex_Music = regexp.MustCompile(`.*(点歌|听歌|点首|来首|想听|要听)\s*(?P.+)`) ) type RuleConfig struct { From bc3346999b6c97096a3073d59e431134bdd74614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 05:56:11 +0800 Subject: [PATCH 026/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=BA=A7=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qq_robot/ai_chat.go | 2 +- qq_robot/check_update.go | 6 +++--- qq_robot/config.go | 2 +- qq_robot/food.go | 2 +- qq_robot/log.go | 2 +- qq_robot/rule.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/qq_robot/ai_chat.go b/qq_robot/ai_chat.go index bd0232add..dbac6c6af 100644 --- a/qq_robot/ai_chat.go +++ b/qq_robot/ai_chat.go @@ -40,7 +40,7 @@ func (r *QQRobot) aiChat(targetQQ int64, chatText string) (responseText string) response, err := r.aiClient.TextProcess(request) if err != nil { - logger.Debugf("aiChat(qq=%v, text=%v) err=%v", targetQQ, chatText, err) + logger.Errorf("aiChat(qq=%v, text=%v) err=%v", targetQQ, chatText, err) return } diff --git a/qq_robot/check_update.go b/qq_robot/check_update.go index b01171ab1..fa771d39b 100644 --- a/qq_robot/check_update.go +++ b/qq_robot/check_update.go @@ -132,7 +132,7 @@ func (r *QQRobot) getLatestGitVersion(gitChangelogPage string) (latestVersion st func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) { resp, err := r.HttpClient.Get(gitChangelogPage) if err != nil { - logger.Errorf("getLatestGitVersion gitChangelogPage=%v err=%v", gitChangelogPage, err) + logger.Debugf("getLatestGitVersion gitChangelogPage=%v err=%v", gitChangelogPage, err) return VersionNone, "" } defer resp.Body.Close() @@ -140,7 +140,7 @@ func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) // 获取网页内容 bytesData, err := ioutil.ReadAll(resp.Body) if err != nil { - logger.Errorf("getLatestGitVersion gitChangelogPage=%v err=%v", gitChangelogPage, err) + logger.Debugf("getLatestGitVersion gitChangelogPage=%v err=%v", gitChangelogPage, err) return VersionNone, "" } @@ -149,7 +149,7 @@ func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) // 解析版本信息 matches := regGitVersion.FindAllStringSubmatch(htmlText, -1) if len(matches) == 0 { - logger.Errorf("getLatestGitVersion gitChangelogPage=%v can not find any match, html text=%v", gitChangelogPage, err, htmlText) + logger.Debugf("getLatestGitVersion gitChangelogPage=%v can not find any match, html text=%v", gitChangelogPage, err, htmlText) return VersionNone, "" } diff --git a/qq_robot/config.go b/qq_robot/config.go index e86be5006..c433d066d 100644 --- a/qq_robot/config.go +++ b/qq_robot/config.go @@ -187,7 +187,7 @@ func LoadConfig(configPath string) Config { logger.Fatalf("load toml file fail, err=%v", err) } config.Init() - // logger.Debugf("%#v", config) + logger.Debugf("%#v", config) return config } diff --git a/qq_robot/food.go b/qq_robot/food.go index 2c21dbad9..e8c99aa27 100644 --- a/qq_robot/food.go +++ b/qq_robot/food.go @@ -169,7 +169,7 @@ func (r *QQRobot) getOneCachedFoodImage(rule *Rule) FoodImage { foodImage = fi break } - logger.Debugf("%v select the %v th food from %v foods, food=%v, 发完这个,库存食物图片还剩%v", + logger.Infof("%v select the %v th food from %v foods, food=%v, 发完这个,库存食物图片还剩%v", r.currentTime(), takeNthFood+1, len(rule.CachedFoodImages), foodImage, len(rule.CachedFoodImages)-1) return foodImage } diff --git a/qq_robot/log.go b/qq_robot/log.go index 42b53aa40..f8c1b3dba 100644 --- a/qq_robot/log.go +++ b/qq_robot/log.go @@ -11,7 +11,7 @@ var logger *mylogger.SugaredLogger func init() { // 初始化日志 var err error - logger, err = mylogger.NewLogger("logs", "qq_robot", mylogger.DebugLevel) + logger, err = mylogger.NewLogger("logs", "qq_robot", mylogger.InfoLevel) if err != nil { fmt.Printf("new logger err=%v\n", err) return diff --git a/qq_robot/rule.go b/qq_robot/rule.go index 5a2ef6207..a808ca8b0 100644 --- a/qq_robot/rule.go +++ b/qq_robot/rule.go @@ -48,7 +48,7 @@ func (r *Rule) UpdateFoodPage(foodSiteUrl string) { if _, fetched := fetchedPage[foodPage]; !fetched { fetchedPage[foodPage] = struct{}{} r.SiteToFoodPage[foodSiteUrl] = foodPage - logger.Debugf("rule=%v UpdateFoodPage to %v", r.Config.Name, foodPage) + logger.Infof("rule=%v UpdateFoodPage to %v", r.Config.Name, foodPage) return } } From 063eb3405c4eb4acdac2eb028a6dd2f9f1969208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 16:28:06 +0800 Subject: [PATCH 027/612] =?UTF-8?q?=E8=B0=83=E6=95=B4todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c43fc1d84..0ee00c98f 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,4 @@ # TODO -[ ] 看看是否有更多事件值得接入,目前仅处理了群聊、私聊和加群这三个事件 \ No newline at end of file +- [ ] 看看是否有更多事件值得接入,目前仅处理了群聊、私聊和加群这三个事件 From 84d95a09795ff7e53ed2007fca41643a1e3eee6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 16:39:24 +0800 Subject: [PATCH 028/612] =?UTF-8?q?go=20build=20=E5=89=8D=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0generate=EF=BC=8C=E9=81=BF=E5=85=8D=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + Dockerfile | 1 + qq_robot/_build.bat | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8ca988c4..94e1cb594 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,7 @@ jobs: if $IS_PR ; then echo $PR_PROMPT; fi export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 + go generate . go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . - name: Upload artifact uses: actions/upload-artifact@v2 diff --git a/Dockerfile b/Dockerfile index c5eff4d93..ad19d987b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,7 @@ COPY ./ . RUN set -ex \ && cd /build \ + && go generate . && go build -ldflags "-s -w -extldflags '-static'" -o cqhttp FROM alpine:latest diff --git a/qq_robot/_build.bat b/qq_robot/_build.bat index 041503b33..e68b14306 100644 --- a/qq_robot/_build.bat +++ b/qq_robot/_build.bat @@ -1 +1,2 @@ +go generate . go build -v -o qq_robot.exe . From fbdabf60bbbe8baf0ba386d5a3d82357edba0454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 16:43:04 +0800 Subject: [PATCH 029/612] =?UTF-8?q?=E7=A7=BB=E9=99=A4resource.syso?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E4=B8=BA=E5=88=A4=E5=AE=9A=E5=9C=A8windows?= =?UTF-8?q?=E4=B8=8B=E7=9A=84=E6=83=85=E5=86=B5=E6=97=B6=E5=8E=BB=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- README.md | 3 +++ resource.syso | Bin 271795 -> 0 bytes 3 files changed, 5 insertions(+), 1 deletion(-) delete mode 100644 resource.syso diff --git a/.gitignore b/.gitignore index e2a255ffa..8c0559b9d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ device.json data/ logs/ -*.exe \ No newline at end of file +*.exe +resource.syso \ No newline at end of file diff --git a/README.md b/README.md index 0ee00c98f..0a2f2ab3a 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,6 @@ # TODO - [ ] 看看是否有更多事件值得接入,目前仅处理了群聊、私聊和加群这三个事件 + + +# re: 移除resource.syso,改为判定在windows下的情况时去自动生成 \ No newline at end of file diff --git a/resource.syso b/resource.syso deleted file mode 100644 index a157a9886896f19f284acce49ee02a66678afbe5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 271795 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 c5&O4iR9>%!qm^4E(Vf^zL0W* Date: Sun, 3 Oct 2021 16:51:31 +0800 Subject: [PATCH 030/612] =?UTF-8?q?=E8=B0=83=E6=95=B4ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94e1cb594..7befcd29d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,10 +25,12 @@ jobs: fail-fast: true steps: - uses: actions/checkout@v2 + - name: Setup Go environment uses: actions/setup-go@v2.1.3 with: go-version: 1.17 + - name: Cache downloaded module uses: actions/cache@v2 with: @@ -36,6 +38,7 @@ jobs: ~/.cache/go-build ~/go/pkg/mod key: ${{ runner.os }}-go-${{ matrix.goos }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }} + - name: Build binary file env: GOOS: ${{ matrix.goos }} @@ -46,8 +49,10 @@ jobs: if $IS_PR ; then echo $PR_PROMPT; fi export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 - go generate . + + if [ $GOOS = "windows" ]; then go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo; go generate .; fi go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . + - name: Upload artifact uses: actions/upload-artifact@v2 if: ${{ !github.head_ref }} From 92177eef5a9462aba97fa2ce2fc3d4ad71aa3072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 17:09:52 +0800 Subject: [PATCH 031/612] =?UTF-8?q?=E8=B0=83=E6=95=B4ci=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7befcd29d..a4524492a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 - if [ $GOOS = "windows" ]; then go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo; go generate .; fi + if [ $GOOS = "windows" ]; then go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest; go generate .; fi go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . - name: Upload artifact From 3d34e1ad7a5667505d6c128d1119ad1ad193528a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 17:52:53 +0800 Subject: [PATCH 032/612] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dlint=E6=8A=A5?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- global/config/config.go | 2 +- main.go | 4 +- {qq_robot => qqrobot}/_build.bat | 0 {qq_robot => qqrobot}/_start.bat | 0 ...0\346\234\272\345\231\250\344\272\272.lnk" | Bin {qq_robot => qqrobot}/ai_chat.go | 10 +- {qq_robot => qqrobot}/check_update.go | 67 ++--- {qq_robot => qqrobot}/color.go | 2 +- {qq_robot => qqrobot}/command.go | 38 +-- {qq_robot => qqrobot}/config.go | 107 ++++---- {qq_robot => qqrobot}/default_config.toml | 0 qqrobot/doc.go | 2 + {qq_robot => qqrobot}/food.go | 58 +++-- {qq_robot => qqrobot}/icon.ico | Bin {qq_robot => qqrobot}/log.go | 2 +- {qq_robot => qqrobot}/music.go | 33 +-- {qq_robot => qqrobot}/mute.go | 10 +- {qq_robot => qqrobot}/qq_robot.exe.manifest | 0 {qq_robot => qqrobot}/qq_robot.go | 235 +++++++++--------- {qq_robot => qqrobot}/qq_robot_test.go | 6 +- {qq_robot => qqrobot}/rule.go | 33 +-- {qq_robot => qqrobot}/util.go | 40 +-- versioninfo.json | 4 +- 24 files changed, 353 insertions(+), 304 deletions(-) rename {qq_robot => qqrobot}/_build.bat (100%) rename {qq_robot => qqrobot}/_start.bat (100%) rename "qq_robot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" => "qqrobot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" (100%) rename {qq_robot => qqrobot}/ai_chat.go (83%) rename {qq_robot => qqrobot}/check_update.go (73%) rename {qq_robot => qqrobot}/color.go (86%) rename {qq_robot => qqrobot}/command.go (63%) rename {qq_robot => qqrobot}/config.go (78%) rename {qq_robot => qqrobot}/default_config.toml (100%) create mode 100644 qqrobot/doc.go rename {qq_robot => qqrobot}/food.go (69%) rename {qq_robot => qqrobot}/icon.ico (100%) rename {qq_robot => qqrobot}/log.go (94%) rename {qq_robot => qqrobot}/music.go (87%) rename {qq_robot => qqrobot}/mute.go (96%) rename {qq_robot => qqrobot}/qq_robot.exe.manifest (100%) rename {qq_robot => qqrobot}/qq_robot.go (76%) rename {qq_robot => qqrobot}/qq_robot_test.go (94%) rename {qq_robot => qqrobot}/rule.go (61%) rename {qq_robot => qqrobot}/util.go (77%) diff --git a/README.md b/README.md index 0a2f2ab3a..1b20d4ebe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # qq机器人 -基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 实现的qq机器人,在其上额外增加了一个直接本地处理消息并根据配置自动回复的逻辑, 具体改动可见 *qq_robot* 目录 +基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 实现的qq机器人,在其上额外增加了一个直接本地处理消息并根据配置自动回复的逻辑, 具体改动可见 *qqrobot* 目录 # 使用说明 @@ -10,7 +10,7 @@ ## 配置自动回复逻辑 -复制qq_robot/default_config.toml到qq_robot.exe所在目录,并重命名为config.toml 然后打开qq_robot/config.go和config.toml,按照注释,自行调整配置,并添加各种规则 +复制qqrobot/default_config.toml到qq_robot.exe所在目录,并重命名为config.toml 然后打开qq_robot/config.go和config.toml,按照注释,自行调整配置,并添加各种规则 # TODO diff --git a/global/config/config.go b/global/config/config.go index 9505e63f2..b99e41a40 100644 --- a/global/config/config.go +++ b/global/config/config.go @@ -40,7 +40,7 @@ type Config struct { Interval int `yaml:"interval"` } UseSSOAddress bool `yaml:"use-sso-address"` - FastStart bool `yaml:"fast-start"` + FastStart bool `yaml:"fast-start"` } `yaml:"account"` Heartbeat struct { diff --git a/main.go b/main.go index 8816022d5..d39bf91d0 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global/config" "github.com/Mrs4s/go-cqhttp/global/terminal" "github.com/Mrs4s/go-cqhttp/global/update" - "github.com/Mrs4s/go-cqhttp/qq_robot" + "github.com/Mrs4s/go-cqhttp/qqrobot" "github.com/Mrs4s/go-cqhttp/server" "github.com/Mrs4s/MiraiGo/binary" @@ -426,7 +426,7 @@ func main() { log.Info("アトリは、高性能ですから!") log.Info("启动本地的机器人处理程序,用于直接收发消息和自动回复") - robot := qq_robot.NewQQRobot(bot, "config.toml") + robot := qqrobot.NewQQRobot(bot, "config.toml") robot.RegisterHandlers() robot.Start() defer robot.Stop() diff --git a/qq_robot/_build.bat b/qqrobot/_build.bat similarity index 100% rename from qq_robot/_build.bat rename to qqrobot/_build.bat diff --git a/qq_robot/_start.bat b/qqrobot/_start.bat similarity index 100% rename from qq_robot/_start.bat rename to qqrobot/_start.bat diff --git "a/qq_robot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" "b/qqrobot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" similarity index 100% rename from "qq_robot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" rename to "qqrobot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" diff --git a/qq_robot/ai_chat.go b/qqrobot/ai_chat.go similarity index 83% rename from qq_robot/ai_chat.go rename to qqrobot/ai_chat.go index dbac6c6af..f8eaee342 100644 --- a/qq_robot/ai_chat.go +++ b/qqrobot/ai_chat.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import ( "strconv" @@ -12,14 +12,14 @@ import ( func (r *QQRobot) initAiChat() { cfg := r.Config.Robot - if cfg.TencentAiAppId == "" || cfg.TencentAiAppKey == "" || cfg.TencentAiBotId == "" { + if cfg.TencentAiAppID == "" || cfg.TencentAiAppKey == "" || cfg.TencentAiBotID == "" { logger.Warnf("未配置腾讯ai的appid、appkey、botid,将不初始化aichat,详情可见 https://console.cloud.tencent.com/tbp/bots") return } - credential := common.NewCredential(cfg.TencentAiAppId, cfg.TencentAiAppKey) + credential := common.NewCredential(cfg.TencentAiAppID, cfg.TencentAiAppKey) cpf := profile.NewClientProfile() - cpf.HttpProfile.Endpoint = TencentAiChatApi + cpf.HttpProfile.Endpoint = TencentAiChatAPI r.aiClient, _ = tbp.NewClient(credential, "", cpf) } @@ -33,7 +33,7 @@ func (r *QQRobot) aiChat(targetQQ int64, chatText string) (responseText string) request := tbp.NewTextProcessRequest() - request.BotId = common.StringPtr(cfg.TencentAiBotId) + request.BotId = common.StringPtr(cfg.TencentAiBotID) request.BotEnv = common.StringPtr("release") request.TerminalId = common.StringPtr(strconv.FormatInt(targetQQ, 10)) request.InputText = common.StringPtr(chatText) diff --git a/qq_robot/check_update.go b/qqrobot/check_update.go similarity index 73% rename from qq_robot/check_update.go rename to qqrobot/check_update.go index fa771d39b..4ecd8f484 100644 --- a/qq_robot/check_update.go +++ b/qqrobot/check_update.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import ( "fmt" @@ -19,7 +19,7 @@ func (r *QQRobot) checkUpdates() { for _, rule := range r.Config.NotifyUpdate.Rules { lastVersion := r.CheckUpdateVersionMap[rule.Name] latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogPage) - if version_less(lastVersion, latestVersion) { + if versionLess(lastVersion, latestVersion) { // 版本有更新 r.CheckUpdateVersionMap[rule.Name] = latestVersion @@ -29,24 +29,24 @@ func (r *QQRobot) checkUpdates() { } replies := r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) nowStr := r.currentTime() - for _, groupId := range rule.NotifyGroups { - rspId := r.cqBot.SendGroupMessage(groupId, replies) - if rspId == -1 { - logger.Errorf("【%v Failed】 %v groupId=%v replies=%v err=%v", rule.Name, nowStr, groupId, replies, rspId) + for _, groupID := range rule.NotifyGroups { + rspID := r.cqBot.SendGroupMessage(groupID, replies) + if rspID == -1 { + logger.Errorf("【%v Failed】 %v groupID=%v replies=%v err=%v", rule.Name, nowStr, groupID, replies, rspID) continue } - logger.Infof("【%v】 %v groupId=%v replies=%v", rule.Name, nowStr, groupId, replies) + logger.Infof("【%v】 %v groupID=%v replies=%v", rule.Name, nowStr, groupID, replies) } logger.Infof("check update %v, from %v to %v", rule.Name, lastVersion, latestVersion) } } } -func (r *QQRobot) manualTriggerUpdateMessage(groupId int64) (replies *message.SendingMessage) { +func (r *QQRobot) manualTriggerUpdateMessage(groupID int64) (replies *message.SendingMessage) { for _, rule := range r.Config.NotifyUpdate.Rules { inRange := false for _, group := range rule.NotifyGroups { - if groupId == group { + if groupID == group { inRange = true break } @@ -77,12 +77,12 @@ func (r *QQRobot) makeNotifyUpdatesReplies(rule NotifyUpdateRule, latestVersion for _, atOnTrigger := range rule.AtQQsOnTrigger { replies.Append(message.NewAt(atOnTrigger)) } - msg := strings.ReplaceAll(rule.Message, TemplateArgs_GitVersion, latestVersion) - msg = strings.ReplaceAll(msg, TemplateArgs_UpdateMessage, updateMessage) + msg := strings.ReplaceAll(rule.Message, templateargsGitversion, latestVersion) + msg = strings.ReplaceAll(msg, templateargsUpdatemessage, updateMessage) replies.Append(message.NewText(msg)) // 如配置了图片url,则额外发送图片 - if rule.ImageUrl != "" { - r.tryAppendImageByUrl(replies, rule.ImageUrl) + if rule.ImageURL != "" { + r.tryAppendImageByURL(replies, rule.ImageURL) } return replies } @@ -103,18 +103,19 @@ var regGitVersion = regexp.MustCompile(`([vV][0-9.]+)(\s+\d+\.\d+\.\d+)`) var regUpdateInfo = regexp.MustCompile(`(更新公告)\s*
      ((\s|\S)+?)
    `) var regUpdateMessages = regexp.MustCompile("
  • (.+?)
  • ") +// VersionNone 默认版本号 var VersionNone = "v0.0.0" -// github的镜像站 -var GITHUB_MIRROR_SITES = []string{ +// GithubMirrorSites github的镜像站 +var GithubMirrorSites = []string{ "hub.fastgit.org", "github.com.cnpmjs.org", } func (r *QQRobot) getLatestGitVersion(gitChangelogPage string) (latestVersion string, updateMessage string) { - var urls []string + urls := make([]string, 0, len(GithubMirrorSites)+1) // 先尝试国内镜像,最后尝试直接访问 - for _, mirrorSite := range GITHUB_MIRROR_SITES { + for _, mirrorSite := range GithubMirrorSites { urls = append(urls, strings.ReplaceAll(gitChangelogPage, "github.com", mirrorSite)) } urls = append(urls, gitChangelogPage) @@ -130,7 +131,7 @@ func (r *QQRobot) getLatestGitVersion(gitChangelogPage string) (latestVersion st } func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) { - resp, err := r.HttpClient.Get(gitChangelogPage) + resp, err := r.httpClient.Get(gitChangelogPage) if err != nil { logger.Debugf("getLatestGitVersion gitChangelogPage=%v err=%v", gitChangelogPage, err) return VersionNone, "" @@ -153,13 +154,13 @@ func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) return VersionNone, "" } - var versions []string + versions := make([]string, 0, len(matches)) for _, match := range matches { versions = append(versions, match[1]) } sort.Slice(versions, func(i, j int) bool { - return !version_less(versions[i], versions[j]) + return !versionLess(versions[i], versions[j]) }) // 解析更新信息 @@ -178,32 +179,34 @@ func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) return versions[0], updateMessage } -func version_less(version_left, version_right string) bool { - return version_less_int_list(version_to_version_int_list(version_left), version_to_version_int_list(version_right)) +func versionLess(versionLeft, versionRight string) bool { + return versionLessIntList(versionToVersionIntList(versionLeft), versionToVersionIntList(versionRight)) } -func version_less_int_list(version_int_list_left, version_int_list_right []int64) bool { - length := len(version_int_list_left) - if len(version_int_list_right) < length { - length = len(version_int_list_right) +func versionLessIntList(versionIntListLeft, versionIntListRight []int64) bool { + length := len(versionIntListLeft) + if len(versionIntListRight) < length { + length = len(versionIntListRight) } for idx := 0; idx < length; idx++ { - if version_int_list_left[idx] != version_int_list_right[idx] { - return version_int_list_left[idx] < version_int_list_right[idx] + if versionIntListLeft[idx] != versionIntListRight[idx] { + return versionIntListLeft[idx] < versionIntListRight[idx] } } - return len(version_int_list_left) < len(version_int_list_right) + return len(versionIntListLeft) < len(versionIntListRight) } // v3.2.2 => [3, 2, 2], 3.2.2 => [3, 2, 2] -func version_to_version_int_list(version string) []int64 { +func versionToVersionIntList(version string) []int64 { // 移除v if version[0] == 'v' { version = version[1:] } - var versionIntList []int64 - for _, subVersion := range strings.Split(version, ".") { + + subVersionList := strings.Split(version, ".") + versionIntList := make([]int64, 0, len(subVersionList)) + for _, subVersion := range subVersionList { subVersionInt, _ := strconv.ParseInt(subVersion, 10, 64) versionIntList = append(versionIntList, subVersionInt) } diff --git a/qq_robot/color.go b/qqrobot/color.go similarity index 86% rename from qq_robot/color.go rename to qqrobot/color.go index 1c9dbd37e..6c237eb24 100644 --- a/qq_robot/color.go +++ b/qqrobot/color.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import "github.com/gookit/color" diff --git a/qq_robot/command.go b/qqrobot/command.go similarity index 63% rename from qq_robot/command.go rename to qqrobot/command.go index c2e4b8859..0f8388f91 100644 --- a/qq_robot/command.go +++ b/qqrobot/command.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import ( "encoding/json" @@ -8,13 +8,15 @@ import ( "strings" "time" + "github.com/pkg/errors" + "github.com/Mrs4s/MiraiGo/message" ) // 2021/10/02 5:25 by fzls -func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (err error, msg string, extraReplies []message.IMessageElement) { +func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (msg string, extraReplies []message.IMessageElement, err error) { var match []string - if match = CommandRegex_AddWhiteList.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_AddWhiteList.SubexpNames()) { + if match = commandregexAddwhitelist.FindStringSubmatch(commandStr); len(match) == len(commandregexAddwhitelist.SubexpNames()) { // full_match|ruleName|qq ruleName := match[1] qq, _ := strconv.ParseInt(match[2], 10, 64) @@ -30,7 +32,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (er } msg += fmt.Sprintf("已将【%v】加入到规则【%v】的白名单", qq, ruleName) } - } else if match = CommandRegex_RuleNameList.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_RuleNameList.SubexpNames()) { + } else if match = commandregexRulenamelist.FindStringSubmatch(commandStr); len(match) == len(commandregexRulenamelist.SubexpNames()) { for _, rule := range r.Rules { if _, ok := rule.Config.GroupIds[m.GroupCode]; !ok { continue @@ -41,11 +43,11 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (er } msg += ", " + rule.Config.Name } - } else if match = CommandRegex_BuyCard.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_BuyCard.SubexpNames()) { + } else if match = commandregexBuycard.FindStringSubmatch(commandStr); len(match) == len(commandregexBuycard.SubexpNames()) { now := time.Now() endTime, _ := time.Parse("2006-01-02", r.Config.Robot.SellCardEndTime) if !r.Config.Robot.EnableSellCard || now.After(endTime) { - return nil, "目前尚未启用卖卡功能哦", nil + return "目前尚未启用卖卡功能哦", nil, nil } qq := match[1] @@ -61,10 +63,13 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (er out, err := cmd.Output() if err != nil { - return err, "", nil + return "", nil, err } - json.Unmarshal(out, &msg) + err = json.Unmarshal(out, &msg) + if err != nil { + return "", nil, err + } if strings.Contains(msg, "成功发送以下卡片") { image, err := r._makeLocalImage("https://z3.ax1x.com/2020/12/16/r1yWZT.png") @@ -72,7 +77,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (er extraReplies = append(extraReplies, image) } } - } else if match = CommandRegex_QueryCard.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_QueryCard.SubexpNames()) { + } else if match = commandregexQuerycard.FindStringSubmatch(commandStr); len(match) == len(commandregexQuerycard.SubexpNames()) { logger.Infof("开始查询卡片信息~") cmd := exec.Command("python", "sell_cards.py", "--run_remote", @@ -82,24 +87,27 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (er out, err := cmd.Output() if err != nil { - return err, "", nil + return "", nil, err } - json.Unmarshal(out, &msg) - } else if match = CommandRegex_Music.FindStringSubmatch(commandStr); len(match) == len(CommandRegex_Music.SubexpNames()) { + err = json.Unmarshal(out, &msg) + if err != nil { + return "", nil, err + } + } else if match = commandRegexMusic.FindStringSubmatch(commandStr); len(match) == len(commandRegexMusic.SubexpNames()) { // full_match|听歌关键词|musicName musicName := match[2] musicElem, err := r.makeMusicShareElement(musicName, message.QQMusic) if err != nil { - return fmt.Errorf("没有找到歌曲:%v", musicName), "", nil + return "", nil, errors.Errorf("没有找到歌曲:%v", musicName) } msg = fmt.Sprintf("请欣赏歌曲:%v", musicName) extraReplies = append(extraReplies, musicElem) } else { - return fmt.Errorf("没有找到该指令哦"), "", nil + return "", nil, errors.Errorf("没有找到该指令哦") } - return nil, msg, extraReplies + return msg, extraReplies, nil } diff --git a/qq_robot/config.go b/qqrobot/config.go similarity index 78% rename from qq_robot/config.go rename to qqrobot/config.go index c433d066d..ed9a2eba5 100644 --- a/qq_robot/config.go +++ b/qqrobot/config.go @@ -1,32 +1,35 @@ -package qq_robot +package qqrobot import ( - "fmt" "os" "regexp" "github.com/BurntSushi/toml" + "github.com/pkg/errors" ) -const TencentAiChatApi = "tbp.tencentcloudapi.com" +// TencentAiChatAPI 腾讯ai聊天接口 +const TencentAiChatAPI = "tbp.tencentcloudapi.com" const ( - TemplateArgs_WorkTime = "$work_time$" // 本次工作时长 - TemplateArgs_FoodName = "$food_name$" // 食物名字 - TemplateArgs_FoodPage = "$food_page$" // 食物页码 - TemplateArgs_CurrentPeriodName = "$current_period_name$" // 当前时间段的名称 - TemplateArgs_MuteTime = "$mute_time$" // 禁言时间 - TemplateArgs_CD = "$cd$" // CD - TemplateArgs_GitVersion = "$git_version$" // 代码版本,若对应规则配置了changelog的链接,则会将这个变量替换为解析出的最新的版本号,如https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD - TemplateArgs_UpdateMessage = "$update_message$" // 最新更新信息,若对应规则配置了changelog的链接,则会将这个变量替换为解析出的最新的更新信息,如https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD + templateargsWorktime = "$work_time$" // 本次工作时长 + templateargsFoodname = "$food_name$" // 食物名字 + templateargsFoodpage = "$food_page$" // 食物页码 + templateargsCurrentperiodname = "$current_period_name$" // 当前时间段的名称 + templateargsMutetime = "$mute_time$" // 禁言时间 + templateargsCd = "$cd$" // CD + templateargsGitversion = "$git_version$" // 代码版本,若对应规则配置了changelog的链接,则会将这个变量替换为解析出的最新的版本号,如https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD + templateargsUpdatemessage = "$update_message$" // 最新更新信息,若对应规则配置了changelog的链接,则会将这个变量替换为解析出的最新的更新信息,如https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD ) +// NotifyConfig 通知规则 type NotifyConfig struct { Name string `toml:"name"` // 操作名称 NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 Message string `toml:"message"` // 通知的消息 } +// RobotConfig 机器人配置 type RobotConfig struct { IgnoreRobotQQs []int64 `toml:"ignore_robot_qqs"` // 机器人的qq号列表,这些号的消息将被忽略,避免多个机器人同时在群里不停循环回复 Timeout int64 `toml:"timeout"` // http请求超时 @@ -35,9 +38,9 @@ type RobotConfig struct { OnStop NotifyConfig `toml:"on_stop"` // 机器人下线时的操作,参数:$work_time$=本次工作时长 MaxRetryTimes int `toml:"max_retry_times"` // 单条消息处理失败后,最多重试次数 MaxContinueEmptyLines int `toml:"max_continue_empty_lines"` // 最大允许的连续空行数目,为0则不限制 - TencentAiAppId string `toml:"tencent_ai_app_id"` // 腾讯ai开放平台的应用ID,具体可见 https://console.cloud.tencent.com/tbp/bots + TencentAiAppID string `toml:"tencent_ai_app_id"` // 腾讯ai开放平台的应用ID,具体可见 https://console.cloud.tencent.com/tbp/bots TencentAiAppKey string `toml:"tencent_ai_app_key"` // 腾讯ai开放平台的应用秘钥 - TencentAiBotId string `toml:"tencent_ai_bot_id"` // 腾讯ai开放平台的机器人BotId + TencentAiBotID string `toml:"tencent_ai_bot_id"` // 腾讯ai开放平台的机器人BotId ChatAnswerNotFoundMessage string `toml:"chat_answer_not_found_message"` // 聊天结果未找到时的提示语 PersonalMessageNotSupportedMessage string `toml:"personal_message_not_supported_message"` // 不支持私聊时的提示语 # 本QQ是机器人,基本不会登录该QQ人工查看消息,如果有事,请私聊大号~ PersonalMessageNotSupportedImage string `toml:"personal_message_not_supported_image"` // 不支持私聊时的图片 @@ -46,47 +49,53 @@ type RobotConfig struct { SellCardEndTime string `toml:"sell_card_end_time"` // 本次卖卡过期时间 %Y-%m-%d } +// RuleType 规则类型 type RuleType string -//const ( -// RuleType_AutoReply = "自动回复" -// RuleType_Command = "机器人指令" -// RuleType_AtSomeOne = "AT某人" -// RuleType_Food = "深夜美食" -// RuleType_Test = "测试" -//) +// const ( +// RuleType_AutoReply = "自动回复" +// RuleType_Command = "机器人指令" +// RuleType_AtSomeOne = "AT某人" +// RuleType_Food = "深夜美食" +// RuleType_Test = "测试" +// ) -const RuleTypeMaxApplyCount_Infinite = -1 +// RuleTypeMaxApplyCountInfinite 不限制应用次数上限 +const RuleTypeMaxApplyCountInfinite = -1 +// RuleTypeConfig 规则类别 type RuleTypeConfig struct { Type RuleType `toml:"type"` // 规则类别 MaxApplyCount int32 `toml:"type_max_apply_count"` // 同一条消息最多应用该类型的规则的数目,-1表示不限制 } +// GroupTypeConfig 群类别 type GroupTypeConfig struct { Type string `toml:"type"` // 群类别 GroupIds []int64 `toml:"group_ids"` // 归属该类别的群组id列表 } +// ActionType 操作类型 type ActionType string const ( - ActionType_Guide ActionType = "guide" - ActionType_Command ActionType = "command" - ActionType_Food ActionType = "food" - ActionType_AiChat ActionType = "ai_chat" - ActionType_SendUpdateMessage ActionType = "send_update_message" - ActionType_Repeater ActionType = "repeater" + actionTypeGuide ActionType = "guide" + actionTypeCommand ActionType = "command" + actionTypeFood ActionType = "food" + actiontypeAichat ActionType = "ai_chat" + actiontypeSendupdatemessage ActionType = "send_update_message" + actiontypeRepeater ActionType = "repeater" ) var ( - CommandRegex_AddWhiteList = regexp.MustCompile(`\s*AddWhiteList\s+(?P.+?)\s+(?P\d+)`) - CommandRegex_RuleNameList = regexp.MustCompile(`RuleNameList`) - CommandRegex_BuyCard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) - CommandRegex_QueryCard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) - CommandRegex_Music = regexp.MustCompile(`.*(点歌|听歌|点首|来首|想听|要听)\s*(?P.+)`) + commandregexAddwhitelist = regexp.MustCompile(`\s*AddWhiteList\s+(?P.+?)\s+(?P\d+)`) + commandregexRulenamelist = regexp.MustCompile(`RuleNameList`) + commandregexBuycard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) + commandregexQuerycard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) + commandRegexMusic = regexp.MustCompile(`.*(点歌|听歌|点首|来首|想听|要听)\s*(?P.+)`) ) +// RuleConfig 规则配置 type RuleConfig struct { Name string `toml:"name"` // 规则名称 Type RuleType `toml:"type"` // 规则类别 @@ -105,7 +114,7 @@ type RuleConfig struct { AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 当触发该规则时,需要at的qq列表 AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 当触发该规则时,是否需要@全体成员 GuideContent string `toml:"guide_content"` // 内容 - ImageUrl string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 + ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 RandomImageUrls []string `toml:"random_image_urls"` // 若配置,则从中随机一个作为图片发送,同时ImageUrl配置会被覆盖 CD int64 `toml:"cd"` // cd时长(秒),0表示不设定,若设定,在cd内触发规则时,若设置了cd内回复内容,则回复该内容,否则视为未触发 GuideContentInCD string `toml:"guide_content_in_cd"` // cd内触发规则时的回复内容 @@ -113,7 +122,7 @@ type RuleConfig struct { ForwardToGroups []int64 `toml:"forward_to_groups"` // 将消息转发到该QQ群列表 RepeatToGroups []int64 `toml:"repeat_to_groups"` // 将消息复读到该QQ群列表 RepeatToGroupTypes []string `toml:"repeat_to_group_types"` // 复读适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 - FoodSiteUrlList []string `toml:"food_site_url_list"` // 美食图片来源网站列表 + FoodSiteURLList []string `toml:"food_site_url_list"` // 美食图片来源网站列表 FoodDescription string `toml:"food_description"` // 美食描述,参数:$food_name$=食物名字 RevokeMessage bool `toml:"revoke_message"` // 是否撤回该条消息 MuteTime int64 `toml:"mute_time"` // 禁言时间,为0则表示不禁言(单位为秒) @@ -126,6 +135,7 @@ type RuleConfig struct { GuideContentHasNoPermission string `toml:"guide_content_has_no_permission"` // 当无权限触发该指令时的回复 } +// TimePeriod 时间规则 type TimePeriod struct { // 以下任意字段不设置则不检查 StartSecond int `toml:"start_second"` // 起始的秒(包含),0-59 @@ -138,11 +148,13 @@ type TimePeriod struct { EndWeekDay int `toml:"end_weekday"` // 截止的小时(包含),1-7表示周一到周日 } +// MiscConfig 其他配置 type MiscConfig struct { Fireworks FireworksConfig `toml:"fireworks"` Ocr OcrConfig `toml:"ocr"` } +// FireworksConfig 烟花活动 type FireworksConfig struct { Enable bool `toml:"enable"` Tips string `toml:"tips"` @@ -150,26 +162,30 @@ type FireworksConfig struct { NotifyGroups []int64 `toml:"notify_groups"` } +// OcrConfig ocr配置 type OcrConfig struct { Enable bool `toml:"enable"` } +// NotifyUpdateConfig 通知更新配置 type NotifyUpdateConfig struct { CheckInterval int64 `toml:"check_interval"` // 检查更新的间隔(秒) Rules []NotifyUpdateRule `toml:"rules"` // 检查规则 } +// NotifyUpdateRule 通知更新规则 type NotifyUpdateRule struct { Name string `toml:"name"` // 名称 NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 NotifyGroupTypes []string `toml:"notify_group_types"` // 通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 Message string `toml:"message"` // 通知的消息,参数:$git_version$=最新版本, $update_message$=更新信息 - ImageUrl string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 + ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 GitChangelogPage string `toml:"git_changelog_page"` // git仓库的changelog的url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 需要at的qq列表 AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 是否需要@全体成员 } +// Config 配置 type Config struct { Robot RobotConfig `toml:"robot"` Rules []RuleConfig `toml:"rules"` @@ -179,6 +195,7 @@ type Config struct { NotifyUpdate NotifyUpdateConfig `toml:"notify_update"` } +// LoadConfig 读取配置 func LoadConfig(configPath string) Config { // 读取配置 var config Config @@ -192,6 +209,7 @@ func LoadConfig(configPath string) Config { return config } +// Init 初始化配置中的一些字段 func (c *Config) Init() { for idx := range c.Rules { rule := &c.Rules[idx] @@ -204,8 +222,8 @@ func (c *Config) Init() { } rule.GroupIds = map[int64]struct{}{} - for _, groupId := range rule.RawGroupIds { - rule.GroupIds[groupId] = struct{}{} + for _, groupID := range rule.RawGroupIds { + rule.GroupIds[groupID] = struct{}{} } for _, groupType := range rule.GroupTypes { for _, groupTypeCfg := range c.GroupTypeConfigs { @@ -213,8 +231,8 @@ func (c *Config) Init() { continue } - for _, groupId := range groupTypeCfg.GroupIds { - rule.GroupIds[groupId] = struct{}{} + for _, groupID := range groupTypeCfg.GroupIds { + rule.GroupIds[groupID] = struct{}{} } } } @@ -243,11 +261,11 @@ func (c *Config) mergeGroupTypesIntoGroups(groups []int64, groupTypes []string) continue } - for _, groupId := range groupTypeCfg.GroupIds { - if InRangeInt64(groupId, merged) { + for _, groupID := range groupTypeCfg.GroupIds { + if InRangeInt64(groupID, merged) { continue } - merged = append(merged, groupId) + merged = append(merged, groupID) } } } @@ -255,6 +273,7 @@ func (c *Config) mergeGroupTypesIntoGroups(groups []int64, groupTypes []string) return merged } +// InRangeInt64 是否在列表内 func InRangeInt64(target int64, list []int64) bool { for _, value := range list { if value == target { @@ -268,7 +287,7 @@ func InRangeInt64(target int64, list []int64) bool { func (c *Config) check() error { for _, rule := range c.Rules { if rule.Type == "" { - return fmt.Errorf("rule=%v type=%v type not set", rule.Name, rule.Type) + return errors.Errorf("rule=%v type=%v type not set", rule.Name, rule.Type) } exists := false for _, ruleType := range c.RuleTypeConfigs { @@ -278,7 +297,7 @@ func (c *Config) check() error { } } if !exists { - return fmt.Errorf("rule=%v type=%v not valid", rule.Name, rule.Type) + return errors.Errorf("rule=%v type=%v not valid", rule.Name, rule.Type) } } diff --git a/qq_robot/default_config.toml b/qqrobot/default_config.toml similarity index 100% rename from qq_robot/default_config.toml rename to qqrobot/default_config.toml diff --git a/qqrobot/doc.go b/qqrobot/doc.go new file mode 100644 index 000000000..90122ebe7 --- /dev/null +++ b/qqrobot/doc.go @@ -0,0 +1,2 @@ +// Package qqrobot 包含基于MiraiGo和go-cqhttp的本地机器人处理逻辑 +package qqrobot diff --git a/qq_robot/food.go b/qqrobot/food.go similarity index 69% rename from qq_robot/food.go rename to qqrobot/food.go index e8c99aa27..ccda0cb02 100644 --- a/qq_robot/food.go +++ b/qqrobot/food.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import ( "encoding/json" @@ -10,23 +10,27 @@ import ( "strings" "github.com/Mrs4s/MiraiGo/message" + "github.com/pkg/errors" ) // 2021/10/02 5:25 by fzls -var ErrNoNewFood = fmt.Errorf("tryFetchMoreFoodImages failed, cannot get new food image that has never been sent") +var errNoNewFood = errors.Errorf("tryFetchMoreFoodImages failed, cannot get new food image that has never been sent") +// FoodImageRegex 食物图片正则 var FoodImageRegex = regexp.MustCompile(` MAX_MUTE_TIME { - return MAX_MUTE_TIME + if muteTime > MaxMuteTime { + return MaxMuteTime } return muteTime diff --git a/qq_robot/qq_robot.exe.manifest b/qqrobot/qq_robot.exe.manifest similarity index 100% rename from qq_robot/qq_robot.exe.manifest rename to qqrobot/qq_robot.exe.manifest diff --git a/qq_robot/qq_robot.go b/qqrobot/qq_robot.go similarity index 76% rename from qq_robot/qq_robot.go rename to qqrobot/qq_robot.go index 060f5c200..be9fa0e33 100644 --- a/qq_robot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import ( "context" @@ -14,26 +14,30 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" - "github.com/Mrs4s/go-cqhttp/coolq" "github.com/gookit/color" lru "github.com/hashicorp/golang-lru" + "github.com/pkg/errors" tbp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp/v20190627" + + "github.com/Mrs4s/go-cqhttp/coolq" ) +// MessageKey 消息key type MessageKey struct { - MessageId int64 + MessageID int64 SenderQQ int64 - GroupId int64 + GroupID int64 } -func makeMessageKey(messageId, senderQQ, groupId int64) MessageKey { +func makeMessageKey(messageID, senderQQ, groupID int64) MessageKey { return MessageKey{ - MessageId: messageId, + MessageID: messageID, SenderQQ: senderQQ, - GroupId: groupId, + GroupID: groupID, } } +// QQRobot qq机器人 type QQRobot struct { cqBot *coolq.CQBot @@ -41,11 +45,11 @@ type QQRobot struct { StartTime time.Time Rules []*Rule - RuleTypeToMessageIdToRuleApplyCount map[RuleType]map[MessageKey]int32 // 消息类型 => 消息Key => 该消息被当前类型规则处理的次数 + RuleTypeToMessageIDToRuleApplyCount map[RuleType]map[MessageKey]int32 // 消息类型 => 消息Key => 该消息被当前类型规则处理的次数 GroupToMemberToTriggerRuleTimes map[int64]map[int64][]int64 // group => qq => list of 触发规则的时间戳 GroupToRuleNameToLastSuccessTriggerTimestamp map[int64]map[string]int64 // group => rulename => 上次在cd外成功触发的时间戳 - HttpClient http.Client + httpClient http.Client aiClient *tbp.Client ocrCache *lru.ARCCache @@ -56,6 +60,7 @@ type QQRobot struct { quitFunc context.CancelFunc } +// NewQQRobot 创建qq机器人 func NewQQRobot(cqRobot *coolq.CQBot, configPath string) *QQRobot { config := LoadConfig(configPath) @@ -63,10 +68,10 @@ func NewQQRobot(cqRobot *coolq.CQBot, configPath string) *QQRobot { cqBot: cqRobot, Config: config, - RuleTypeToMessageIdToRuleApplyCount: map[RuleType]map[MessageKey]int32{}, + RuleTypeToMessageIDToRuleApplyCount: map[RuleType]map[MessageKey]int32{}, GroupToMemberToTriggerRuleTimes: map[int64]map[int64][]int64{}, GroupToRuleNameToLastSuccessTriggerTimestamp: map[int64]map[string]int64{}, - HttpClient: http.Client{Timeout: time.Duration(config.Robot.Timeout) * time.Second}, + httpClient: http.Client{Timeout: time.Duration(config.Robot.Timeout) * time.Second}, CheckUpdateVersionMap: map[string]string{}, } @@ -78,6 +83,8 @@ func NewQQRobot(cqRobot *coolq.CQBot, configPath string) *QQRobot { } return r } + +// Start 开始运行 func (r *QQRobot) Start() { r.StartTime = time.Now() r.quitCtx, r.quitFunc = context.WithCancel(context.Background()) @@ -86,6 +93,7 @@ func (r *QQRobot) Start() { go r.ticker() } +// Stop 停止运行 func (r *QQRobot) Stop() { r.notify(r.Config.Robot.OnStop) r.quitFunc() @@ -94,7 +102,7 @@ func (r *QQRobot) Stop() { func (r *QQRobot) notify(cfg NotifyConfig) { msgTemplate := cfg.Message if cfg.Name == "机器人下线" { - msgTemplate = strings.ReplaceAll(msgTemplate, TemplateArgs_WorkTime, time.Since(r.StartTime).String()) + msgTemplate = strings.ReplaceAll(msgTemplate, templateargsWorktime, time.Since(r.StartTime).String()) } if r.Config.Robot.Debug { logger.Debug("debug mode, do not notify", cfg.Name, cfg.NotifyGroups, msgTemplate) @@ -103,17 +111,17 @@ func (r *QQRobot) notify(cfg NotifyConfig) { msg := message.NewSendingMessage() msg.Append(message.NewText(msgTemplate)) nowStr := r.currentTime() - for _, groupId := range cfg.NotifyGroups { - if r.cqBot.Client.FindGroup(groupId) == nil { + for _, groupID := range cfg.NotifyGroups { + if r.cqBot.Client.FindGroup(groupID) == nil { // 不在该群里,跳过 continue } - retCode := r.cqBot.SendGroupMessage(groupId, msg) + retCode := r.cqBot.SendGroupMessage(groupID, msg) if retCode == -1 { - logger.Errorf("【%v Failed】 %v groupId=%v message=%v err=%v", cfg.Name, nowStr, groupId, msg, retCode) + logger.Errorf("【%v Failed】 %v groupID=%v message=%v err=%v", cfg.Name, nowStr, groupID, msg, retCode) return } - logger.Infof("【%v】 %v groupId=%v message=%v", cfg.Name, nowStr, groupId, msg) + logger.Infof("【%v】 %v groupID=%v message=%v", cfg.Name, nowStr, groupID, msg) } logger.Infof("robot on %v finished", cfg.Name) } @@ -139,35 +147,37 @@ func (r *QQRobot) ticker() { } } +// RegisterHandlers 注册事件处理函数 func (r *QQRobot) RegisterHandlers() { // TODO: re: 添加其他事件的处理 @2021-10-02 05:35:37 r.cqBot.Client.OnGroupMessage(r.OnGroupMessage) r.cqBot.Client.OnPrivateMessage(r.OnPrivateMessage) - //r.cqBot.Client.OnSelfPrivateMessage(rprivateMessageEvent) - //r.cqBot.Client.OnSelfGroupMessage(rgroupMessageEvent) + // r.cqBot.Client.OnSelfPrivateMessage(rprivateMessageEvent) + // r.cqBot.Client.OnSelfGroupMessage(rgroupMessageEvent) r.cqBot.Client.OnTempMessage(r.OnTempMessage) - //r.cqBot.Client.OnGroupMuted(rgroupMutedEvent) - //r.cqBot.Client.OnGroupMessageRecalled(rgroupRecallEvent) - //r.cqBot.Client.OnGroupNotify(rgroupNotifyEvent) - //r.cqBot.Client.OnFriendNotify(rfriendNotifyEvent) - //r.cqBot.Client.OnMemberSpecialTitleUpdated(rmemberTitleUpdatedEvent) - //r.cqBot.Client.OnFriendMessageRecalled(rfriendRecallEvent) - //r.cqBot.Client.OnReceivedOfflineFile(rofflineFileEvent) - //r.cqBot.Client.OnJoinGroup(rjoinGroupEvent) - //r.cqBot.Client.OnLeaveGroup(rleaveGroupEvent) + // r.cqBot.Client.OnGroupMuted(rgroupMutedEvent) + // r.cqBot.Client.OnGroupMessageRecalled(rgroupRecallEvent) + // r.cqBot.Client.OnGroupNotify(rgroupNotifyEvent) + // r.cqBot.Client.OnFriendNotify(rfriendNotifyEvent) + // r.cqBot.Client.OnMemberSpecialTitleUpdated(rmemberTitleUpdatedEvent) + // r.cqBot.Client.OnFriendMessageRecalled(rfriendRecallEvent) + // r.cqBot.Client.OnReceivedOfflineFile(rofflineFileEvent) + // r.cqBot.Client.OnJoinGroup(rjoinGroupEvent) + // r.cqBot.Client.OnLeaveGroup(rleaveGroupEvent) r.cqBot.Client.OnGroupMemberJoined(r.OnGroupMemberJoined) - //r.cqBot.Client.OnGroupMemberLeaved(rmemberLeaveEvent) - //r.cqBot.Client.OnGroupMemberPermissionChanged(rmemberPermissionChangedEvent) - //r.cqBot.Client.OnGroupMemberCardUpdated(rmemberCardUpdatedEvent) - //r.cqBot.Client.OnNewFriendRequest(rfriendRequestEvent) - //r.cqBot.Client.OnNewFriendAdded(rfriendAddedEvent) - //r.cqBot.Client.OnGroupInvited(rgroupInvitedEvent) - //r.cqBot.Client.OnUserWantJoinGroup(rgroupJoinReqEvent) - //r.cqBot.Client.OnOtherClientStatusChanged(rotherClientStatusChangedEvent) - //r.cqBot.Client.OnGroupDigest(rgroupEssenceMsg) + // r.cqBot.Client.OnGroupMemberLeaved(rmemberLeaveEvent) + // r.cqBot.Client.OnGroupMemberPermissionChanged(rmemberPermissionChangedEvent) + // r.cqBot.Client.OnGroupMemberCardUpdated(rmemberCardUpdatedEvent) + // r.cqBot.Client.OnNewFriendRequest(rfriendRequestEvent) + // r.cqBot.Client.OnNewFriendAdded(rfriendAddedEvent) + // r.cqBot.Client.OnGroupInvited(rgroupInvitedEvent) + // r.cqBot.Client.OnUserWantJoinGroup(rgroupJoinReqEvent) + // r.cqBot.Client.OnOtherClientStatusChanged(rotherClientStatusChangedEvent) + // r.cqBot.Client.OnGroupDigest(rgroupEssenceMsg) } +// OnGroupMessage 处理群消息 func (r *QQRobot) OnGroupMessage(client *client.QQClient, m *message.GroupMessage) { for _, rule := range r.Rules { if err := r.applyGroupRule(m, rule); err != nil { @@ -176,6 +186,7 @@ func (r *QQRobot) OnGroupMessage(client *client.QQClient, m *message.GroupMessag } } +// OnGroupMemberJoined 处理加群 func (r *QQRobot) OnGroupMemberJoined(client *client.QQClient, m *client.MemberJoinGroupEvent) { for _, rule := range r.Rules { if err := r.onMemberJoin(m, rule); err != nil { @@ -184,10 +195,12 @@ func (r *QQRobot) OnGroupMemberJoined(client *client.QQClient, m *client.MemberJ } } +// OnPrivateMessage 处理私聊 func (r *QQRobot) OnPrivateMessage(client *client.QQClient, m *message.PrivateMessage) { r.onPrivateOrTempMessage(m.Sender.Uin, 0, 0, m) } +// OnTempMessage 处理临时消息 func (r *QQRobot) OnTempMessage(client *client.QQClient, m *client.TempMessageEvent) { r.onPrivateOrTempMessage(0, m.Message.GroupCode, m.Message.Sender.Uin, m) } @@ -197,17 +210,17 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { nowStr := r.currentTime() nowUnix := time.Now().Unix() - groupId := m.GroupCode + groupID := m.GroupCode senderUin := m.Sender.Uin senderName := m.Sender.Nickname - senderInfo, err := r.cqBot.Client.GetMemberInfo(groupId, senderUin) + senderInfo, err := r.cqBot.Client.GetMemberInfo(groupID, senderUin) isAdmin := false if err == nil { isAdmin = isMemberAdmin(senderInfo.Permission) } - if _, ok := config.GroupIds[groupId]; !ok { + if _, ok := config.GroupIds[groupID]; !ok { return nil } @@ -257,11 +270,11 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } } } - if config.TriggerRuleCount != 0 && config.TriggerRuleDuration != 0 && r.GroupToMemberToTriggerRuleTimes[groupId] != nil { + if config.TriggerRuleCount != 0 && config.TriggerRuleDuration != 0 && r.GroupToMemberToTriggerRuleTimes[groupID] != nil { // 计算是否这个QQ在滥用机器人功能 var triggerCount int64 checkStartTime := time.Now().Unix() - config.TriggerRuleDuration - triggerTimes := r.GroupToMemberToTriggerRuleTimes[groupId][senderUin] + triggerTimes := r.GroupToMemberToTriggerRuleTimes[groupID][senderUin] startIdx := sort.Search(len(triggerTimes), func(i int) bool { return triggerTimes[i] >= checkStartTime }) @@ -334,8 +347,8 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } // 判断是否是排除的用户列表 - for _, exclude_qq := range config.ExcludeQQs { - if exclude_qq == senderUin { + for _, excludeQQ := range config.ExcludeQQs { + if excludeQQ == senderUin { logger.Info("【ExcludedQQ】", nowStr, config.Name, p(m)) return nil } @@ -351,10 +364,10 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } } - messageApplyCount := r.RuleTypeToMessageIdToRuleApplyCount[config.Type] + messageApplyCount := r.RuleTypeToMessageIDToRuleApplyCount[config.Type] if messageApplyCount == nil { messageApplyCount = map[MessageKey]int32{} - r.RuleTypeToMessageIdToRuleApplyCount[config.Type] = messageApplyCount + r.RuleTypeToMessageIDToRuleApplyCount[config.Type] = messageApplyCount } ruleTypeConfig := RuleTypeConfig{} for _, cfg := range r.Config.RuleTypeConfigs { @@ -363,38 +376,38 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { break } } - messageKey := makeMessageKey(source, senderUin, groupId) - if ruleTypeConfig.MaxApplyCount != RuleTypeMaxApplyCount_Infinite && messageApplyCount[messageKey] >= ruleTypeConfig.MaxApplyCount { + messageKey := makeMessageKey(source, senderUin, groupID) + if ruleTypeConfig.MaxApplyCount != RuleTypeMaxApplyCountInfinite && messageApplyCount[messageKey] >= ruleTypeConfig.MaxApplyCount { return nil } guideContent := config.GuideContent // 判断是否在cd内触发了规则 - if config.CD != 0 && r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId] != nil { - lastTriggerTime := r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId][config.Name] + if config.CD != 0 && r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupID] != nil { + lastTriggerTime := r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupID][config.Name] if nowUnix < lastTriggerTime+config.CD { if len(config.GuideContentInCD) == 0 { // 未设置cd内回复内容,则视为未触发 logger.Info("【InCD】", nowStr, config.Name, p(m)) return nil - } else { - // 替换回复内容为cd回复内容 - guideContent = strings.ReplaceAll(config.GuideContentInCD, TemplateArgs_CD, strconv.FormatInt(config.CD, 10)) } + + // 替换回复内容为cd回复内容 + guideContent = strings.ReplaceAll(config.GuideContentInCD, templateargsCd, strconv.FormatInt(config.CD, 10)) } } // 记录这个QQ触发规则的时间戳 - if r.GroupToMemberToTriggerRuleTimes[groupId] == nil { - r.GroupToMemberToTriggerRuleTimes[groupId] = map[int64][]int64{} + if r.GroupToMemberToTriggerRuleTimes[groupID] == nil { + r.GroupToMemberToTriggerRuleTimes[groupID] = map[int64][]int64{} } - r.GroupToMemberToTriggerRuleTimes[groupId][senderUin] = append(r.GroupToMemberToTriggerRuleTimes[groupId][senderUin], time.Now().Unix()) + r.GroupToMemberToTriggerRuleTimes[groupID][senderUin] = append(r.GroupToMemberToTriggerRuleTimes[groupID][senderUin], time.Now().Unix()) // 记录这个规则触发的时间戳 - if r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId] == nil { - r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId] = map[string]int64{} + if r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupID] == nil { + r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupID] = map[string]int64{} } - r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupId][config.Name] = nowUnix + r.GroupToRuleNameToLastSuccessTriggerTimestamp[groupID][config.Name] = nowUnix // ok @@ -423,17 +436,17 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } switch config.Action { - case ActionType_Guide: - guideContent = strings.ReplaceAll(guideContent, TemplateArgs_MuteTime, strconv.FormatInt(muteTime, 10)) + case actionTypeGuide: + guideContent = strings.ReplaceAll(guideContent, templateargsMutetime, strconv.FormatInt(muteTime, 10)) if config.GitChangelogPage != "" { latestVersion, updateMessage := r.getLatestGitVersion(config.GitChangelogPage) - guideContent = strings.ReplaceAll(guideContent, TemplateArgs_GitVersion, latestVersion) - guideContent = strings.ReplaceAll(guideContent, TemplateArgs_UpdateMessage, updateMessage) + guideContent = strings.ReplaceAll(guideContent, templateargsGitversion, latestVersion) + guideContent = strings.ReplaceAll(guideContent, templateargsUpdatemessage, updateMessage) } if len(guideContent) != 0 { replies.Append(message.NewText(guideContent)) } - case ActionType_Command: + case actionTypeCommand: for _, msg := range m.Elements { msgVal, ok := msg.(*message.TextElement) if !ok { @@ -443,7 +456,7 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { for _, keywordRegex := range config.KeywordRegexes { if keywordRegex.MatchString(msgVal.Content) { commandStr := keywordRegex.ReplaceAllString(msgVal.Content, "") - err, msg, extraReplies := r.processCommand(commandStr, m) + msg, extraReplies, err := r.processCommand(commandStr, m) if err != nil { replies.Append(message.NewText(guideContent)) replies.Elements = append(replies.Elements, extraReplies...) @@ -459,14 +472,14 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } } } - case ActionType_Food: + case actionTypeFood: extraReplies, err := r.createFoodMessage(rule) replies.Elements = append(replies.Elements, extraReplies.Elements...) if err != nil { logger.Errorf("createFoodMessage, rule=%v err=%v", config.Name, err) replies.Append(message.NewText(guideContent)) } - case ActionType_AiChat: + case actiontypeAichat: var chatText string for _, msg := range m.Elements { if msgVal, ok := msg.(*message.TextElement); ok { @@ -481,10 +494,10 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { if reply != "" { replies.Append(message.NewText(reply)) } - case ActionType_SendUpdateMessage: + case actiontypeSendupdatemessage: if isAdmin { // 手动触发更新通知 - if res := r.manualTriggerUpdateMessage(groupId); res != nil { + if res := r.manualTriggerUpdateMessage(groupID); res != nil { replies.Elements = append(replies.Elements, res.Elements...) } else { replies.Append(message.NewText("当前群组没有配置检查更新哦~")) @@ -492,7 +505,7 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } else { replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) } - case ActionType_Repeater: + case actiontypeRepeater: if isAdmin { // 复读内容到指定的群组 // 移除首行(首行设定为关键词) @@ -515,12 +528,12 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } for _, repeatToGroup := range config.RepeatToGroups { - forwardRspId := r.cqBot.SendGroupMessage(repeatToGroup, repeatMessages) - if forwardRspId == -1 { - logger.Error(fmt.Sprintf("【RepeatToGroup(%v) Failed】", repeatToGroup), nowStr, config.Name, repeatMessages, forwardRspId) + forwardRspID := r.cqBot.SendGroupMessage(repeatToGroup, repeatMessages) + if forwardRspID == -1 { + logger.Error(fmt.Sprintf("【RepeatToGroup(%v) Failed】", repeatToGroup), nowStr, config.Name, repeatMessages, forwardRspID) continue } - logger.Info(fmt.Sprintf("【RepeatToGroup(%v)】", repeatToGroup), nowStr, config.Name, repeatMessages, forwardRspId) + logger.Info(fmt.Sprintf("【RepeatToGroup(%v)】", repeatToGroup), nowStr, config.Name, repeatMessages, forwardRspID) } } } else { @@ -536,18 +549,18 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } // 如配置了图片url,则额外发送图片 - imageUrl := config.ImageUrl + imageURL := config.ImageURL if len(config.RandomImageUrls) != 0 { randIdx := rand.Intn(len(config.RandomImageUrls)) - imageUrl = config.RandomImageUrls[randIdx] + imageURL = config.RandomImageUrls[randIdx] } - if imageUrl != "" { - r.tryAppendImageByUrl(replies, imageUrl) + if imageURL != "" { + r.tryAppendImageByURL(replies, imageURL) } if maybeKilledWrongPerson { replies.Append(message.NewText("似乎前面有人代替你被误杀了。但是,正义的铁拳虽然会乱锤,却不会错过正确的人。宁可错杀三千,不可放过一人!(手动眼部红光特效)")) - r.tryAppendImageByUrl(replies, "https://s3.ax1x.com/2021/03/17/66NRi9.gif") + r.tryAppendImageByURL(replies, "https://s3.ax1x.com/2021/03/17/66NRi9.gif") } if len(replies.Elements) != 0 { @@ -556,16 +569,16 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { // 补充reply信息 replies.Elements = append([]message.IMessageElement{message.NewReply(m)}, replies.Elements...) - rspId := r.cqBot.SendGroupMessage(groupId, replies) - if rspId == -1 { - logger.Error("【ReplyFail】", nowStr, config.Name, keyWord, p(m), rspId) + rspID := r.cqBot.SendGroupMessage(groupID, replies) + if rspID == -1 { + logger.Error("【ReplyFail】", nowStr, config.Name, keyWord, p(m), rspID) return err } - logger.Info(color.Style{color.Bold, color.Green}.Renderln("【OK】", nowStr, config.Name, keyWord, p(m), source, replies, rspId)) + logger.Info(color.Style{color.Bold, color.Green}.Renderln("【OK】", nowStr, config.Name, keyWord, p(m), source, replies, rspID)) } if config.RevokeMessage { - err := r.cqBot.Client.RecallGroupMessage(groupId, m.Id, m.InternalId) + err := r.cqBot.Client.RecallGroupMessage(groupID, m.Id, m.InternalId) if err != nil { logger.Error("【RevokeMessage Fail】", nowStr, config.Name, p(m), err) } else { @@ -589,20 +602,20 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { if needForward := len(config.ForwardToQQs) != 0 || len(config.ForwardToGroups) != 0; needForward { for _, forwardMessages := range r.getForwardMessagesList(m, false) { for _, forwardToQQ := range config.ForwardToQQs { - forwardRspId := r.cqBot.SendPrivateMessage(forwardToQQ, 0, forwardMessages) - if forwardRspId == -1 { - logger.Error(fmt.Sprintf("【ForwardToQQ(%v) Failed】", forwardToQQ), nowStr, config.Name, forwardMessages, forwardRspId) + forwardRspID := r.cqBot.SendPrivateMessage(forwardToQQ, 0, forwardMessages) + if forwardRspID == -1 { + logger.Error(fmt.Sprintf("【ForwardToQQ(%v) Failed】", forwardToQQ), nowStr, config.Name, forwardMessages, forwardRspID) continue } - logger.Info(fmt.Sprintf("【ForwardToQQ(%v)】", forwardToQQ), nowStr, config.Name, forwardMessages, forwardRspId) + logger.Info(fmt.Sprintf("【ForwardToQQ(%v)】", forwardToQQ), nowStr, config.Name, forwardMessages, forwardRspID) } for _, forwardToGroup := range config.ForwardToGroups { - forwardRspId := r.cqBot.SendGroupMessage(forwardToGroup, forwardMessages) - if forwardRspId == -1 { - logger.Error(fmt.Sprintf("【ForwardToGroup(%v) Failed】", forwardToGroup), nowStr, config.Name, forwardMessages, forwardRspId) + forwardRspID := r.cqBot.SendGroupMessage(forwardToGroup, forwardMessages) + if forwardRspID == -1 { + logger.Error(fmt.Sprintf("【ForwardToGroup(%v) Failed】", forwardToGroup), nowStr, config.Name, forwardMessages, forwardRspID) continue } - logger.Info(fmt.Sprintf("【ForwardToGroup(%v)】", forwardToGroup), nowStr, config.Name, forwardMessages, forwardRspId) + logger.Info(fmt.Sprintf("【ForwardToGroup(%v)】", forwardToGroup), nowStr, config.Name, forwardMessages, forwardRspID) } } } @@ -628,7 +641,7 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool case *message.TextElement: forwardMessages.Elements = append(forwardMessages.Elements, splitPlainMessage(msgVal.Content)...) case *message.GroupImageElement: - r.tryAppendImageByUrl(forwardMessages, msgVal.Url) + r.tryAppendImageByURL(forwardMessages, msgVal.Url) case *message.AtElement: if msgVal.Target != 0 { forwardMessages.Append(message.NewText(fmt.Sprintf("@%v(%v)", msgVal.Display, msgVal.Target))) @@ -646,7 +659,7 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool jsonBytes, _ := json.Marshal(forwardMessages.Elements[len(forwardMessages.Elements)-1]) msgSize += len(jsonBytes) // 需要确保每次至少转发一条消息 - if len(forwardMessages.Elements) > 1 && msgSize > maxMessageJsonSize { + if len(forwardMessages.Elements) > 1 && msgSize > maxMessageJSONSize { forwardMessages.Elements = forwardMessages.Elements[:len(forwardMessages.Elements)-1] msgSize -= len(jsonBytes) leftMessages.Append(msg) @@ -675,10 +688,10 @@ func (r *QQRobot) onMemberJoin(m *client.MemberJoinGroupEvent, rule *Rule) error config := rule.Config nowStr := r.currentTime() - groupId := m.Group.Code + groupID := m.Group.Code newMemberUin := m.Member.Uin - if _, ok := config.GroupIds[groupId]; !ok { + if _, ok := config.GroupIds[groupID]; !ok { return nil } @@ -699,17 +712,17 @@ func (r *QQRobot) onMemberJoin(m *client.MemberJoinGroupEvent, rule *Rule) error replies.Append(message.NewText(config.GuideContent)) // 如配置了图片url,则额外发送图片 - if config.ImageUrl != "" { - r.tryAppendImageByUrl(replies, config.ImageUrl) + if config.ImageURL != "" { + r.tryAppendImageByURL(replies, config.ImageURL) } if len(replies.Elements) != 0 { - rspId := r.cqBot.SendGroupMessage(groupId, replies) - if rspId == -1 { - logger.Error("【ReplyFail】", nowStr, p(m), rspId) - return fmt.Errorf("reply fail, rspId=%v", rspId) + rspID := r.cqBot.SendGroupMessage(groupID, replies) + if rspID == -1 { + logger.Error("【ReplyFail】", nowStr, p(m), rspID) + return errors.Errorf("reply fail, rspID=%v", rspID) } - logger.Info("【OK】", nowStr, p(m.Group), 0, (replies), rspId) + logger.Info("【OK】", nowStr, p(m.Group), 0, (replies), rspID) } if muteTime := config.MuteTime; muteTime != 0 { @@ -724,7 +737,7 @@ func (r *QQRobot) onMemberJoin(m *client.MemberJoinGroupEvent, rule *Rule) error return nil } -func (r *QQRobot) onPrivateOrTempMessage(senderFriendUin int64, tempGroupId int64, tempUin int64, m interface{}) { +func (r *QQRobot) onPrivateOrTempMessage(senderFriendUin int64, tempGroupID int64, tempUin int64, m interface{}) { // 回复消息 replies := message.NewSendingMessage() @@ -735,23 +748,23 @@ func (r *QQRobot) onPrivateOrTempMessage(senderFriendUin int64, tempGroupId int6 } if cfg.PersonalMessageNotSupportedImage != "" { - r.tryAppendImageByUrl(replies, cfg.PersonalMessageNotSupportedImage) + r.tryAppendImageByURL(replies, cfg.PersonalMessageNotSupportedImage) } if len(replies.Elements) == 0 { return } - var rspId int32 + var rspID int32 if senderFriendUin != 0 { - rspId = r.cqBot.SendPrivateMessage(senderFriendUin, 0, replies) + rspID = r.cqBot.SendPrivateMessage(senderFriendUin, 0, replies) } else { - rspId = r.cqBot.SendPrivateMessage(tempUin, tempGroupId, replies) + rspID = r.cqBot.SendPrivateMessage(tempUin, tempGroupID, replies) } - if rspId == -1 { - logger.Error("【ReplyFail】", p(m), rspId) + if rspID == -1 { + logger.Error("【ReplyFail】", p(m), rspID) return } - logger.Info("【OK】", p(m), 0, replies, rspId) + logger.Info("【OK】", p(m), 0, replies, rspID) } diff --git a/qq_robot/qq_robot_test.go b/qqrobot/qq_robot_test.go similarity index 94% rename from qq_robot/qq_robot_test.go rename to qqrobot/qq_robot_test.go index 48319a0ee..0f2820e7c 100644 --- a/qq_robot/qq_robot_test.go +++ b/qqrobot/qq_robot_test.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import ( "fmt" @@ -67,7 +67,7 @@ func Test_version_to_version_int_list(t *testing.T) { } for _, tt := range tests { t.Run(tt.version, func(t *testing.T) { - if got := version_to_version_int_list(tt.version); !reflect.DeepEqual(got, tt.want) { + if got := versionToVersionIntList(tt.version); !reflect.DeepEqual(got, tt.want) { t.Errorf("version_to_version_int_list(%v) = %v, want %v", tt.version, got, tt.want) } }) @@ -90,7 +90,7 @@ func Test_version_less(t *testing.T) { } for _, tt := range tests { t.Run(fmt.Sprintf("%v-%v", tt.version_left, tt.version_right), func(t *testing.T) { - if got := version_less(tt.version_left, tt.version_right); got != tt.want { + if got := versionLess(tt.version_left, tt.version_right); got != tt.want { t.Errorf("version_less(%v, %v) = %v, want %v", tt.version_left, tt.version_right, got, tt.want) } }) diff --git a/qq_robot/rule.go b/qqrobot/rule.go similarity index 61% rename from qq_robot/rule.go rename to qqrobot/rule.go index a808ca8b0..fa7adfa01 100644 --- a/qq_robot/rule.go +++ b/qqrobot/rule.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import ( "math/rand" @@ -7,16 +7,19 @@ import ( // 2020/06/01 2:42 by fzls -var MAX_FOOD_PAGE_MAP = map[string]int64{ +// MaxFoodPageMap 美食网站与其最大页数 +var MaxFoodPageMap = map[string]int64{ "www.xinshipu.com": 30, "home.meishichina.com": 40, } +// FoodImage 美食图片 type FoodImage struct { Name string - Url string + URL string } +// Rule 规则,附带一些缓存数据 type Rule struct { Config RuleConfig ProcessedMessages map[int64]struct{} @@ -26,34 +29,36 @@ type Rule struct { SentFoodImages map[string]struct{} // 目前已发送过的食物的图片url的集合 } -func (r *Rule) UpdateFoodPage(foodSiteUrl string) { - if r.SiteToFetchedPage[foodSiteUrl] == nil { - r.SiteToFetchedPage[foodSiteUrl] = map[int64]struct{}{} +// UpdateFoodPage 更新食物页数 +func (r *Rule) UpdateFoodPage(foodSiteURL string) { + if r.SiteToFetchedPage[foodSiteURL] == nil { + r.SiteToFetchedPage[foodSiteURL] = map[int64]struct{}{} } - fetchedPage := r.SiteToFetchedPage[foodSiteUrl] + fetchedPage := r.SiteToFetchedPage[foodSiteURL] - var MAX_FOOD_PAGE int64 = 30 - for site, maxFoodPage := range MAX_FOOD_PAGE_MAP { - if strings.Contains(foodSiteUrl, site) { - MAX_FOOD_PAGE = maxFoodPage + var MaxFoodPage int64 = 30 + for site, maxFoodPage := range MaxFoodPageMap { + if strings.Contains(foodSiteURL, site) { + MaxFoodPage = maxFoodPage } } - if len(fetchedPage) == int(MAX_FOOD_PAGE) { + if len(fetchedPage) == int(MaxFoodPage) { return } for { - foodPage := 1 + rand.Int63n(MAX_FOOD_PAGE) + foodPage := 1 + rand.Int63n(MaxFoodPage) if _, fetched := fetchedPage[foodPage]; !fetched { fetchedPage[foodPage] = struct{}{} - r.SiteToFoodPage[foodSiteUrl] = foodPage + r.SiteToFoodPage[foodSiteURL] = foodPage logger.Infof("rule=%v UpdateFoodPage to %v", r.Config.Name, foodPage) return } } } +// NewRule 创建新的规则 func NewRule(config RuleConfig) *Rule { return &Rule{ Config: config, diff --git a/qq_robot/util.go b/qqrobot/util.go similarity index 77% rename from qq_robot/util.go rename to qqrobot/util.go index acb94acbe..01c7ec73b 100644 --- a/qq_robot/util.go +++ b/qqrobot/util.go @@ -1,4 +1,4 @@ -package qq_robot +package qqrobot import ( "crypto/md5" @@ -12,17 +12,18 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" + log "github.com/sirupsen/logrus" + "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - log "github.com/sirupsen/logrus" ) const ( maxImageSize = 1024 * 1024 * 30 // 30MB ) -func (r *QQRobot) tryAppendImageByUrl(m *message.SendingMessage, imageUrl string) { - image, err := r._makeLocalImage(imageUrl) +func (r *QQRobot) tryAppendImageByURL(m *message.SendingMessage, imageURL string) { + image, err := r._makeLocalImage(imageURL) if err != nil { log.Errorf("_makeLocalImage err=%v", err) return @@ -32,8 +33,8 @@ func (r *QQRobot) tryAppendImageByUrl(m *message.SendingMessage, imageUrl string } // modified based on makeImageOrVideoElem -func (r *QQRobot) _makeLocalImage(imageUrl string) (message.IMessageElement, error) { - hash := md5.Sum([]byte(imageUrl)) +func (r *QQRobot) _makeLocalImage(imageURL string) (message.IMessageElement, error) { + hash := md5.Sum([]byte(imageURL)) cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash[:])+".cache") maxSize := func() int64 { return maxImageSize @@ -42,7 +43,7 @@ func (r *QQRobot) _makeLocalImage(imageUrl string) (message.IMessageElement, err if exist { goto hasCacheFile } - if err := global.DownloadFileMultiThreading(imageUrl, cacheFile, maxSize, runtime.NumCPU(), nil); err != nil { + if err := global.DownloadFileMultiThreading(imageURL, cacheFile, maxSize, runtime.NumCPU(), nil); err != nil { return nil, err } hasCacheFile: @@ -50,17 +51,17 @@ hasCacheFile: } func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) (ocrResultString string) { - image_md5 := fmt.Sprintf("%x", groupImageElement.Md5) + imageMd5 := fmt.Sprintf("%x", groupImageElement.Md5) - cached, ok := r.ocrCache.Get(image_md5) + cached, ok := r.ocrCache.Get(imageMd5) if ok { return cached.(string) } defer func() { - r.ocrCache.Add(image_md5, ocrResultString) + r.ocrCache.Add(imageMd5, ocrResultString) }() - + ocrResult, err := r.cqBot.Client.ImageOcr(groupImageElement) if err != nil { logger.Errorf("ocr出错了,image=%+v,err=%v", groupImageElement, err) @@ -84,15 +85,16 @@ func (r *QQRobot) formatTime(t time.Time) string { func getCurrentPeriodName() string { hour := time.Now().Hour() - if hour == 23 || 0 <= hour && hour < 5 { + switch { + case hour == 23 || 0 <= hour && hour < 5: return "深夜" - } else if 5 <= hour && hour < 11 { + case 5 <= hour && hour < 11: return "早上" - } else if 11 <= hour && hour < 13 { + case 11 <= hour && hour < 13: return "中午" - } else if 13 <= hour && hour < 17 { + case 13 <= hour && hour < 17: return "下午" - } else { + default: return "晚上" } } @@ -103,10 +105,10 @@ func isMemberAdmin(permission client.MemberPermission) bool { } // 单条消息发送的大小有限制,所以需要分成多段来发 -const maxMessageJsonSize = 400 +const maxMessageJSONSize = 400 func splitPlainMessage(content string) []message.IMessageElement { - if len(content) <= maxMessageJsonSize { + if len(content) <= maxMessageJSONSize { return []message.IMessageElement{message.NewText(content)} } @@ -117,7 +119,7 @@ func splitPlainMessage(content string) []message.IMessageElement { for len(remainingText) != 0 { partSize := 0 for byteIdx, runeValue := range remainingText { - if partSize+byteIdx > maxMessageJsonSize { + if partSize+byteIdx > maxMessageJSONSize { break } partSize += len(string(runeValue)) diff --git a/versioninfo.json b/versioninfo.json index 9c734555c..6a8f13dc4 100644 --- a/versioninfo.json +++ b/versioninfo.json @@ -38,6 +38,6 @@ "CharsetID": "04B0" } }, - "IconPath": "qq_robot/icon.ico", - "ManifestPath": "qq_robot/qq_robot.exe.manifest" + "IconPath": "qqrobot/icon.ico", + "ManifestPath": "qqrobot/qq_robot.exe.manifest" } From f75aaec8966017e89f0e9dd4d05d16d327b8a5fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 17:54:14 +0800 Subject: [PATCH 033/612] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot_test.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/qqrobot/qq_robot_test.go b/qqrobot/qq_robot_test.go index 0f2820e7c..3600fcca1 100644 --- a/qqrobot/qq_robot_test.go +++ b/qqrobot/qq_robot_test.go @@ -2,7 +2,6 @@ package qqrobot import ( "fmt" - "net/url" "reflect" "testing" @@ -96,18 +95,3 @@ func Test_version_less(t *testing.T) { }) } } - -func TestMakeSign(t *testing.T) { - params := url.Values{} - params.Set("app_id", "2159831370") - params.Set("time_stamp", "1605690895") - params.Set("nonce_str", "0.21999282737760117") - params.Set("session", "123456789") - params.Set("question", "月球是什么?") - appKey := "cHQ286kZhNKlpwbj" - - want := "562D5D6303E61FB9EE590CB20AC29218" - if got := MakeSign(params, appKey); got != want { - t.Errorf("MakeSign() = %v, want %v", got, want) - } -} From 5c4ad18c5a6ce57e602381bf6e64fdcb5fdf6669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 17:58:55 +0800 Subject: [PATCH 034/612] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 5 ++++- qqrobot/qq_robot_test.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index be9fa0e33..b15c23de0 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -62,7 +62,10 @@ type QQRobot struct { // NewQQRobot 创建qq机器人 func NewQQRobot(cqRobot *coolq.CQBot, configPath string) *QQRobot { - config := LoadConfig(configPath) + var config Config + if configPath != "" { + config = LoadConfig(configPath) + } r := &QQRobot{ cqBot: cqRobot, diff --git a/qqrobot/qq_robot_test.go b/qqrobot/qq_robot_test.go index 3600fcca1..f876ad1b4 100644 --- a/qqrobot/qq_robot_test.go +++ b/qqrobot/qq_robot_test.go @@ -49,7 +49,7 @@ func Test_getLatestGitVersion(t *testing.T) { return } - qqRobot := NewQQRobot(nil, "../config.toml") + qqRobot := NewQQRobot(nil, "") version, updateMessage := qqRobot.getLatestGitVersion("https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD") t.Logf("version=%v, updateMessage如下:\n%v", version, updateMessage) } From 809b2db6d8c6b56fcd31e3abcb73499a10c99457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:03:05 +0800 Subject: [PATCH 035/612] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=89=BE=E4=B8=8D?= =?UTF-8?q?=E5=88=B0goversioninfo=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4524492a..809e15920 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,8 @@ jobs: export CGO_ENABLED=0 if [ $GOOS = "windows" ]; then go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest; go generate .; fi + export PATH=${PATH}:`go env GOPATH`/bin + go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . - name: Upload artifact From 4df4bff9ebfed62918e2b9244fd999793780c4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:05:03 +0800 Subject: [PATCH 036/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 809e15920..d6c8c622e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,7 @@ jobs: if [ $GOOS = "windows" ]; then go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest; go generate .; fi export PATH=${PATH}:`go env GOPATH`/bin + echo $PATH go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . From 948581b5394dd1570dd9cf39335159330f554f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:09:18 +0800 Subject: [PATCH 037/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6c8c622e..94a685060 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,9 +50,12 @@ jobs: export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 - if [ $GOOS = "windows" ]; then go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest; go generate .; fi - export PATH=${PATH}:`go env GOPATH`/bin - echo $PATH + if [ $GOOS = "windows" ]; then + go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest + export PATH=${PATH}:`go env GOPATH`/bin + echo $PATH + go generate . + fi go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . From dc4519fdfce0176f0047398e7939880641d6e3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:12:13 +0800 Subject: [PATCH 038/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94a685060..6b3033aec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,6 +54,7 @@ jobs: go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest export PATH=${PATH}:`go env GOPATH`/bin echo $PATH + ls -alh `go env GOPATH`/bin go generate . fi From 226302823311a634582e49052f38d4901d02c691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:20:12 +0800 Subject: [PATCH 039/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b3033aec..d4b800562 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,7 @@ jobs: export PATH=${PATH}:`go env GOPATH`/bin echo $PATH ls -alh `go env GOPATH`/bin + ls -alh `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} go generate . fi From f33ca265c7f85e56b99006c960c12df9a486f4de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:22:43 +0800 Subject: [PATCH 040/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4b800562..b1c05519c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,10 +52,7 @@ jobs: if [ $GOOS = "windows" ]; then go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest - export PATH=${PATH}:`go env GOPATH`/bin - echo $PATH - ls -alh `go env GOPATH`/bin - ls -alh `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} + export PATH=${PATH}:`go env GOPATH`/bin:`go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} go generate . fi From 07e6379af162bc4814ad7ed4eb617cc80aed8f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:24:33 +0800 Subject: [PATCH 041/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1c05519c..8df3af0f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,8 @@ jobs: if [ $GOOS = "windows" ]; then go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest export PATH=${PATH}:`go env GOPATH`/bin:`go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} + echo $PATH + ls -alh `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} go generate . fi From 2357829c03426b3586266d6b314425b5246356d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:30:20 +0800 Subject: [PATCH 042/612] =?UTF-8?q?ci=E4=B8=8D=E5=86=8D=E5=B0=9D=E8=AF=95?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8df3af0f9..a004c3ab1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,14 +50,6 @@ jobs: export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 - if [ $GOOS = "windows" ]; then - go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest - export PATH=${PATH}:`go env GOPATH`/bin:`go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} - echo $PATH - ls -alh `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} - go generate . - fi - go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . - name: Upload artifact From a36844705846cdff5d99dcb30fce0e7dfa36cca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:37:58 +0800 Subject: [PATCH 043/612] =?UTF-8?q?ci=E5=A2=9E=E5=8A=A0=E7=94=9F=E6=88=90?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E7=9A=84=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a004c3ab1..7e7357b9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: [push, pull_request,workflow_dispatch] env: - BINARY_PREFIX: "go-cqhttp_" + BINARY_PREFIX: "qq_robot" BINARY_SUFFIX: "" PR_PROMPT: "::warning:: Build artifact will not be uploaded due to the workflow is trigged by pull request." LD_FLAGS: "-w -s" @@ -52,6 +52,9 @@ jobs: go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . + cp global/config/default_config.yml output/config.yml + cp qqrobot/default_config.toml output/config.toml + - name: Upload artifact uses: actions/upload-artifact@v2 if: ${{ !github.head_ref }} From 451af077657a213d896a2e014433ab5d90501b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:42:22 +0800 Subject: [PATCH 044/612] =?UTF-8?q?=E8=B0=83=E6=95=B4exe=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e7357b9e..b70c53454 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: [push, pull_request,workflow_dispatch] env: - BINARY_PREFIX: "qq_robot" + BINARY_PREFIX: "qq_robot_" BINARY_SUFFIX: "" PR_PROMPT: "::warning:: Build artifact will not be uploaded due to the workflow is trigged by pull request." LD_FLAGS: "-w -s" From fdb9add0df7a0686fb1d653458018387fb044506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:42:33 +0800 Subject: [PATCH 045/612] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E4=B8=BA=E5=BF=AB=E9=80=9F=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/config/default_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global/config/default_config.yml b/global/config/default_config.yml index 4fb52e804..016ac4b2a 100644 --- a/global/config/default_config.yml +++ b/global/config/default_config.yml @@ -14,7 +14,7 @@ account: # 账号相关 # 注意, 此设置可能导致在海外服务器上连接情况更差 use-sso-address: true - fast-start: false # 是否快速启动 + fast-start: true # 是否快速启动 heartbeat: # 心跳频率, 单位秒 From 0d68c30c777a1a8e14c46b4d569e56e573b7885d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:45:55 +0800 Subject: [PATCH 046/612] =?UTF-8?q?=E5=86=8D=E8=AF=95=E8=AF=95=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b70c53454..2d9eeac99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,15 @@ jobs: export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 + if [ $GOOS = "windows" ]; then + go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest + cp `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }}/goversioninfo.exe . + export PATH=${PATH}:`go env GOPATH`/bin:`go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} + ls -alh `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} + ls -alh . + go generate . + fi + go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . cp global/config/default_config.yml output/config.yml From 1a7390a63bcd332898a188d10c8df09974099a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:53:38 +0800 Subject: [PATCH 047/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d9eeac99..d1b1e7b2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,10 +51,11 @@ jobs: export CGO_ENABLED=0 if [ $GOOS = "windows" ]; then - go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest - cp `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }}/goversioninfo.exe . + GOOS=linux GOARCH=amd64 go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest export PATH=${PATH}:`go env GOPATH`/bin:`go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} ls -alh `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} + ls -alh `go env GOPATH`/bin + ls -alh `go env GOPATH`/bin/linux_amd64 ls -alh . go generate . fi From b092b553c492a0b16e198abbbe8859d9d3a70c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:55:31 +0800 Subject: [PATCH 048/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1b1e7b2b..c0806c4ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,11 +52,8 @@ jobs: if [ $GOOS = "windows" ]; then GOOS=linux GOARCH=amd64 go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest - export PATH=${PATH}:`go env GOPATH`/bin:`go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} - ls -alh `go env GOPATH`/bin/${{ matrix.goos }}_${{ matrix.goarch }} ls -alh `go env GOPATH`/bin ls -alh `go env GOPATH`/bin/linux_amd64 - ls -alh . go generate . fi From 3059d125ffab475b6d56666c60f415b80b7ddefe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 18:56:26 +0800 Subject: [PATCH 049/612] =?UTF-8?q?=E8=B0=83=E8=AF=95ci?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0806c4ea..6ebe91a7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,8 +52,6 @@ jobs: if [ $GOOS = "windows" ]; then GOOS=linux GOARCH=amd64 go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest - ls -alh `go env GOPATH`/bin - ls -alh `go env GOPATH`/bin/linux_amd64 go generate . fi From b7f50c95260a7a862e971a3e83a12cda6ef39aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 19:00:38 +0800 Subject: [PATCH 050/612] =?UTF-8?q?=E4=BB=85=E5=9C=A8windows=20amd64?= =?UTF-8?q?=E4=B8=8B=E8=BF=9B=E8=A1=8Cicon=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ebe91a7b..03a49e3d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 - if [ $GOOS = "windows" ]; then + if [ $GOOS = "windows" ] && [ $GOARCH = "amd64" ]; then GOOS=linux GOARCH=amd64 go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest go generate . fi From eb44b46def67d52397cc72ae9b500b32ced1012b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 19:09:20 +0800 Subject: [PATCH 051/612] =?UTF-8?q?windows=20x86/64=E9=83=BD=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03a49e3d1..32dd99359 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 - if [ $GOOS = "windows" ] && [ $GOARCH = "amd64" ]; then + if [ $GOOS = "windows" ] && [ $GOARCH = "386" -o $GOARCH = "amd64" ]; then GOOS=linux GOARCH=amd64 go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest go generate . fi From e678cbbdf551e54ae9afbf6b3a44e263b7006de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 3 Oct 2021 19:27:28 +0800 Subject: [PATCH 052/612] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E5=8D=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 1b20d4ebe..6ae3b078a 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,3 @@ # TODO - [ ] 看看是否有更多事件值得接入,目前仅处理了群聊、私聊和加群这三个事件 - - -# re: 移除resource.syso,改为判定在windows下的情况时去自动生成 \ No newline at end of file From 13b4f5cf01707d303fae1b49f9976a6b2c05a8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 03:51:45 +0800 Subject: [PATCH 053/612] =?UTF-8?q?=E5=8E=9F=E6=9C=89=E7=9A=84=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=88=86=E7=89=87=E9=80=BB=E8=BE=91=E6=9C=89=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E5=BD=93=E4=B8=80=E6=9D=A1=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E8=A2=AB=E5=88=86=E5=89=B2=E6=88=90=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E6=97=B6=EF=BC=8C=E5=8F=AA=E8=80=83=E8=99=91=E4=BA=86?= =?UTF-8?q?=E6=9C=80=E5=90=8E=E4=B8=80=E4=B8=AA=E5=88=86=E7=89=87=EF=BC=8C?= =?UTF-8?q?=E5=9B=A0=E6=AD=A4=E9=87=8D=E6=96=B0=E5=AE=9E=E7=8E=B0=E4=B8=80?= =?UTF-8?q?=E9=81=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 56 ++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index b15c23de0..afea6b50a 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -634,43 +634,32 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool messages := m.Elements - for len(messages) != 0 { - forwardMessages := message.NewSendingMessage() - leftMessages := message.NewSendingMessage() - - msgSize := 0 + if len(messages) != 0 { + // 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素 + messageParts := message.NewSendingMessage() for _, msg := range messages { switch msgVal := msg.(type) { case *message.TextElement: - forwardMessages.Elements = append(forwardMessages.Elements, splitPlainMessage(msgVal.Content)...) + messageParts.Elements = append(messageParts.Elements, splitPlainMessage(msgVal.Content)...) case *message.GroupImageElement: - r.tryAppendImageByURL(forwardMessages, msgVal.Url) + r.tryAppendImageByURL(messageParts, msgVal.Url) case *message.AtElement: if msgVal.Target != 0 { - forwardMessages.Append(message.NewText(fmt.Sprintf("@%v(%v)", msgVal.Display, msgVal.Target))) + messageParts.Append(message.NewText(fmt.Sprintf("@%v(%v)", msgVal.Display, msgVal.Target))) } else { - forwardMessages.Append(message.NewText("@全体成员(转发)")) + messageParts.Append(message.NewText("@全体成员(转发)")) } case *message.FaceElement, *message.LightAppElement: - forwardMessages.Append(msg) + messageParts.Append(msg) default: jsonBytes, _ := json.Marshal(msg) - forwardMessages.Elements = append(forwardMessages.Elements, splitPlainMessage(fmt.Sprintf("%v\n", string(jsonBytes)))...) - } - - // 如果加了该消息后会超出单个消息大小,则先放入待定队列 - jsonBytes, _ := json.Marshal(forwardMessages.Elements[len(forwardMessages.Elements)-1]) - msgSize += len(jsonBytes) - // 需要确保每次至少转发一条消息 - if len(forwardMessages.Elements) > 1 && msgSize > maxMessageJSONSize { - forwardMessages.Elements = forwardMessages.Elements[:len(forwardMessages.Elements)-1] - msgSize -= len(jsonBytes) - leftMessages.Append(msg) + messageParts.Elements = append(messageParts.Elements, splitPlainMessage(fmt.Sprintf("%v\n", string(jsonBytes)))...) } } if !forRepeat { - forwardMessages.Append(message.NewText(fmt.Sprintf(""+ + // 如果是转发的消息,增加一个转发信息分片元素 + messageParts.Append(message.NewText(fmt.Sprintf(""+ "\n"+ "------------------------------\n"+ "转发自 群[%v:%v] QQ[%v:%v] 时间[%v]", @@ -680,8 +669,27 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool ))) } - forwardMessagesList = append(forwardMessagesList, forwardMessages) - messages = leftMessages.Elements + // 根据大小分为多个消息进行发送 + forwardMessages := message.NewSendingMessage() + msgSize := 0 + for _, part := range messageParts.Elements { + jsonBytes, _ := json.Marshal(part) + // 若当前分消息加上新的元素后大小会超限,且已经有元素(确保不会无限循环),则开始切分为新的一个元素 + if msgSize+len(jsonBytes) > maxMessageJSONSize && len(forwardMessages.Elements) > 0 { + forwardMessagesList = append(forwardMessagesList, forwardMessages) + + forwardMessages = message.NewSendingMessage() + msgSize = 0 + } + + // 加上新的元素 + forwardMessages.Append(part) + msgSize += len(jsonBytes) + } + // 将最后一个分片加上 + if len(forwardMessages.Elements) != 0 { + forwardMessagesList = append(forwardMessagesList, forwardMessages) + } } return forwardMessagesList From fbaaab177dccea2f419597acd2c83f6f7d897e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 04:33:55 +0800 Subject: [PATCH 054/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=88=87=E5=89=B2?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=E6=97=B6=E4=BD=BF=E7=94=A8=E7=9A=84=E9=A2=84?= =?UTF-8?q?=E8=AE=A1=E5=87=BD=E6=95=B0=E4=B8=BAgo-cqhttp=E4=B8=AD=E5=B0=81?= =?UTF-8?q?=E8=A3=85=E7=9A=84=E4=BC=B0=E8=AE=A1=E5=87=BD=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E8=B0=83=E6=95=B4=E5=A4=A7=E5=B0=8F=E4=B8=BA5000?= =?UTF-8?q?=EF=BC=88=E4=B8=8Emirai-go=E4=B8=ADSendGroupMessage=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E9=99=90=E5=88=B6=E4=BF=9D=E6=8C=81=E4=B8=80=E8=87=B4?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 136 +++++++++++++++++++++++++++----------------- qqrobot/util.go | 11 ++-- 2 files changed, 89 insertions(+), 58 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index afea6b50a..c0c237839 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -630,66 +630,96 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool) []*message.SendingMessage { - var forwardMessagesList []*message.SendingMessage + if len(m.Elements) == 0 { + return nil + } - messages := m.Elements - - if len(messages) != 0 { - // 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素 - messageParts := message.NewSendingMessage() - for _, msg := range messages { - switch msgVal := msg.(type) { - case *message.TextElement: - messageParts.Elements = append(messageParts.Elements, splitPlainMessage(msgVal.Content)...) - case *message.GroupImageElement: - r.tryAppendImageByURL(messageParts, msgVal.Url) - case *message.AtElement: - if msgVal.Target != 0 { - messageParts.Append(message.NewText(fmt.Sprintf("@%v(%v)", msgVal.Display, msgVal.Target))) - } else { - messageParts.Append(message.NewText("@全体成员(转发)")) - } - case *message.FaceElement, *message.LightAppElement: - messageParts.Append(msg) - default: - jsonBytes, _ := json.Marshal(msg) - messageParts.Elements = append(messageParts.Elements, splitPlainMessage(fmt.Sprintf("%v\n", string(jsonBytes)))...) - } + messages := append([]message.IMessageElement{}, m.Elements...) + if !forRepeat { + // 如果是转发的消息,增加一个转发信息分片元素 + messages = append(messages, message.NewText(fmt.Sprintf(""+ + "\n"+ + "------------------------------\n"+ + "转发自 群[%v:%v] QQ[%v:%v] 时间[%v]", + m.GroupName, m.GroupCode, + m.Sender.Nickname, m.Sender.Uin, + r.currentTime(), + ))) + } + + // 预先将所有连续的文本消息合并为到一起,方便后续统一切割 + mergeContinuousTextMessages := message.NewSendingMessage() + + textBuffer := strings.Builder{} + lastIsText := false + for _, msg := range messages { + if msgVal, ok := msg.(*message.TextElement); ok { + // 遇到文本元素先存放起来,方便将连续的文本元素合并 + textBuffer.WriteString(msgVal.Content) + lastIsText = true + continue } - if !forRepeat { - // 如果是转发的消息,增加一个转发信息分片元素 - messageParts.Append(message.NewText(fmt.Sprintf(""+ - "\n"+ - "------------------------------\n"+ - "转发自 群[%v:%v] QQ[%v:%v] 时间[%v]", - m.GroupName, m.GroupCode, - m.Sender.Nickname, m.Sender.Uin, - r.currentTime(), - ))) - } - - // 根据大小分为多个消息进行发送 - forwardMessages := message.NewSendingMessage() - msgSize := 0 - for _, part := range messageParts.Elements { - jsonBytes, _ := json.Marshal(part) - // 若当前分消息加上新的元素后大小会超限,且已经有元素(确保不会无限循环),则开始切分为新的一个元素 - if msgSize+len(jsonBytes) > maxMessageJSONSize && len(forwardMessages.Elements) > 0 { - forwardMessagesList = append(forwardMessagesList, forwardMessages) - - forwardMessages = message.NewSendingMessage() - msgSize = 0 - } + // 如果之前的是文本元素(可能是多个合并起来的),则在这里将其实际放入消息中 + if lastIsText { + mergeContinuousTextMessages.Append(message.NewText(textBuffer.String())) + textBuffer.Reset() + } + lastIsText = false + + // 非文本元素则直接处理 + mergeContinuousTextMessages.Append(msg) + } + // 处理最后几个元素是文本的情况 + if textBuffer.Len() != 0 { + mergeContinuousTextMessages.Append(message.NewText(textBuffer.String())) + textBuffer.Reset() + } - // 加上新的元素 - forwardMessages.Append(part) - msgSize += len(jsonBytes) + // 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素 + messageParts := message.NewSendingMessage() + for _, msg := range mergeContinuousTextMessages.Elements { + switch msgVal := msg.(type) { + case *message.TextElement: + messageParts.Elements = append(messageParts.Elements, splitPlainMessage(msgVal.Content)...) + case *message.GroupImageElement: + r.tryAppendImageByURL(messageParts, msgVal.Url) + case *message.AtElement: + if msgVal.Target != 0 { + messageParts.Append(message.NewText(fmt.Sprintf("@%v(%v)", msgVal.Display, msgVal.Target))) + } else { + messageParts.Append(message.NewText("@全体成员(转发)")) + } + case *message.FaceElement, *message.LightAppElement: + messageParts.Append(msg) + default: + jsonBytes, _ := json.Marshal(msg) + messageParts.Elements = append(messageParts.Elements, splitPlainMessage(fmt.Sprintf("%v\n", string(jsonBytes)))...) } - // 将最后一个分片加上 - if len(forwardMessages.Elements) != 0 { + } + + // 根据大小分为多个消息进行发送 + var forwardMessagesList []*message.SendingMessage + + forwardMessages := message.NewSendingMessage() + msgSize := 0 + for _, part := range messageParts.Elements { + estimateSize := message.EstimateLength([]message.IMessageElement{part}) + // 若当前分消息加上新的元素后大小会超限,且已经有元素(确保不会无限循环),则开始切分为新的一个元素 + if msgSize+estimateSize > maxMessageSize && len(forwardMessages.Elements) > 0 { forwardMessagesList = append(forwardMessagesList, forwardMessages) + + forwardMessages = message.NewSendingMessage() + msgSize = 0 } + + // 加上新的元素 + forwardMessages.Append(part) + msgSize += estimateSize + } + // 将最后一个分片加上 + if len(forwardMessages.Elements) != 0 { + forwardMessagesList = append(forwardMessagesList, forwardMessages) } return forwardMessagesList diff --git a/qqrobot/util.go b/qqrobot/util.go index 01c7ec73b..2ab423a06 100644 --- a/qqrobot/util.go +++ b/qqrobot/util.go @@ -105,10 +105,10 @@ func isMemberAdmin(permission client.MemberPermission) bool { } // 单条消息发送的大小有限制,所以需要分成多段来发 -const maxMessageJSONSize = 400 +const maxMessageSize = 5000 func splitPlainMessage(content string) []message.IMessageElement { - if len(content) <= maxMessageJSONSize { + if len(content) <= maxMessageSize { return []message.IMessageElement{message.NewText(content)} } @@ -118,11 +118,12 @@ func splitPlainMessage(content string) []message.IMessageElement { remainingText := content for len(remainingText) != 0 { partSize := 0 - for byteIdx, runeValue := range remainingText { - if partSize+byteIdx > maxMessageJSONSize { + for _, runeValue := range remainingText { + runeSize := len(string(runeValue)) + if partSize+runeSize > maxMessageSize { break } - partSize += len(string(runeValue)) + partSize += runeSize } part, remainingText = remainingText[:partSize], remainingText[partSize:] From 3333b17db5fbf44466d728a62f362e529d531fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 04:37:20 +0800 Subject: [PATCH 055/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E4=B8=BA=E4=B8=80=E9=94=AE=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=B9=B6=E9=87=8D=E6=96=B0=E7=BC=96=E8=AF=91=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E7=9A=84=E6=96=B9=E4=BE=BF=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/{_build.bat => _build_latest_version_and_run.bat} | 4 ++++ 1 file changed, 4 insertions(+) rename qqrobot/{_build.bat => _build_latest_version_and_run.bat} (64%) diff --git a/qqrobot/_build.bat b/qqrobot/_build_latest_version_and_run.bat similarity index 64% rename from qqrobot/_build.bat rename to qqrobot/_build_latest_version_and_run.bat index e68b14306..fa6d05274 100644 --- a/qqrobot/_build.bat +++ b/qqrobot/_build_latest_version_and_run.bat @@ -1,2 +1,6 @@ +git pull + go generate . go build -v -o qq_robot.exe . + +qq_robot.exe From fa0d029dacd7735ddd28fdc05be9cd80cb967ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 04:46:03 +0800 Subject: [PATCH 056/612] =?UTF-8?q?=E5=B0=81=E8=A3=85=E5=90=84=E4=B8=AA?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=B5=81=E7=A8=8B=EF=BC=8C=E5=8A=A0=E5=BC=BA?= =?UTF-8?q?=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index c0c237839..4e1f800fd 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -634,10 +634,13 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool return nil } - messages := append([]message.IMessageElement{}, m.Elements...) + // 转换为消息类型,方便处理 + messages := message.NewSendingMessage() + messages.Elements = append([]message.IMessageElement{}, m.Elements...) + if !forRepeat { // 如果是转发的消息,增加一个转发信息分片元素 - messages = append(messages, message.NewText(fmt.Sprintf(""+ + messages.Append(message.NewText(fmt.Sprintf(""+ "\n"+ "------------------------------\n"+ "转发自 群[%v:%v] QQ[%v:%v] 时间[%v]", @@ -647,12 +650,23 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool ))) } - // 预先将所有连续的文本消息合并为到一起,方便后续统一切割 + // 合并连续文本消息 + messages = r.mergeContinuousTextMessages(messages) + // 分割过长元素 + messages = r.splitElements(messages) + // 将元素分为多组,确保各组不超过单条消息的上限 + forwardMessagesList := r.splitMessages(messages) + + return forwardMessagesList +} + +// mergeContinuousTextMessages 预先将所有连续的文本消息合并为到一起,方便后续统一切割 +func (r *QQRobot) mergeContinuousTextMessages(messages *message.SendingMessage) *message.SendingMessage { mergeContinuousTextMessages := message.NewSendingMessage() textBuffer := strings.Builder{} lastIsText := false - for _, msg := range messages { + for _, msg := range messages.Elements { if msgVal, ok := msg.(*message.TextElement); ok { // 遇到文本元素先存放起来,方便将连续的文本元素合并 textBuffer.WriteString(msgVal.Content) @@ -676,9 +690,13 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool textBuffer.Reset() } - // 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素 + return mergeContinuousTextMessages +} + +// splitElements 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素 +func (r *QQRobot) splitElements(messages *message.SendingMessage) *message.SendingMessage { messageParts := message.NewSendingMessage() - for _, msg := range mergeContinuousTextMessages.Elements { + for _, msg := range messages.Elements { switch msgVal := msg.(type) { case *message.TextElement: messageParts.Elements = append(messageParts.Elements, splitPlainMessage(msgVal.Content)...) @@ -697,13 +715,16 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool messageParts.Elements = append(messageParts.Elements, splitPlainMessage(fmt.Sprintf("%v\n", string(jsonBytes)))...) } } + return messageParts +} - // 根据大小分为多个消息进行发送 +// splitMessages 根据大小分为多个消息进行发送 +func (r *QQRobot) splitMessages(messages *message.SendingMessage) []*message.SendingMessage { var forwardMessagesList []*message.SendingMessage forwardMessages := message.NewSendingMessage() msgSize := 0 - for _, part := range messageParts.Elements { + for _, part := range messages.Elements { estimateSize := message.EstimateLength([]message.IMessageElement{part}) // 若当前分消息加上新的元素后大小会超限,且已经有元素(确保不会无限循环),则开始切分为新的一个元素 if msgSize+estimateSize > maxMessageSize && len(forwardMessages.Elements) > 0 { @@ -721,7 +742,6 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool if len(forwardMessages.Elements) != 0 { forwardMessagesList = append(forwardMessagesList, forwardMessages) } - return forwardMessagesList } From e1512896ed08aa097c3681ec03731e93a478f5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 04:54:16 +0800 Subject: [PATCH 057/612] =?UTF-8?q?=E5=86=8D=E9=A2=9D=E5=A4=96=E5=B0=81?= =?UTF-8?q?=E8=A3=85=E4=B8=80=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 4e1f800fd..df5b6c5f8 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -635,12 +635,12 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool } // 转换为消息类型,方便处理 - messages := message.NewSendingMessage() - messages.Elements = append([]message.IMessageElement{}, m.Elements...) + sendingMessage := message.NewSendingMessage() + sendingMessage.Elements = append([]message.IMessageElement{}, m.Elements...) if !forRepeat { // 如果是转发的消息,增加一个转发信息分片元素 - messages.Append(message.NewText(fmt.Sprintf(""+ + sendingMessage.Append(message.NewText(fmt.Sprintf(""+ "\n"+ "------------------------------\n"+ "转发自 群[%v:%v] QQ[%v:%v] 时间[%v]", @@ -650,14 +650,22 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool ))) } + forwardMessagesList := r.splitLongMessage(sendingMessage) + + return forwardMessagesList +} + +func (r *QQRobot) splitLongMessage(sendingMessage *message.SendingMessage) []*message.SendingMessage { // 合并连续文本消息 - messages = r.mergeContinuousTextMessages(messages) + sendingMessage = r.mergeContinuousTextMessages(sendingMessage) + // 分割过长元素 - messages = r.splitElements(messages) + sendingMessage = r.splitElements(sendingMessage) + // 将元素分为多组,确保各组不超过单条消息的上限 - forwardMessagesList := r.splitMessages(messages) + splitMessages := r.splitMessages(sendingMessage) - return forwardMessagesList + return splitMessages } // mergeContinuousTextMessages 预先将所有连续的文本消息合并为到一起,方便后续统一切割 From d74c9a6eb2c32c8385e5426eab4d836b5599a08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 04:57:24 +0800 Subject: [PATCH 058/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index df5b6c5f8..c775dd7c1 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -669,12 +669,12 @@ func (r *QQRobot) splitLongMessage(sendingMessage *message.SendingMessage) []*me } // mergeContinuousTextMessages 预先将所有连续的文本消息合并为到一起,方便后续统一切割 -func (r *QQRobot) mergeContinuousTextMessages(messages *message.SendingMessage) *message.SendingMessage { +func (r *QQRobot) mergeContinuousTextMessages(sendingMessage *message.SendingMessage) *message.SendingMessage { mergeContinuousTextMessages := message.NewSendingMessage() textBuffer := strings.Builder{} lastIsText := false - for _, msg := range messages.Elements { + for _, msg := range sendingMessage.Elements { if msgVal, ok := msg.(*message.TextElement); ok { // 遇到文本元素先存放起来,方便将连续的文本元素合并 textBuffer.WriteString(msgVal.Content) @@ -702,9 +702,10 @@ func (r *QQRobot) mergeContinuousTextMessages(messages *message.SendingMessage) } // splitElements 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素 -func (r *QQRobot) splitElements(messages *message.SendingMessage) *message.SendingMessage { +func (r *QQRobot) splitElements(sendingMessage *message.SendingMessage) *message.SendingMessage { messageParts := message.NewSendingMessage() - for _, msg := range messages.Elements { + + for _, msg := range sendingMessage.Elements { switch msgVal := msg.(type) { case *message.TextElement: messageParts.Elements = append(messageParts.Elements, splitPlainMessage(msgVal.Content)...) @@ -723,34 +724,36 @@ func (r *QQRobot) splitElements(messages *message.SendingMessage) *message.Sendi messageParts.Elements = append(messageParts.Elements, splitPlainMessage(fmt.Sprintf("%v\n", string(jsonBytes)))...) } } + return messageParts } // splitMessages 根据大小分为多个消息进行发送 -func (r *QQRobot) splitMessages(messages *message.SendingMessage) []*message.SendingMessage { - var forwardMessagesList []*message.SendingMessage +func (r *QQRobot) splitMessages(sendingMessage *message.SendingMessage) []*message.SendingMessage { + var splitMessages []*message.SendingMessage - forwardMessages := message.NewSendingMessage() + messagePart := message.NewSendingMessage() msgSize := 0 - for _, part := range messages.Elements { + for _, part := range sendingMessage.Elements { estimateSize := message.EstimateLength([]message.IMessageElement{part}) // 若当前分消息加上新的元素后大小会超限,且已经有元素(确保不会无限循环),则开始切分为新的一个元素 - if msgSize+estimateSize > maxMessageSize && len(forwardMessages.Elements) > 0 { - forwardMessagesList = append(forwardMessagesList, forwardMessages) + if msgSize+estimateSize > maxMessageSize && len(messagePart.Elements) > 0 { + splitMessages = append(splitMessages, messagePart) - forwardMessages = message.NewSendingMessage() + messagePart = message.NewSendingMessage() msgSize = 0 } // 加上新的元素 - forwardMessages.Append(part) + messagePart.Append(part) msgSize += estimateSize } // 将最后一个分片加上 - if len(forwardMessages.Elements) != 0 { - forwardMessagesList = append(forwardMessagesList, forwardMessages) + if len(messagePart.Elements) != 0 { + splitMessages = append(splitMessages, messagePart) } - return forwardMessagesList + + return splitMessages } func (r *QQRobot) onMemberJoin(m *client.MemberJoinGroupEvent, rule *Rule) error { From c9f2c6dc5e385c32e4709b28348f88a123e213c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 05:08:21 +0800 Subject: [PATCH 059/612] =?UTF-8?q?=E5=90=88=E5=B9=B6=E8=BF=9E=E7=BB=AD?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA=E9=A2=84?= =?UTF-8?q?=E5=85=88=E5=88=A4=E6=96=AD=EF=BC=8C=E8=8B=A5=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=EF=BC=8C=E5=88=99=E7=9B=B4=E6=8E=A5=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index c775dd7c1..1d7330276 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -655,6 +655,7 @@ func (r *QQRobot) getForwardMessagesList(m *message.GroupMessage, forRepeat bool return forwardMessagesList } +// splitLongMessage 将过长的消息分割为若干个适合发送的消息 func (r *QQRobot) splitLongMessage(sendingMessage *message.SendingMessage) []*message.SendingMessage { // 合并连续文本消息 sendingMessage = r.mergeContinuousTextMessages(sendingMessage) @@ -670,10 +671,33 @@ func (r *QQRobot) splitLongMessage(sendingMessage *message.SendingMessage) []*me // mergeContinuousTextMessages 预先将所有连续的文本消息合并为到一起,方便后续统一切割 func (r *QQRobot) mergeContinuousTextMessages(sendingMessage *message.SendingMessage) *message.SendingMessage { + // 检查下是否有连续的文本消息,若没有,则可以直接返回 + lastIsText := false + hasContinuousText := false + for _, msg := range sendingMessage.Elements { + if _, ok := msg.(*message.TextElement); ok { + if lastIsText { + // 有连续的文本消息,需要进行处理 + hasContinuousText = true + break + } + + // 遇到文本元素先存放起来,方便将连续的文本元素合并 + lastIsText = true + continue + } else { + lastIsText = false + } + } + if !hasContinuousText { + return sendingMessage + } + + // 存在连续的文本消息,需要进行合并处理 mergeContinuousTextMessages := message.NewSendingMessage() textBuffer := strings.Builder{} - lastIsText := false + lastIsText = false for _, msg := range sendingMessage.Elements { if msgVal, ok := msg.(*message.TextElement); ok { // 遇到文本元素先存放起来,方便将连续的文本元素合并 @@ -752,7 +776,7 @@ func (r *QQRobot) splitMessages(sendingMessage *message.SendingMessage) []*messa if len(messagePart.Elements) != 0 { splitMessages = append(splitMessages, messagePart) } - + return splitMessages } From df7f395ed631a535108906b527086c856c47e211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 07:08:18 +0800 Subject: [PATCH 060/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=89=B2=E5=BD=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/log_hook.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/global/log_hook.go b/global/log_hook.go index eb4f27310..a942a02a4 100644 --- a/global/log_hook.go +++ b/global/log_hook.go @@ -9,6 +9,7 @@ import ( "strings" "sync" + "github.com/gookit/color" "github.com/sirupsen/logrus" ) @@ -183,6 +184,9 @@ type LogFormat struct{} func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) { buf := NewBuffer() defer PutBuffer(buf) + + buf.WriteString(GetLogLevelColorCode(entry.Level)) + buf.WriteByte('[') buf.WriteString(entry.Time.Format("2006-01-02 15:04:05")) buf.WriteString("] [") @@ -190,6 +194,42 @@ func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) { buf.WriteString("]: ") buf.WriteString(entry.Message) buf.WriteString(" \n") + + buf.WriteString(color.ResetSet) + ret := append([]byte(nil), buf.Bytes()...) // copy buffer return ret, nil } + +var ( + ColorCodePanic = fmt.Sprintf(color.SettingTpl, color.Style{color.Bold, color.Red}.String()) + ColorCodeFatal = fmt.Sprintf(color.SettingTpl, color.Style{color.Bold, color.Red}.String()) + ColorCodeError = fmt.Sprintf(color.SettingTpl, color.Style{color.Red}.String()) + ColorCodeWarn = fmt.Sprintf(color.SettingTpl, color.Style{color.Yellow}.String()) + ColorCodeInfo = fmt.Sprintf(color.SettingTpl, color.Style{color.Green}.String()) + ColorCodeDebug = fmt.Sprintf(color.SettingTpl, color.Style{color.White}.String()) + ColorCodeTrace = fmt.Sprintf(color.SettingTpl, color.Style{color.Cyan}.String()) +) + +// GetLogLevelColorCode 获取日志等级对应色彩code +func GetLogLevelColorCode(level logrus.Level) string { + switch level { + case logrus.PanicLevel: + return ColorCodePanic + case logrus.FatalLevel: + return ColorCodeFatal + case logrus.ErrorLevel: + return ColorCodeError + case logrus.WarnLevel: + return ColorCodeWarn + case logrus.InfoLevel: + return ColorCodeInfo + case logrus.DebugLevel: + return ColorCodeDebug + case logrus.TraceLevel: + return ColorCodeTrace + + default: + return ColorCodeInfo + } +} From 1b0154ef30e53d9ace2d0bd95e02b7f42312abe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 07:08:39 +0800 Subject: [PATCH 061/612] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 45 ++++++++++++---------- go.sum | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index f3603d248..7a604ea35 100644 --- a/go.mod +++ b/go.mod @@ -8,26 +8,26 @@ replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f - github.com/BurntSushi/toml v0.3.1 + github.com/BurntSushi/toml v0.4.1 github.com/Microsoft/go-winio v0.5.0 - github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82 + github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a github.com/dustin/go-humanize v1.0.0 github.com/fzls/logger v1.0.1 github.com/gabriel-vasile/mimetype v1.3.1 github.com/gookit/color v1.4.2 github.com/gorilla/websocket v1.4.2 - github.com/guonaihong/gout v0.2.4 + github.com/guonaihong/gout v0.2.9 github.com/hashicorp/golang-lru v0.5.4 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/pkg/errors v0.9.1 - github.com/segmentio/asm v1.0.0 + github.com/segmentio/asm v1.0.1 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264 - github.com/tidwall/gjson v1.8.1 + github.com/tidwall/gjson v1.9.1 github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 @@ -38,30 +38,35 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect - github.com/google/uuid v1.1.1 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.9.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect - github.com/klauspost/cpuid/v2 v2.0.6 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/leodido/go-urn v1.2.1 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect - github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/maruel/rs v1.0.0 // indirect + github.com/mattn/go-colorable v0.1.11 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/tidwall/match v1.0.3 // indirect - github.com/tidwall/pretty v1.1.0 // indirect + github.com/tidwall/pretty v1.2.0 // indirect github.com/willf/bitset v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - go.uber.org/atomic v1.6.0 // indirect - go.uber.org/multierr v1.5.0 // indirect - go.uber.org/zap v1.15.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.7.0 // indirect + go.uber.org/zap v1.19.1 // indirect golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect + golang.org/x/text v0.3.6 // indirect google.golang.org/protobuf v1.27.1 // indirect - gopkg.in/yaml.v2 v2.2.8 // indirect - modernc.org/libc v1.8.1 // indirect - modernc.org/mathutil v1.2.2 // indirect - modernc.org/memory v1.0.4 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + modernc.org/libc v1.11.22 // indirect + modernc.org/mathutil v1.4.1 // indirect + modernc.org/memory v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index d6be9bcdc..13869c1f0 100644 --- a/go.sum +++ b/go.sum @@ -3,14 +3,20 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82 h1:VAav2Z5n41cp2bjskooPXAg9W39qQeEEj3FmYlnS3+E= github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82/go.mod h1:EpB6wQ+iaIqLRxVrs6si2J3XGv6wHXMrRXNUnJFaoww= +github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a h1:D2zG/7w7q5QbDeHSx9n4lS9cv9MGBqEjNTzeVO5g3Mo= +github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a/go.mod h1:EpB6wQ+iaIqLRxVrs6si2J3XGv6wHXMrRXNUnJFaoww= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -30,10 +36,16 @@ github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjX github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= +github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -50,10 +62,13 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -61,12 +76,16 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/guonaihong/gout v0.2.4 h1:BlWpWWay/Q1LkyIwupEWBZE3PMl4xzzAgMHw+OrZxBs= github.com/guonaihong/gout v0.2.4/go.mod h1:ISabiAAj0z1h3bOFUKzfRqPMvX0wmcYzIh6i4xIxMPo= +github.com/guonaihong/gout v0.2.9 h1:8nU5hrtwP1qDwiadFvU+D+z3ud9WEk8iPSfxQDiebng= +github.com/guonaihong/gout v0.2.9/go.mod h1:H1JqEuZmK4h/urWUq/LnIOEzS1kxl5rK3NkFqZ6Rn48= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -77,16 +96,24 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= @@ -95,10 +122,16 @@ github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 h1:u9jwvcKbQpghIXgNl/EOL8hzhAFXh4ePrEP493W3tNA= github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4/go.mod h1:kcRFpEzolcEklV6rD7W95mG49/sbdX/PlFmd7ni3RvA= +github.com/maruel/rs v1.0.0 h1:NoRR74bfxQy8CzWdkwYb5Rz+H93sl30GSJIvf6ADZZg= +github.com/maruel/rs v1.0.0/go.mod h1:F3JiIVIlJdcXIW+Ep10ZeiPAwKwSA5IQ6QTpILEE/aM= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= @@ -108,6 +141,7 @@ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -117,8 +151,12 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/segmentio/asm v1.0.0 h1:GMF7/2eLkte13LERSOmPI766AJXJDRlsqqiN8T7Mfmk= github.com/segmentio/asm v1.0.0/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4= +github.com/segmentio/asm v1.0.1 h1:g9VK62hXylgXI4yJV+dLTu/1j7kTxG9bkUSYBxL9dpg= +github.com/segmentio/asm v1.0.1/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -139,10 +177,14 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264 h1:kGT41c6 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= +github.com/tidwall/gjson v1.9.1 h1:wrrRk7TyL7MmKanNRck/Mcr3VU1sdMvJHvJXzqBIUNo= +github.com/tidwall/gjson v1.9.1/go.mod h1:jydLKE7s8J0+1/5jC4eXcuFlzKizGrCKvLmBVX/5oXc= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 h1:BWVtt2VBY+lmVDu9MGKqLGKl04B+iRHcrW1Ptyi/8tg= github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2/go.mod h1:lPnW9HVS0vJdeYyQtOvIvlXgZPNhUAhwz+z5r8AJk0Y= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= @@ -151,19 +193,33 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4= github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -173,6 +229,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -181,13 +239,16 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -197,13 +258,20 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -223,9 +291,14 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -248,6 +321,7 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -257,6 +331,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -264,10 +340,49 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.22 h1:u2ek1qgzki0RUDI2kW3EC9VKHP9LXKxKnoQLZmIeCbA= +modernc.org/libc v1.11.22/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= From 6c0945f7f5c49574f3a2b4980a3003a0c9be7180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 23:52:33 +0800 Subject: [PATCH 062/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9E=84=E5=BB=BAdoc?= =?UTF-8?q?ker=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build_docker_image.yml | 89 ++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 .github/workflows/build_docker_image.yml diff --git a/.github/workflows/build_docker_image.yml b/.github/workflows/build_docker_image.yml new file mode 100644 index 000000000..d0cf1d581 --- /dev/null +++ b/.github/workflows/build_docker_image.yml @@ -0,0 +1,89 @@ +name: Build And Push Docker Image + +on: + push: + branches: + - 'master' + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + schedule: + # 每晚十点生成 nightly 镜像 + - cron: '00 14 * * *' # GMT 14:00 => 北京时间 22:00 + workflow_dispatch: + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + build-platform: [ + linux/386, linux/amd64, linux/arm, linux/arm64, + windows/386, windows/amd64, windows/arm, windows/arm64, + darwin/amd64, darwin/arm64, + ] + + permissions: + packages: write + contents: read + + steps: + - uses: actions/checkout@v2 + + - name: Set time zone + uses: szenius/set-timezone@v1.0 + with: + timezoneLinux: "Asia/Shanghai" + timezoneMacos: "Asia/Shanghai" + timezoneWindows: "China Standard Time" + + # # 如果有 dockerhub 账户,可以在github的secrets中配置下面两个,然后取消注释这几行 + # - name: Login to DockerHub + # uses: docker/login-action@v1 + # with: + # username: ${{ secrets.DOCKERHUB_USERNAME }} + # password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GHCR + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v3 + with: + images: | + ${{ github.repository }} + ghcr.io/${{ github.repository }} + ccr.ccs.tencentyun.com/${{ github.repository }} + # generate Docker tags based on the following events/attributes + # nightly, master, pr-2, 1.2.3, 1.2, 1 + tags: | + type=schedule,pattern=nightly + type=edge + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + context: . + platforms: ${{ matrix.build-platform }} + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} From 65fc4e82ca8a445b6c12c0923bc015e692851f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 4 Oct 2021 23:59:36 +0800 Subject: [PATCH 063/612] =?UTF-8?q?=E7=A7=BB=E9=99=A4dockerfile=E5=A4=9A?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=9A=84=E4=B8=80=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ad19d987b..c5eff4d93 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,6 @@ COPY ./ . RUN set -ex \ && cd /build \ - && go generate . && go build -ldflags "-s -w -extldflags '-static'" -o cqhttp FROM alpine:latest From 1c6574a36dfcdfbfbd92f8145916475c407dafd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 5 Oct 2021 00:01:22 +0800 Subject: [PATCH 064/612] lint --- global/log_hook.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/global/log_hook.go b/global/log_hook.go index a942a02a4..c2a667559 100644 --- a/global/log_hook.go +++ b/global/log_hook.go @@ -202,34 +202,34 @@ func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) { } var ( - ColorCodePanic = fmt.Sprintf(color.SettingTpl, color.Style{color.Bold, color.Red}.String()) - ColorCodeFatal = fmt.Sprintf(color.SettingTpl, color.Style{color.Bold, color.Red}.String()) - ColorCodeError = fmt.Sprintf(color.SettingTpl, color.Style{color.Red}.String()) - ColorCodeWarn = fmt.Sprintf(color.SettingTpl, color.Style{color.Yellow}.String()) - ColorCodeInfo = fmt.Sprintf(color.SettingTpl, color.Style{color.Green}.String()) - ColorCodeDebug = fmt.Sprintf(color.SettingTpl, color.Style{color.White}.String()) - ColorCodeTrace = fmt.Sprintf(color.SettingTpl, color.Style{color.Cyan}.String()) + colorCodePanic = fmt.Sprintf(color.SettingTpl, color.Style{color.Bold, color.Red}.String()) + colorCodeFatal = fmt.Sprintf(color.SettingTpl, color.Style{color.Bold, color.Red}.String()) + colorCodeError = fmt.Sprintf(color.SettingTpl, color.Style{color.Red}.String()) + colorCodeWarn = fmt.Sprintf(color.SettingTpl, color.Style{color.Yellow}.String()) + colorCodeInfo = fmt.Sprintf(color.SettingTpl, color.Style{color.Green}.String()) + colorCodeDebug = fmt.Sprintf(color.SettingTpl, color.Style{color.White}.String()) + colorCodeTrace = fmt.Sprintf(color.SettingTpl, color.Style{color.Cyan}.String()) ) // GetLogLevelColorCode 获取日志等级对应色彩code func GetLogLevelColorCode(level logrus.Level) string { switch level { case logrus.PanicLevel: - return ColorCodePanic + return colorCodePanic case logrus.FatalLevel: - return ColorCodeFatal + return colorCodeFatal case logrus.ErrorLevel: - return ColorCodeError + return colorCodeError case logrus.WarnLevel: - return ColorCodeWarn + return colorCodeWarn case logrus.InfoLevel: - return ColorCodeInfo + return colorCodeInfo case logrus.DebugLevel: - return ColorCodeDebug + return colorCodeDebug case logrus.TraceLevel: - return ColorCodeTrace + return colorCodeTrace default: - return ColorCodeInfo + return colorCodeInfo } } From 8dde5b21e7889cf211558bffbdac4740c23ad6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 5 Oct 2021 00:02:55 +0800 Subject: [PATCH 065/612] =?UTF-8?q?=E8=B0=83=E8=AF=95docker=20image?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build_docker_image.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/build_docker_image.yml b/.github/workflows/build_docker_image.yml index d0cf1d581..5f96532c0 100644 --- a/.github/workflows/build_docker_image.yml +++ b/.github/workflows/build_docker_image.yml @@ -17,14 +17,6 @@ jobs: runs-on: ubuntu-latest - strategy: - matrix: - build-platform: [ - linux/386, linux/amd64, linux/arm, linux/arm64, - windows/386, windows/amd64, windows/arm, windows/arm64, - darwin/amd64, darwin/arm64, - ] - permissions: packages: write contents: read @@ -83,7 +75,6 @@ jobs: uses: docker/build-push-action@v2 with: context: . - platforms: ${{ matrix.build-platform }} push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} From f59d8423fde2a61707aa6ff0c1cb000df45bdf09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 5 Oct 2021 00:10:13 +0800 Subject: [PATCH 066/612] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=9A=84tags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build_docker_image.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_docker_image.yml b/.github/workflows/build_docker_image.yml index 5f96532c0..0b592bc8d 100644 --- a/.github/workflows/build_docker_image.yml +++ b/.github/workflows/build_docker_image.yml @@ -31,12 +31,12 @@ jobs: timezoneMacos: "Asia/Shanghai" timezoneWindows: "China Standard Time" - # # 如果有 dockerhub 账户,可以在github的secrets中配置下面两个,然后取消注释这几行 - # - name: Login to DockerHub - # uses: docker/login-action@v1 - # with: - # username: ${{ secrets.DOCKERHUB_USERNAME }} - # password: ${{ secrets.DOCKERHUB_TOKEN }} + # # 如果有 dockerhub 账户,可以在github的secrets中配置下面两个,然后取消下面注释的这几行,并在meta步骤的images增加一行 ${{ github.repository }} + # - name: Login to DockerHub + # uses: docker/login-action@v1 + # with: + # username: ${{ secrets.DOCKERHUB_USERNAME }} + # password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GHCR uses: docker/login-action@v1 @@ -50,9 +50,7 @@ jobs: uses: docker/metadata-action@v3 with: images: | - ${{ github.repository }} ghcr.io/${{ github.repository }} - ccr.ccs.tencentyun.com/${{ github.repository }} # generate Docker tags based on the following events/attributes # nightly, master, pr-2, 1.2.3, 1.2, 1 tags: | From 6ddb02394cc2e86ebcedfb7d59f3e3292231b715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 5 Oct 2021 01:01:17 +0800 Subject: [PATCH 067/612] =?UTF-8?q?=E5=88=86=E5=BC=80=E5=A4=84=E7=90=86con?= =?UTF-8?q?sole=E5=92=8Cfile=E7=9A=84log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/log_hook.go | 32 ++++++++++++++++++++++++-------- main.go | 4 +++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/global/log_hook.go b/global/log_hook.go index c2a667559..8d307c3a1 100644 --- a/global/log_hook.go +++ b/global/log_hook.go @@ -81,10 +81,20 @@ func (hook *LocalHook) Fire(entry *logrus.Entry) error { } // SetFormatter 设置日志格式 -func (hook *LocalHook) SetFormatter(formatter logrus.Formatter) { +func (hook *LocalHook) SetFormatter(consoleFormatter, fileFormatter logrus.Formatter) { hook.lock.Lock() defer hook.lock.Unlock() + tryChangeFormatter(consoleFormatter) + tryChangeFormatter(fileFormatter) + + // 用于在console写出 + logrus.SetFormatter(consoleFormatter) + // 用于写入文件 + hook.formatter = fileFormatter +} + +func tryChangeFormatter(formatter logrus.Formatter) logrus.Formatter { if formatter == nil { // 用默认的 formatter = &logrus.TextFormatter{DisableColors: true} @@ -97,8 +107,8 @@ func (hook *LocalHook) SetFormatter(formatter logrus.Formatter) { // todo } } - logrus.SetFormatter(formatter) - hook.formatter = formatter + + return formatter } // SetWriter 设置Writer @@ -116,11 +126,11 @@ func (hook *LocalHook) SetPath(path string) { } // NewLocalHook 初始化本地日志钩子实现 -func NewLocalHook(args interface{}, formatter logrus.Formatter, levels ...logrus.Level) *LocalHook { +func NewLocalHook(args interface{}, consoleFormatter, fileFormatter logrus.Formatter, levels ...logrus.Level) *LocalHook { hook := &LocalHook{ lock: new(sync.Mutex), } - hook.SetFormatter(formatter) + hook.SetFormatter(consoleFormatter, fileFormatter) hook.levels = append(hook.levels, levels...) switch arg := args.(type) { @@ -178,14 +188,18 @@ func GetLogLevel(level string) []logrus.Level { } // LogFormat specialize for go-cqhttp -type LogFormat struct{} +type LogFormat struct { + EnableColor bool +} // Format implements logrus.Formatter func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) { buf := NewBuffer() defer PutBuffer(buf) - buf.WriteString(GetLogLevelColorCode(entry.Level)) + if f.EnableColor { + buf.WriteString(GetLogLevelColorCode(entry.Level)) + } buf.WriteByte('[') buf.WriteString(entry.Time.Format("2006-01-02 15:04:05")) @@ -195,7 +209,9 @@ func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) { buf.WriteString(entry.Message) buf.WriteString(" \n") - buf.WriteString(color.ResetSet) + if f.EnableColor { + buf.WriteString(color.ResetSet) + } ret := append([]byte(nil), buf.Bytes()...) // copy buffer return ret, nil diff --git a/main.go b/main.go index d39bf91d0..70f6e0834 100644 --- a/main.go +++ b/main.go @@ -106,7 +106,9 @@ func main() { panic(err) } - log.AddHook(global.NewLocalHook(w, global.LogFormat{}, global.GetLogLevel(conf.Output.LogLevel)...)) + consoleFormatter := global.LogFormat{true} + fileFormatter := global.LogFormat{false} + log.AddHook(global.NewLocalHook(w, consoleFormatter, fileFormatter, global.GetLogLevel(conf.Output.LogLevel)...)) mkCacheDir := func(path string, _type string) { if !global.PathExists(path) { From 06278ba9fdec20b5094db1595e68710610fb6a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 5 Oct 2021 01:43:17 +0800 Subject: [PATCH 068/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=89=B2=E5=BD=A9?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=8C=E4=BD=BF=E7=94=A8=E5=B7=B2=E6=9C=89?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/log_hook.go | 31 ++++++++++++++++++++----------- go.mod | 2 +- go.sum | 45 ++++++++------------------------------------- main.go | 4 ++-- 4 files changed, 31 insertions(+), 51 deletions(-) diff --git a/global/log_hook.go b/global/log_hook.go index 8d307c3a1..93606aa2c 100644 --- a/global/log_hook.go +++ b/global/log_hook.go @@ -9,7 +9,7 @@ import ( "strings" "sync" - "github.com/gookit/color" + "github.com/mattn/go-colorable" "github.com/sirupsen/logrus" ) @@ -85,9 +85,11 @@ func (hook *LocalHook) SetFormatter(consoleFormatter, fileFormatter logrus.Forma hook.lock.Lock() defer hook.lock.Unlock() - tryChangeFormatter(consoleFormatter) - tryChangeFormatter(fileFormatter) + consoleFormatter = tryChangeFormatter(consoleFormatter) + fileFormatter = tryChangeFormatter(fileFormatter) + // 支持处理windows平台的console色彩 + logrus.SetOutput(colorable.NewColorableStdout()) // 用于在console写出 logrus.SetFormatter(consoleFormatter) // 用于写入文件 @@ -210,21 +212,28 @@ func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) { buf.WriteString(" \n") if f.EnableColor { - buf.WriteString(color.ResetSet) + buf.WriteString(ResetSet) } ret := append([]byte(nil), buf.Bytes()...) // copy buffer return ret, nil } +// 为了不引入新依赖,直接将对应库需要的部分复制过来了,具体可参考 github.com\gookit\color@v1.4.2\color.go +const ResetSet = "\x1b[0m" + +const ( + SettingTpl = "\x1b[%sm" +) + var ( - colorCodePanic = fmt.Sprintf(color.SettingTpl, color.Style{color.Bold, color.Red}.String()) - colorCodeFatal = fmt.Sprintf(color.SettingTpl, color.Style{color.Bold, color.Red}.String()) - colorCodeError = fmt.Sprintf(color.SettingTpl, color.Style{color.Red}.String()) - colorCodeWarn = fmt.Sprintf(color.SettingTpl, color.Style{color.Yellow}.String()) - colorCodeInfo = fmt.Sprintf(color.SettingTpl, color.Style{color.Green}.String()) - colorCodeDebug = fmt.Sprintf(color.SettingTpl, color.Style{color.White}.String()) - colorCodeTrace = fmt.Sprintf(color.SettingTpl, color.Style{color.Cyan}.String()) + colorCodePanic = fmt.Sprintf(SettingTpl, "1;31") // color.Style{color.Bold, color.Red}.String() + colorCodeFatal = fmt.Sprintf(SettingTpl, "1;31") // color.Style{color.Bold, color.Red}.String() + colorCodeError = fmt.Sprintf(SettingTpl, "31") // color.Style{color.Red}.String() + colorCodeWarn = fmt.Sprintf(SettingTpl, "33") // color.Style{color.Yellow}.String() + colorCodeInfo = fmt.Sprintf(SettingTpl, "32") // color.Style{color.Green}.String() + colorCodeDebug = fmt.Sprintf(SettingTpl, "37") // color.Style{color.White}.String() + colorCodeTrace = fmt.Sprintf(SettingTpl, "36") // color.Style{color.Cyan}.String() ) // GetLogLevelColorCode 获取日志等级对应色彩code diff --git a/go.mod b/go.mod index 7a604ea35..43ecd938e 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible + github.com/mattn/go-colorable v0.1.11 github.com/pkg/errors v0.9.1 github.com/segmentio/asm v1.0.1 github.com/sirupsen/logrus v1.8.1 @@ -48,7 +49,6 @@ require ( github.com/leodido/go-urn v1.2.1 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/maruel/rs v1.0.0 // indirect - github.com/mattn/go-colorable v0.1.11 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect diff --git a/go.sum b/go.sum index 13869c1f0..ce2de6985 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,14 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82 h1:VAav2Z5n41cp2bjskooPXAg9W39qQeEEj3FmYlnS3+E= -github.com/Mrs4s/MiraiGo v0.0.0-20210916113136-0238b2382b82/go.mod h1:EpB6wQ+iaIqLRxVrs6si2J3XGv6wHXMrRXNUnJFaoww= github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a h1:D2zG/7w7q5QbDeHSx9n4lS9cv9MGBqEjNTzeVO5g3Mo= github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a/go.mod h1:EpB6wQ+iaIqLRxVrs6si2J3XGv6wHXMrRXNUnJFaoww= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= @@ -33,16 +31,14 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU= github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= @@ -60,7 +56,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -74,7 +69,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -82,8 +76,6 @@ github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/guonaihong/gout v0.2.4 h1:BlWpWWay/Q1LkyIwupEWBZE3PMl4xzzAgMHw+OrZxBs= -github.com/guonaihong/gout v0.2.4/go.mod h1:ISabiAAj0z1h3bOFUKzfRqPMvX0wmcYzIh6i4xIxMPo= github.com/guonaihong/gout v0.2.9 h1:8nU5hrtwP1qDwiadFvU+D+z3ud9WEk8iPSfxQDiebng= github.com/guonaihong/gout v0.2.9/go.mod h1:H1JqEuZmK4h/urWUq/LnIOEzS1kxl5rK3NkFqZ6Rn48= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= @@ -98,19 +90,17 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uia github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= @@ -120,15 +110,11 @@ github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkL github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= -github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 h1:u9jwvcKbQpghIXgNl/EOL8hzhAFXh4ePrEP493W3tNA= github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4/go.mod h1:kcRFpEzolcEklV6rD7W95mG49/sbdX/PlFmd7ni3RvA= github.com/maruel/rs v1.0.0 h1:NoRR74bfxQy8CzWdkwYb5Rz+H93sl30GSJIvf6ADZZg= github.com/maruel/rs v1.0.0/go.mod h1:F3JiIVIlJdcXIW+Ep10ZeiPAwKwSA5IQ6QTpILEE/aM= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -152,9 +138,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6O github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/segmentio/asm v1.0.0 h1:GMF7/2eLkte13LERSOmPI766AJXJDRlsqqiN8T7Mfmk= -github.com/segmentio/asm v1.0.0/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4= github.com/segmentio/asm v1.0.1 h1:g9VK62hXylgXI4yJV+dLTu/1j7kTxG9bkUSYBxL9dpg= github.com/segmentio/asm v1.0.1/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -175,13 +160,11 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264 h1:USf7 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264 h1:kGT41c6hLBFfiTjacWDpq62ePSnx1/9+MXrpMZII2Uk= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= -github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/gjson v1.9.1 h1:wrrRk7TyL7MmKanNRck/Mcr3VU1sdMvJHvJXzqBIUNo= github.com/tidwall/gjson v1.9.1/go.mod h1:jydLKE7s8J0+1/5jC4eXcuFlzKizGrCKvLmBVX/5oXc= github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -198,20 +181,17 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHg github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= @@ -226,7 +206,6 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -257,7 +236,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -265,7 +243,6 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -289,15 +266,14 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -319,8 +295,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= @@ -329,7 +305,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -338,7 +313,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= @@ -359,7 +333,6 @@ modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6 modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= -modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= @@ -374,12 +347,10 @@ modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= modernc.org/libc v1.11.22 h1:u2ek1qgzki0RUDI2kW3EC9VKHP9LXKxKnoQLZmIeCbA= modernc.org/libc v1.11.22/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= diff --git a/main.go b/main.go index 70f6e0834..965798972 100644 --- a/main.go +++ b/main.go @@ -106,8 +106,8 @@ func main() { panic(err) } - consoleFormatter := global.LogFormat{true} - fileFormatter := global.LogFormat{false} + consoleFormatter := global.LogFormat{EnableColor: true} + fileFormatter := global.LogFormat{EnableColor: false} log.AddHook(global.NewLocalHook(w, consoleFormatter, fileFormatter, global.GetLogLevel(conf.Output.LogLevel)...)) mkCacheDir := func(path string, _type string) { From d17181701632eb8e4a72c31207b2f7b876107904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 5 Oct 2021 04:54:34 +0800 Subject: [PATCH 069/612] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dlint=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/log_hook.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/global/log_hook.go b/global/log_hook.go index 93606aa2c..6fd7512b4 100644 --- a/global/log_hook.go +++ b/global/log_hook.go @@ -220,9 +220,12 @@ func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) { } // 为了不引入新依赖,直接将对应库需要的部分复制过来了,具体可参考 github.com\gookit\color@v1.4.2\color.go + +// ResetSet 重置色彩 ansi code const ResetSet = "\x1b[0m" const ( + // SettingTpl 开始色彩 ansi code SettingTpl = "\x1b[%sm" ) From bea69f04980bff95e83677f1d369ab254c103dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 5 Oct 2021 23:44:09 +0800 Subject: [PATCH 070/612] =?UTF-8?q?ocr=E5=A2=9E=E5=8A=A0=E8=AF=86=E5=88=AB?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/util.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qqrobot/util.go b/qqrobot/util.go index 2ab423a06..5ed9bb38d 100644 --- a/qqrobot/util.go +++ b/qqrobot/util.go @@ -12,6 +12,7 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" + "github.com/gookit/color" log "github.com/sirupsen/logrus" "github.com/Mrs4s/go-cqhttp/coolq" @@ -72,7 +73,10 @@ func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) (ocrResultSt for _, textDetection := range ocrResult.Texts { resultBuffer.WriteString(textDetection.Text) } - return resultBuffer.String() + ocrResultString = resultBuffer.String() + + logger.Debugf(bold(color.Yellow).Render(fmt.Sprintf("ocr ok image=%v result is:\n%v", groupImageElement.Url, ocrResultString))) + return ocrResultString } func (r *QQRobot) currentTime() string { From 84a7dbb4ff20002a3eebc9a981d4d36f5d21a843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 5 Oct 2021 23:47:08 +0800 Subject: [PATCH 071/612] =?UTF-8?q?error=E4=B9=9F=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=B7=B1=E7=BA=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/log_hook.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global/log_hook.go b/global/log_hook.go index 6fd7512b4..4374f6f9a 100644 --- a/global/log_hook.go +++ b/global/log_hook.go @@ -232,7 +232,7 @@ const ( var ( colorCodePanic = fmt.Sprintf(SettingTpl, "1;31") // color.Style{color.Bold, color.Red}.String() colorCodeFatal = fmt.Sprintf(SettingTpl, "1;31") // color.Style{color.Bold, color.Red}.String() - colorCodeError = fmt.Sprintf(SettingTpl, "31") // color.Style{color.Red}.String() + colorCodeError = fmt.Sprintf(SettingTpl, "1;31") // color.Style{color.Bold, color.Red}.String() colorCodeWarn = fmt.Sprintf(SettingTpl, "33") // color.Style{color.Yellow}.String() colorCodeInfo = fmt.Sprintf(SettingTpl, "32") // color.Style{color.Green}.String() colorCodeDebug = fmt.Sprintf(SettingTpl, "37") // color.Style{color.White}.String() From d9e13bc294a3c47b9a56e6fa58bfca96498dd22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 11:21:49 +0800 Subject: [PATCH 072/612] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E7=9A=84=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/update/update_others.go | 2 +- global/update/update_windows.go | 2 +- main.go | 19 +++++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/global/update/update_others.go b/global/update/update_others.go index 21d39c7c5..e3087b9e4 100644 --- a/global/update/update_others.go +++ b/global/update/update_others.go @@ -41,7 +41,7 @@ func Update(url string, sum []byte) error { if err != nil { return err } - if header.Name == "go-cqhttp" { + if header.Name == "qq_robot_go" { err, _ := FromStream(tr) fmt.Println() if err != nil { diff --git a/global/update/update_windows.go b/global/update/update_windows.go index 7fce75b83..bd27a88d1 100644 --- a/global/update/update_windows.go +++ b/global/update/update_windows.go @@ -28,7 +28,7 @@ func Update(url string, sum []byte) error { return errors.New("文件已损坏") } reader, _ := zip.NewReader(bytes.NewReader(rsp), resp.ContentLength) - file, err := reader.Open("go-cqhttp.exe") + file, err := reader.Open("qq_robot_go.exe") if err != nil { return err } diff --git a/main.go b/main.go index 965798972..c7232940d 100644 --- a/main.go +++ b/main.go @@ -469,6 +469,9 @@ func PasswordHashDecrypt(encryptedPasswordHash string, key []byte) ([]byte, erro return result, nil } +const githubRepo = "fzls/qq_robot_go" +const exeName = "qq_robot_go" + func checkUpdate() { log.Infof("正在检查更新.") if coolq.Version == "(devel)" { @@ -476,13 +479,13 @@ func checkUpdate() { return } var res string - if err := gout.GET("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest").BindBody(&res).Do(); err != nil { + if err := gout.GET(fmt.Sprintf("https://api.github.com/repos/%v/releases/latest", githubRepo)).BindBody(&res).Do(); err != nil { log.Warnf("检查更新失败: %v", err) return } info := gjson.Parse(res) if global.VersionNameCompare(coolq.Version, info.Get("tag_name").Str) { - log.Infof("当前有更新的 go-cqhttp 可供更新, 请前往 https://github.com/Mrs4s/go-cqhttp/releases 下载.") + log.Infof("当前有更新的 go-cqhttp 可供更新, 请前往 https://github.com/%v/releases 下载.", githubRepo) log.Infof("当前版本: %v 最新版本: %v", coolq.Version, info.Get("tag_name").Str) return } @@ -492,7 +495,7 @@ func checkUpdate() { func selfUpdate(imageURL string) { log.Infof("正在检查更新.") var res, r string - if err := gout.GET("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest").BindBody(&res).Do(); err != nil { + if err := gout.GET(fmt.Sprintf("https://api.github.com/repos/%v/releases/latest", githubRepo)).BindBody(&res).Do(); err != nil { log.Warnf("检查更新失败: %v", err) return } @@ -509,20 +512,20 @@ func selfUpdate(imageURL string) { log.Warn("已取消更新!") } else { log.Info("正在更新,请稍等...") - sumURL := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp_checksums.txt", + sumURL := fmt.Sprintf("%v/%v/releases/download/%v/%v_checksums.txt", func() string { if imageURL != "" { return imageURL } return "https://github.com" - }(), version) + }(), githubRepo, version, exeName) closer, err := global.HTTPGetReadCloser(sumURL) if err != nil { log.Error("更新失败: ", err) goto wait } rd := bufio.NewReader(closer) - binaryName := fmt.Sprintf("go-cqhttp_%v_%v.%v", runtime.GOOS, func() string { + binaryName := fmt.Sprintf("%v_%v_%v.%v", exeName, runtime.GOOS, func() string { if runtime.GOARCH == "arm" { return "armv7" } @@ -545,13 +548,13 @@ func selfUpdate(imageURL string) { break } } - url := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/%v", + url := fmt.Sprintf("%v/%v/releases/download/%v/%v", func() string { if imageURL != "" { return imageURL } return "https://github.com" - }(), version, binaryName) + }(), githubRepo, version, binaryName) err = update.Update(url, sum) if err != nil { From bde50da6a80df2406402be8c422a2ec540b4ea94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 11:22:00 +0800 Subject: [PATCH 073/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=80=E9=94=AE?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E8=84=9A=E6=9C=AC=E7=9A=84=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\346\234\272\345\231\250\344\272\272.lnk" | Bin 1245 -> 1327 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git "a/qqrobot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" "b/qqrobot/_\344\270\200\351\224\256\345\220\257\345\212\250\346\234\272\345\231\250\344\272\272.lnk" index 8a0c12240c30c17f5834297d20ca44d49b4cb345..470199b479ab5fb436a0133aff9ee84d373e2ae9 100644 GIT binary patch delta 61 zcmcc1xt?o+2;=9Cq8}I;4JNZO*-v(7QexDc%*SLdUBr;jkOagf3^5Fu49P%N9)li) KwK;>SlnDR_qz^v; delta 29 lcmZ3_b(eF32;=6Bq8}I;4JWfP*)zsZc41Q9yql?n2>_8C2>k#6 From 2827d484438e930f689a109bc33aed734c2f751c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 12:03:56 +0800 Subject: [PATCH 074/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=90=8D=E7=A7=B0=EF=BC=8C=E7=A7=BB=E9=99=A4?= =?UTF-8?q?default=5F=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 +- README.md | 2 +- global/config/config.go | 2 +- global/config/default_config.yml | 86 ----------------------- qqrobot/default_config.toml | 117 ------------------------------- 5 files changed, 4 insertions(+), 207 deletions(-) delete mode 100644 global/config/default_config.yml delete mode 100644 qqrobot/default_config.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32dd99359..a5fc583a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,8 +57,8 @@ jobs: go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . - cp global/config/default_config.yml output/config.yml - cp qqrobot/default_config.toml output/config.toml + cp global/config/config.yml output/config.yml + cp qqrobot/config.toml output/config.toml - name: Upload artifact uses: actions/upload-artifact@v2 diff --git a/README.md b/README.md index 6ae3b078a..cde2ae555 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## 配置自动回复逻辑 -复制qqrobot/default_config.toml到qq_robot.exe所在目录,并重命名为config.toml 然后打开qq_robot/config.go和config.toml,按照注释,自行调整配置,并添加各种规则 +复制qqrobot/config.toml到qq_robot.exe所在目录 然后打开qq_robot/config.go和config.toml,按照注释,自行调整配置,并添加各种规则 # TODO diff --git a/global/config/config.go b/global/config/config.go index b99e41a40..ad0d750ed 100644 --- a/global/config/config.go +++ b/global/config/config.go @@ -18,7 +18,7 @@ import ( ) // defaultConfig 默认配置文件 -//go:embed default_config.yml +//go:embed config.yml var defaultConfig string var currentPath = getCurrentPath() diff --git a/global/config/default_config.yml b/global/config/default_config.yml deleted file mode 100644 index 016ac4b2a..000000000 --- a/global/config/default_config.yml +++ /dev/null @@ -1,86 +0,0 @@ -# go-cqhttp 默认配置文件 - -account: # 账号相关 - uin: 1233456 # QQ账号 - password: '' # 密码为空时使用扫码登录 - encrypt: false # 是否开启密码加密 - status: 0 # 在线状态 请参考 https://docs.go-cqhttp.org/guide/config.html#在线状态 - relogin: # 重连设置 - delay: 3 # 首次重连延迟, 单位秒 - interval: 3 # 重连间隔 - max-times: 0 # 最大重连次数, 0为无限制 - - # 是否使用服务器下发的新地址进行重连 - # 注意, 此设置可能导致在海外服务器上连接情况更差 - use-sso-address: true - - fast-start: true # 是否快速启动 - -heartbeat: - # 心跳频率, 单位秒 - # -1 为关闭心跳 - interval: 5 - -message: - # 上报数据类型 - # 可选: string,array - post-format: string - # 是否忽略无效的CQ码, 如果为假将原样发送 - ignore-invalid-cqcode: false - # 是否强制分片发送消息 - # 分片发送将会带来更快的速度 - # 但是兼容性会有些问题 - force-fragment: false - # 是否将url分片发送 - fix-url: false - # 下载图片等请求网络代理 - proxy-rewrite: '' - # 是否上报自身消息 - report-self-message: false - # 移除服务端的Reply附带的At - remove-reply-at: false - # 为Reply附加更多信息 - extra-reply-data: false - # 跳过 Mime 扫描, 忽略错误数据 - skip-mime-scan: false - -output: - # 日志等级 trace,debug,info,warn,error - log-level: warn - # 日志时效 单位天. 超过这个时间之前的日志将会被自动删除. 设置为 0 表示永久保留. - log-aging: 15 - # 是否在每次启动时强制创建全新的文件储存日志. 为 false 的情况下将会在上次启动时创建的日志文件续写 - log-force-new: true - # 是否启用 DEBUG - debug: false # 开启调试模式 - -# 默认中间件锚点 -default-middlewares: &default - # 访问密钥, 强烈推荐在公网的服务器设置 - access-token: '' - # 事件过滤器文件目录 - filter: '' - # API限速设置 - # 该设置为全局生效 - # 原 cqhttp 虽然启用了 rate_limit 后缀, 但是基本没插件适配 - # 目前该限速设置为令牌桶算法, 请参考: - # https://baike.baidu.com/item/%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95/6597000?fr=aladdin - rate-limit: - enabled: false # 是否启用限速 - frequency: 1 # 令牌回复频率, 单位秒 - bucket: 1 # 令牌桶大小 - -database: # 数据库相关设置 - leveldb: - # 是否启用内置leveldb数据库 - # 启用将会增加10-20MB的内存占用和一定的磁盘空间 - # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 - enable: true - -# 连接服务列表 -servers: - # 添加方式,同一连接方式可添加多个,具体配置说明请查看文档 - #- http: # http 通信 - #- ws: # 正向 Websocket - #- ws-reverse: # 反向 Websocket - #- pprof: #性能分析服务器 diff --git a/qqrobot/default_config.toml b/qqrobot/default_config.toml deleted file mode 100644 index 72ad35778..000000000 --- a/qqrobot/default_config.toml +++ /dev/null @@ -1,117 +0,0 @@ -### ------------机器人配置------------------ -[robot] -# http请求超时 -timeout = 15 -# 调试模式 -debug = false -# 单条消息处理失败后,最多重试次数 -# 先设置一次,避免出现异常多次发放消息,做个保底 -max_retry_times = 0 -# 最大允许的连续空行数目,为0则不限制 -max_continue_empty_lines = 10000 -# 腾讯ai开放平台的应用ID -tencent_ai_app_id = "" -# 腾讯ai开放平台的应用秘钥 -tencent_ai_app_key = "" -# 聊天结果未找到时的提示语 -chat_answer_not_found_message = "我还小,听不懂你这些骚话(〃'▽'〃)" -# 不支持私聊时的提示语 -personal_message_not_supported_message = "" -# 不支持私聊时的图片 -personal_message_not_supported_image = "" - -# 机器人上线时的操作 -[robot.on_start] -# 操作名称 -name = "机器人上线" -# 通知的群 -notify_groups = [123456] -# 通知的消息 -message = """你们的小可爱我开始上岗啦●ヽ(゚∀゚)ノ● """ - -# 机器人下线时的操作 -[robot.on_stop] -# 操作名称 -name = "机器人下线" -# 通知的群 -notify_groups = [123456] -# 通知的消息,参数:$work_time$=本次工作时长 -message = """又是辛勤工作的一天呢,今天一共工作了$work_time$,累坏啦o(╥﹏╥)o 下班回家睡觉觉去啦(。◕ˇ∀ˇ◕),大家不要太想我哦。◕ᴗ◕。""" - -[misc] - -### ------------规则类型配置------------------ -[[rule_types]] -# 规则类别 -type = "自动回复" -# 同一条消息最多应用该类型的规则的数目,-1表示不限制 -type_max_apply_count = 1 - -[[rule_types]] -# 规则类别 -type = "机器人指令" -# 同一条消息最多应用该类型的规则的数目,-1表示不限制 -type_max_apply_count = -1 - -[[rule_types]] -# 规则类别 -type = "AT某人" -# 同一条消息最多应用该类型的规则的数目,-1表示不限制 -type_max_apply_count = -1 - -[[rule_types]] -# 规则类别 -type = "深夜美食" -# 同一条消息最多应用该类型的规则的数目,-1表示不限制 -type_max_apply_count = 1 - -[[rule_types]] -# 规则类别 -type = "重要通知" -# 同一条消息最多应用该类型的规则的数目,-1表示不限制 -type_max_apply_count = -1 - -[[rule_types]] -# 规则类别 -type = "测试" -# 同一条消息最多应用该类型的规则的数目,-1表示不限制 -type_max_apply_count = -1 - -### ------------规则类型配置------------------ -[[rule_type_configs]] -# 群类别 -type = "小号" -# 归属该类别的群组id列表 -group_ids = [123456, 789101] - - -[[rules]] -# 规则名称 -name = "测试规则" -# 规则类别 -type = "自动回复" -# 适用的QQ群ID列表 -group_ids = [123456] -# 适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 -group_types = ["小号"] -# 适用的关键词列表 -keywords = [] -# 排除的一些关键词列表 -exclude_keywords = [] -# 排除的QQ列表 -exclude_qqs = [] -# 动作 -action = "guide" -# 内容 -guide_content = """测试回复消息 -""" -# 图片URL,若有,则会额外附加图片 -image_url = "" -# 是否撤回该条消息 -revoke_message = true -# 禁言时间,为0则表示不禁言(单位为秒) -mute_time = 600 -# TriggerRuleDuration内触发的规则数目是否超过该数目 -trigger_rule_count = 5 -# 判定恶意触发机器人规则的时间周期(秒) -trigger_rule_duration = 60 From 3224b9e7a6f30d85ce556fbde041f187a7afc24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 12:04:51 +0800 Subject: [PATCH 075/612] =?UTF-8?q?=E8=B0=83=E6=95=B4goreleaser=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8Cwin=20x86/64=E4=B8=8B=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=9B=BE=E7=89=87=EF=BC=8C=E5=B9=B6=E5=9C=A8?= =?UTF-8?q?=E6=89=93=E5=8C=85=E7=9A=84exe=E4=B8=AD=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .goreleaser.yml | 53 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index a04497f08..243c81a05 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -4,36 +4,51 @@ before: hooks: - go mod tidy builds: - - id: nowin + - id: win-x86/64 env: - CGO_ENABLED=0 - GO111MODULE=on goos: - - linux - - darwin + - windows goarch: - 386 - amd64 + goarm: + - 7 + mod_timestamp: "{{ .CommitTimestamp }}" + flags: + - -trimpath + ldflags: + - -s -w -X github.com/Mrs4s/go-cqhttp/coolq.Version=v{{.Version}} + hooks: + pre: + - go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest + - go generate . + post: + - rm -f resource.syso + - id: win-arm + env: + - CGO_ENABLED=0 + - GO111MODULE=on + goos: + - windows + goarch: - arm - arm64 goarm: - 7 - ignore: - - goos: darwin - goarch: arm - - goos: darwin - goarch: 386 mod_timestamp: "{{ .CommitTimestamp }}" flags: - -trimpath ldflags: - -s -w -X github.com/Mrs4s/go-cqhttp/coolq.Version=v{{.Version}} - - id: win + - id: nowin env: - CGO_ENABLED=0 - GO111MODULE=on goos: - - windows + - linux + - darwin goarch: - 386 - amd64 @@ -41,6 +56,11 @@ builds: - arm64 goarm: - 7 + ignore: + - goos: darwin + goarch: arm + - goos: darwin + goarch: 386 mod_timestamp: "{{ .CommitTimestamp }}" flags: - -trimpath @@ -64,7 +84,8 @@ changelog: archives: - id: binary builds: - - win + - win-x86/64 + - win-arm name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" format_overrides: - goos: windows @@ -72,11 +93,19 @@ archives: - id: nowin builds: - nowin - - win + - win-x86/64 + - win-arm name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" format_overrides: - goos: windows format: zip + files: + - src: global/config/config.yml + dst: . + strip_parent: true + - src: qqrobot/config.toml + dst: . + strip_parent: true nfpms: - license: AGPL 3.0 From e80567639e3412b394d82099e1bb0731bcc143c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 12:09:28 +0800 Subject: [PATCH 076/612] =?UTF-8?q?=E6=BC=8F=E6=8F=90=E4=BA=A4=E6=94=B9?= =?UTF-8?q?=E5=90=8D=E5=90=8E=E7=9A=84=E6=A8=A1=E6=9D=BF=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- global/config/config.yml | 86 ++++++++++++++++++++++++++++ qqrobot/config.toml | 117 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 global/config/config.yml create mode 100644 qqrobot/config.toml diff --git a/.gitignore b/.gitignore index 8c0559b9d..c193f3538 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ data/ logs/ *.exe -resource.syso \ No newline at end of file +resource.syso +!global/config/config.yml +!qqrobot/config.toml diff --git a/global/config/config.yml b/global/config/config.yml new file mode 100644 index 000000000..016ac4b2a --- /dev/null +++ b/global/config/config.yml @@ -0,0 +1,86 @@ +# go-cqhttp 默认配置文件 + +account: # 账号相关 + uin: 1233456 # QQ账号 + password: '' # 密码为空时使用扫码登录 + encrypt: false # 是否开启密码加密 + status: 0 # 在线状态 请参考 https://docs.go-cqhttp.org/guide/config.html#在线状态 + relogin: # 重连设置 + delay: 3 # 首次重连延迟, 单位秒 + interval: 3 # 重连间隔 + max-times: 0 # 最大重连次数, 0为无限制 + + # 是否使用服务器下发的新地址进行重连 + # 注意, 此设置可能导致在海外服务器上连接情况更差 + use-sso-address: true + + fast-start: true # 是否快速启动 + +heartbeat: + # 心跳频率, 单位秒 + # -1 为关闭心跳 + interval: 5 + +message: + # 上报数据类型 + # 可选: string,array + post-format: string + # 是否忽略无效的CQ码, 如果为假将原样发送 + ignore-invalid-cqcode: false + # 是否强制分片发送消息 + # 分片发送将会带来更快的速度 + # 但是兼容性会有些问题 + force-fragment: false + # 是否将url分片发送 + fix-url: false + # 下载图片等请求网络代理 + proxy-rewrite: '' + # 是否上报自身消息 + report-self-message: false + # 移除服务端的Reply附带的At + remove-reply-at: false + # 为Reply附加更多信息 + extra-reply-data: false + # 跳过 Mime 扫描, 忽略错误数据 + skip-mime-scan: false + +output: + # 日志等级 trace,debug,info,warn,error + log-level: warn + # 日志时效 单位天. 超过这个时间之前的日志将会被自动删除. 设置为 0 表示永久保留. + log-aging: 15 + # 是否在每次启动时强制创建全新的文件储存日志. 为 false 的情况下将会在上次启动时创建的日志文件续写 + log-force-new: true + # 是否启用 DEBUG + debug: false # 开启调试模式 + +# 默认中间件锚点 +default-middlewares: &default + # 访问密钥, 强烈推荐在公网的服务器设置 + access-token: '' + # 事件过滤器文件目录 + filter: '' + # API限速设置 + # 该设置为全局生效 + # 原 cqhttp 虽然启用了 rate_limit 后缀, 但是基本没插件适配 + # 目前该限速设置为令牌桶算法, 请参考: + # https://baike.baidu.com/item/%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95/6597000?fr=aladdin + rate-limit: + enabled: false # 是否启用限速 + frequency: 1 # 令牌回复频率, 单位秒 + bucket: 1 # 令牌桶大小 + +database: # 数据库相关设置 + leveldb: + # 是否启用内置leveldb数据库 + # 启用将会增加10-20MB的内存占用和一定的磁盘空间 + # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 + enable: true + +# 连接服务列表 +servers: + # 添加方式,同一连接方式可添加多个,具体配置说明请查看文档 + #- http: # http 通信 + #- ws: # 正向 Websocket + #- ws-reverse: # 反向 Websocket + #- pprof: #性能分析服务器 diff --git a/qqrobot/config.toml b/qqrobot/config.toml new file mode 100644 index 000000000..72ad35778 --- /dev/null +++ b/qqrobot/config.toml @@ -0,0 +1,117 @@ +### ------------机器人配置------------------ +[robot] +# http请求超时 +timeout = 15 +# 调试模式 +debug = false +# 单条消息处理失败后,最多重试次数 +# 先设置一次,避免出现异常多次发放消息,做个保底 +max_retry_times = 0 +# 最大允许的连续空行数目,为0则不限制 +max_continue_empty_lines = 10000 +# 腾讯ai开放平台的应用ID +tencent_ai_app_id = "" +# 腾讯ai开放平台的应用秘钥 +tencent_ai_app_key = "" +# 聊天结果未找到时的提示语 +chat_answer_not_found_message = "我还小,听不懂你这些骚话(〃'▽'〃)" +# 不支持私聊时的提示语 +personal_message_not_supported_message = "" +# 不支持私聊时的图片 +personal_message_not_supported_image = "" + +# 机器人上线时的操作 +[robot.on_start] +# 操作名称 +name = "机器人上线" +# 通知的群 +notify_groups = [123456] +# 通知的消息 +message = """你们的小可爱我开始上岗啦●ヽ(゚∀゚)ノ● """ + +# 机器人下线时的操作 +[robot.on_stop] +# 操作名称 +name = "机器人下线" +# 通知的群 +notify_groups = [123456] +# 通知的消息,参数:$work_time$=本次工作时长 +message = """又是辛勤工作的一天呢,今天一共工作了$work_time$,累坏啦o(╥﹏╥)o 下班回家睡觉觉去啦(。◕ˇ∀ˇ◕),大家不要太想我哦。◕ᴗ◕。""" + +[misc] + +### ------------规则类型配置------------------ +[[rule_types]] +# 规则类别 +type = "自动回复" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = 1 + +[[rule_types]] +# 规则类别 +type = "机器人指令" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = -1 + +[[rule_types]] +# 规则类别 +type = "AT某人" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = -1 + +[[rule_types]] +# 规则类别 +type = "深夜美食" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = 1 + +[[rule_types]] +# 规则类别 +type = "重要通知" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = -1 + +[[rule_types]] +# 规则类别 +type = "测试" +# 同一条消息最多应用该类型的规则的数目,-1表示不限制 +type_max_apply_count = -1 + +### ------------规则类型配置------------------ +[[rule_type_configs]] +# 群类别 +type = "小号" +# 归属该类别的群组id列表 +group_ids = [123456, 789101] + + +[[rules]] +# 规则名称 +name = "测试规则" +# 规则类别 +type = "自动回复" +# 适用的QQ群ID列表 +group_ids = [123456] +# 适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 +group_types = ["小号"] +# 适用的关键词列表 +keywords = [] +# 排除的一些关键词列表 +exclude_keywords = [] +# 排除的QQ列表 +exclude_qqs = [] +# 动作 +action = "guide" +# 内容 +guide_content = """测试回复消息 +""" +# 图片URL,若有,则会额外附加图片 +image_url = "" +# 是否撤回该条消息 +revoke_message = true +# 禁言时间,为0则表示不禁言(单位为秒) +mute_time = 600 +# TriggerRuleDuration内触发的规则数目是否超过该数目 +trigger_rule_count = 5 +# 判定恶意触发机器人规则的时间周期(秒) +trigger_rule_duration = 60 From 2d6d86956761a45a97742cd38772c831274c50c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 13:15:24 +0800 Subject: [PATCH 077/612] =?UTF-8?q?=E7=A7=BB=E9=99=A4resource.syso?= =?UTF-8?q?=E7=9A=84=E6=B5=81=E7=A8=8B=E6=94=BE=E5=88=B0=E5=85=B6=E4=BB=96?= =?UTF-8?q?build=E7=9A=84pre=20hooks=E7=9A=84=E5=9C=B0=E6=96=B9=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8Dpost=E4=B8=ADx86=E5=92=8C64=E5=85=88=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E4=B8=80=E4=B8=AA=E5=90=8E=E5=8F=A6=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=AD=A3=E5=B8=B8=E7=BC=96=E8=AF=91=EF=BC=88?= =?UTF-8?q?=E5=9B=A0=E4=B8=BAgoreleaser=E4=BC=BC=E4=B9=8E=E5=8D=95?= =?UTF-8?q?=E4=B8=AAbuild=E5=86=85=E7=9A=84target=E5=B9=B6=E8=A1=8C?= =?UTF-8?q?=E5=A4=84=E7=90=86=EF=BC=8C=E4=B8=8D=E5=90=8Cbuild=E5=88=99?= =?UTF-8?q?=E6=98=AF=E4=B8=B2=E8=A1=8C=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .goreleaser.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 243c81a05..570fa578e 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -24,8 +24,6 @@ builds: pre: - go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest - go generate . - post: - - rm -f resource.syso - id: win-arm env: - CGO_ENABLED=0 @@ -42,6 +40,8 @@ builds: - -trimpath ldflags: - -s -w -X github.com/Mrs4s/go-cqhttp/coolq.Version=v{{.Version}} + hooks: + pre: rm -f resource.syso - id: nowin env: - CGO_ENABLED=0 @@ -66,6 +66,8 @@ builds: - -trimpath ldflags: - -s -w -X github.com/Mrs4s/go-cqhttp/coolq.Version=v{{.Version}} + hooks: + pre: rm -f resource.syso checksum: name_template: "{{ .ProjectName }}_checksums.txt" From 0d4b1184334ea79dd1ade036a50f94c1ab0a3ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 13:28:34 +0800 Subject: [PATCH 078/612] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=97=B6=E7=9A=84=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index c7232940d..4c2ac2b2b 100644 --- a/main.go +++ b/main.go @@ -485,7 +485,7 @@ func checkUpdate() { } info := gjson.Parse(res) if global.VersionNameCompare(coolq.Version, info.Get("tag_name").Str) { - log.Infof("当前有更新的 go-cqhttp 可供更新, 请前往 https://github.com/%v/releases 下载.", githubRepo) + log.Infof("当前有更新的 %v 可供更新, 请前往 https://github.com/%v/releases 下载.", exeName, githubRepo) log.Infof("当前版本: %v 最新版本: %v", coolq.Version, info.Get("tag_name").Str) return } From 87dad924ac06afd51bba751ae7d81b85af51f898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 13:29:25 +0800 Subject: [PATCH 079/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0go-cqhttp=E7=9A=84?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 4c2ac2b2b..bcad151c6 100644 --- a/main.go +++ b/main.go @@ -427,6 +427,7 @@ func main() { log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") + log.Info("以下内容基于 go-cqhttp 魔改而成") log.Info("启动本地的机器人处理程序,用于直接收发消息和自动回复") robot := qqrobot.NewQQRobot(bot, "config.toml") robot.RegisterHandlers() From b8450bbec119ace04b463f8e8752a09aaf3180b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 13:32:02 +0800 Subject: [PATCH 080/612] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BD=B2=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index bcad151c6..0c409bada 100644 --- a/main.go +++ b/main.go @@ -427,7 +427,7 @@ func main() { log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") - log.Info("以下内容基于 go-cqhttp 魔改而成") + log.Info("以下内容基于 go-cqhttp 魔改而成,powered by 风之凌殇") log.Info("启动本地的机器人处理程序,用于直接收发消息和自动回复") robot := qqrobot.NewQQRobot(bot, "config.toml") robot.RegisterHandlers() From d3dda217f8a3516bcedafee72775f476148c64af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 13:46:16 +0800 Subject: [PATCH 081/612] =?UTF-8?q?=E8=B0=83=E6=95=B4ocr=E7=9A=84=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E7=BA=A7=E5=88=AB=E4=B8=BAinfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/util.go b/qqrobot/util.go index 5ed9bb38d..46f2e20bc 100644 --- a/qqrobot/util.go +++ b/qqrobot/util.go @@ -75,7 +75,7 @@ func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) (ocrResultSt } ocrResultString = resultBuffer.String() - logger.Debugf(bold(color.Yellow).Render(fmt.Sprintf("ocr ok image=%v result is:\n%v", groupImageElement.Url, ocrResultString))) + logger.Infof(bold(color.Yellow).Render(fmt.Sprintf("ocr ok image=%v result is:\n%v", groupImageElement.Url, ocrResultString))) return ocrResultString } From c988c085aef3642c94d9ed93bd39b08469226450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 13:53:51 +0800 Subject: [PATCH 082/612] =?UTF-8?q?=E6=9B=B4=E5=A4=9A=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=A4=84=E8=B0=83=E6=95=B4=E4=B8=BAjson?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 1d7330276..89a01a078 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -121,10 +121,10 @@ func (r *QQRobot) notify(cfg NotifyConfig) { } retCode := r.cqBot.SendGroupMessage(groupID, msg) if retCode == -1 { - logger.Errorf("【%v Failed】 %v groupID=%v message=%v err=%v", cfg.Name, nowStr, groupID, msg, retCode) + logger.Errorf("【%v Failed】 %v groupID=%v message=%v err=%v", cfg.Name, nowStr, groupID, p(msg), retCode) return } - logger.Infof("【%v】 %v groupID=%v message=%v", cfg.Name, nowStr, groupID, msg) + logger.Infof("【%v】 %v groupID=%v message=%v", cfg.Name, nowStr, groupID, p(msg)) } logger.Infof("robot on %v finished", cfg.Name) } @@ -533,10 +533,10 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { for _, repeatToGroup := range config.RepeatToGroups { forwardRspID := r.cqBot.SendGroupMessage(repeatToGroup, repeatMessages) if forwardRspID == -1 { - logger.Error(fmt.Sprintf("【RepeatToGroup(%v) Failed】", repeatToGroup), nowStr, config.Name, repeatMessages, forwardRspID) + logger.Error(fmt.Sprintf("【RepeatToGroup(%v) Failed】", repeatToGroup), nowStr, config.Name, p(repeatMessages), forwardRspID) continue } - logger.Info(fmt.Sprintf("【RepeatToGroup(%v)】", repeatToGroup), nowStr, config.Name, repeatMessages, forwardRspID) + logger.Info(fmt.Sprintf("【RepeatToGroup(%v)】", repeatToGroup), nowStr, config.Name, p(repeatMessages), forwardRspID) } } } else { @@ -574,10 +574,10 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { rspID := r.cqBot.SendGroupMessage(groupID, replies) if rspID == -1 { - logger.Error("【ReplyFail】", nowStr, config.Name, keyWord, p(m), rspID) + logger.Error("【ReplyFail】", nowStr, config.Name, keyWord, p(m), p(replies), rspID) return err } - logger.Info(color.Style{color.Bold, color.Green}.Renderln("【OK】", nowStr, config.Name, keyWord, p(m), source, replies, rspID)) + logger.Info(color.Style{color.Bold, color.Green}.Renderln("【OK】", nowStr, config.Name, keyWord, p(m), source, p(replies), rspID)) } if config.RevokeMessage { @@ -607,18 +607,18 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { for _, forwardToQQ := range config.ForwardToQQs { forwardRspID := r.cqBot.SendPrivateMessage(forwardToQQ, 0, forwardMessages) if forwardRspID == -1 { - logger.Error(fmt.Sprintf("【ForwardToQQ(%v) Failed】", forwardToQQ), nowStr, config.Name, forwardMessages, forwardRspID) + logger.Error(fmt.Sprintf("【ForwardToQQ(%v) Failed】", forwardToQQ), nowStr, config.Name, p(forwardMessages), forwardRspID) continue } - logger.Info(fmt.Sprintf("【ForwardToQQ(%v)】", forwardToQQ), nowStr, config.Name, forwardMessages, forwardRspID) + logger.Info(fmt.Sprintf("【ForwardToQQ(%v)】", forwardToQQ), nowStr, config.Name, p(forwardMessages), forwardRspID) } for _, forwardToGroup := range config.ForwardToGroups { forwardRspID := r.cqBot.SendGroupMessage(forwardToGroup, forwardMessages) if forwardRspID == -1 { - logger.Error(fmt.Sprintf("【ForwardToGroup(%v) Failed】", forwardToGroup), nowStr, config.Name, forwardMessages, forwardRspID) + logger.Error(fmt.Sprintf("【ForwardToGroup(%v) Failed】", forwardToGroup), nowStr, config.Name, p(forwardMessages), forwardRspID) continue } - logger.Info(fmt.Sprintf("【ForwardToGroup(%v)】", forwardToGroup), nowStr, config.Name, forwardMessages, forwardRspID) + logger.Info(fmt.Sprintf("【ForwardToGroup(%v)】", forwardToGroup), nowStr, config.Name, p(forwardMessages), forwardRspID) } } } @@ -818,7 +818,7 @@ func (r *QQRobot) onMemberJoin(m *client.MemberJoinGroupEvent, rule *Rule) error logger.Error("【ReplyFail】", nowStr, p(m), rspID) return errors.Errorf("reply fail, rspID=%v", rspID) } - logger.Info("【OK】", nowStr, p(m.Group), 0, (replies), rspID) + logger.Info("【OK】", nowStr, p(m.Group), 0, p(replies), rspID) } if muteTime := config.MuteTime; muteTime != 0 { @@ -859,8 +859,8 @@ func (r *QQRobot) onPrivateOrTempMessage(senderFriendUin int64, tempGroupID int6 } if rspID == -1 { - logger.Error("【ReplyFail】", p(m), rspID) + logger.Error("【ReplyFail】", p(m), p(replies), rspID) return } - logger.Info("【OK】", p(m), 0, replies, rspID) + logger.Info("【OK】", p(m), 0, p(replies), rspID) } From a4ffbec43bda62fa7748cba1a9bed7c6349d8ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 8 Oct 2021 16:51:33 +0800 Subject: [PATCH 083/612] =?UTF-8?q?=E8=BD=AC=E5=8F=91=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9C=A8@all=E5=92=8C=E5=90=8E=E7=BB=AD?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E4=B9=8B=E9=97=B4=E5=A2=9E=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E7=A9=BA=E6=A0=BC=EF=BC=8C=E9=81=BF=E5=85=8D=E8=BF=9E?= =?UTF-8?q?=E5=88=B0=E4=B8=80=E8=B5=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 89a01a078..c03b26731 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -526,8 +526,12 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } for idx, repeatMessages := range r.getForwardMessagesList(m, true) { if idx == 0 { - // 第一条转发的消息加上 @all - repeatMessages.Elements = append([]message.IMessageElement{message.AtAll()}, repeatMessages.Elements...) + repeatMessages.Elements = append([]message.IMessageElement{ + // 第一条转发的消息加上 @all + message.AtAll(), + // 再补上一个空格,避免连到一起 + message.NewText(" "), + }, repeatMessages.Elements...) } for _, repeatToGroup := range config.RepeatToGroups { From c4d10a6c3d3b73bf1d8257f69d2280729e8a4732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 9 Oct 2021 17:21:25 +0800 Subject: [PATCH 084/612] =?UTF-8?q?fix:=20ocr=E5=87=BA=E9=94=99=E6=97=B6?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E6=89=93=E5=8D=B0url=EF=BC=8C=E8=80=8C?= =?UTF-8?q?=E4=B8=8D=E6=98=AF=E6=95=B4=E4=B8=AA=E7=BB=93=E6=9E=84=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/util.go b/qqrobot/util.go index 46f2e20bc..7a71fde23 100644 --- a/qqrobot/util.go +++ b/qqrobot/util.go @@ -65,7 +65,7 @@ func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) (ocrResultSt ocrResult, err := r.cqBot.Client.ImageOcr(groupImageElement) if err != nil { - logger.Errorf("ocr出错了,image=%+v,err=%v", groupImageElement, err) + logger.Errorf("ocr出错了,image=%+v,err=%v", groupImageElement.Url, err) return "" } From 0a492f364faa202522f2b850b2a3ca86037c565f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 31 Oct 2021 16:00:45 +0800 Subject: [PATCH 085/612] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=BB=93?= =?UTF-8?q?=E7=AE=97=E9=80=9A=E7=9F=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_settlements.go | 122 +++++++++++++++++++++++++++++++++++ qqrobot/config.go | 12 ++++ qqrobot/qq_robot.go | 31 ++++++++- 3 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 qqrobot/check_settlements.go diff --git a/qqrobot/check_settlements.go b/qqrobot/check_settlements.go new file mode 100644 index 000000000..b6691c054 --- /dev/null +++ b/qqrobot/check_settlements.go @@ -0,0 +1,122 @@ +package qqrobot + +import ( + "encoding/json" + "io/ioutil" + "strings" + "time" + + "github.com/Mrs4s/MiraiGo/message" + "github.com/pkg/errors" +) + +type SettleResponse struct { + Code int `json:"code"` + Msg string `json:"msg"` + Pid int `json:"pid"` + Key string `json:"key"` + Type interface{} `json:"type"` + Data []SettleInfo `json:"data"` +} + +type SettleInfo struct { + Id string `json:"id"` + Uid string `json:"uid"` + Batch interface{} `json:"batch"` + Auto string `json:"auto"` + Type string `json:"type"` + Account string `json:"account"` + UserName string `json:"username"` + Money string `json:"money"` + RealMoney string `json:"realmoney"` + AddTime string `json:"addtime"` + EndTime string `json:"endtime"` + Status string `json:"status"` + TransferStatus string `json:"transfer_status"` + TransferResult interface{} `json:"transfer_result"` + TransferDate interface{} `json:"transfer_date"` + Result interface{} `json:"result"` +} + +func (r *QQRobot) checkSettlements() { + // 每天大概00:00:01的时候开始结算,12:00:00后某个时间完成结算 + now := time.Now() + h, m, s := now.Clock() + todaySettleStartTime := now.Add(-time.Duration(3600*h+60*m+s) * time.Second).Truncate(time.Second) + todaySettleFinishTime := todaySettleStartTime.Add(12 * time.Hour) + + // 下列情况需要尝试处理 + // 1. 过了开始结算时间,但是今天尚未开始结算 + // 2. 过了完成结算时间,但是今天尚未完成结算 + needProcess := now.After(todaySettleStartTime) && r.lastSettleStartTime.Before(todaySettleStartTime) || + now.After(todaySettleFinishTime) && r.lastSettleFinishTime.Before(todaySettleStartTime) + if !needProcess { + return + } + + settleInfo, err := r.getLatestSettleInfo() + if err != nil { + return + } + + if r.lastSettleStartTime.Before(todaySettleStartTime) && settleInfo.AddTime != "" { + startTime, _ := time.Parse("2006-01-02 15:04:05", settleInfo.AddTime) + if startTime.Before(todaySettleStartTime) { + // 不是今天的结算记录 + return + } + + // 发送结算消息 + reply := message.NewSendingMessage() + msg := r.Config.NotifySettle.StartMessage + msg = strings.ReplaceAll(msg, templateargsRealMoney, settleInfo.RealMoney) + msg = strings.ReplaceAll(msg, templateargsSettleTime, settleInfo.AddTime) + reply.Append(message.NewText(msg)) + r.cqBot.SendPrivateMessage(r.Config.NotifySettle.NotifyQQ, 0, reply) + + // 更新通知时间 + r.lastSettleStartTime = startTime + } + + if r.lastSettleFinishTime.Before(todaySettleStartTime) && settleInfo.EndTime != "" { + endTime, _ := time.Parse("2006-01-02 15:04:05", settleInfo.EndTime) + if endTime.Before(todaySettleStartTime) { + // 不是今天的结算记录 + return + } + + // 发送结算消息 + reply := message.NewSendingMessage() + msg := r.Config.NotifySettle.StartMessage + msg = strings.ReplaceAll(msg, templateargsRealMoney, settleInfo.RealMoney) + msg = strings.ReplaceAll(msg, templateargsSettleTime, settleInfo.EndTime) + reply.Append(message.NewText(msg)) + r.cqBot.SendPrivateMessage(r.Config.NotifySettle.NotifyQQ, 0, reply) + + // 更新通知时间 + r.lastSettleStartTime = endTime + } +} + +// getLatestSettleInfo 获取最近的结算信息 +func (r *QQRobot) getLatestSettleInfo() (*SettleInfo, error) { + resp, err := r.httpClient.Get(r.Config.NotifySettle.ApiUrl) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + bytesData, _ := ioutil.ReadAll(resp.Body) + + var settleResponse SettleResponse + err = json.Unmarshal(bytesData, &settleResponse) + if err != nil { + return nil, err + } + + if len(settleResponse.Data) == 0 { + return nil, errors.Errorf("empty result") + } + + return &settleResponse.Data[0], nil +} diff --git a/qqrobot/config.go b/qqrobot/config.go index ed9a2eba5..2fe7ef65e 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -20,6 +20,8 @@ const ( templateargsCd = "$cd$" // CD templateargsGitversion = "$git_version$" // 代码版本,若对应规则配置了changelog的链接,则会将这个变量替换为解析出的最新的版本号,如https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD templateargsUpdatemessage = "$update_message$" // 最新更新信息,若对应规则配置了changelog的链接,则会将这个变量替换为解析出的最新的更新信息,如https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD + templateargsRealMoney = "$real_money$" // 结算金额 + templateargsSettleTime = "$settle_time$" // 对应结算操作的时间 ) // NotifyConfig 通知规则 @@ -185,6 +187,15 @@ type NotifyUpdateRule struct { AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 是否需要@全体成员 } +// NotifySettleConfig 通知结算配置 +type NotifySettleConfig struct { + CheckInterval int64 `toml:"check_interval"` // 检查更新的间隔(秒),建议3600s + ApiUrl string `toml:"api_url"` // 结算api的url + NotifyQQ int64 `toml:"notify_qq"` // 要通知的QQ + StartMessage string `toml:"start_message"` // 开始结算的消息,参数:$real_money$=结算金额, $settle_time$=对应结算操作的时间 + FinishMessage string `toml:"end_message"` // 完成结算的消息,参数:$real_money$=结算金额, $settle_time$=对应结算操作的时间 +} + // Config 配置 type Config struct { Robot RobotConfig `toml:"robot"` @@ -193,6 +204,7 @@ type Config struct { GroupTypeConfigs []GroupTypeConfig `toml:"rule_type_configs"` Misc MiscConfig `toml:"misc"` NotifyUpdate NotifyUpdateConfig `toml:"notify_update"` + NotifySettle NotifySettleConfig `toml:"notify_settle"` } // LoadConfig 读取配置 diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index c03b26731..90c48e7f6 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -56,6 +56,9 @@ type QQRobot struct { CheckUpdateVersionMap map[string]string // 配置的检查更新名称=>最近的版本号,如"DNF蚊子腿小助手更新"=>"v4.2.2" + lastSettleStartTime time.Time + lastSettleFinishTime time.Time + quitCtx context.Context quitFunc context.CancelFunc } @@ -93,7 +96,8 @@ func (r *QQRobot) Start() { r.quitCtx, r.quitFunc = context.WithCancel(context.Background()) r.notify(r.Config.Robot.OnStart) - go r.ticker() + go r.updateTicker() + go r.settleTicker() } // Stop 停止运行 @@ -129,7 +133,8 @@ func (r *QQRobot) notify(cfg NotifyConfig) { logger.Infof("robot on %v finished", cfg.Name) } -func (r *QQRobot) ticker() { +// updateTicker 检查更新 +func (r *QQRobot) updateTicker() { if r.Config.NotifyUpdate.CheckInterval <= 0 { return } @@ -150,6 +155,28 @@ func (r *QQRobot) ticker() { } } +// settleTicker 检查结算 +func (r *QQRobot) settleTicker() { + if r.Config.NotifySettle.CheckInterval <= 0 { + return + } + + checkSettleTicker := time.NewTicker(time.Second * time.Duration(r.Config.NotifySettle.CheckInterval)) + defer checkSettleTicker.Stop() + + // 立即开始检查一次 + r.checkSettlements() + + for { + select { + case <-checkSettleTicker.C: + r.checkSettlements() + case <-r.quitCtx.Done(): + return + } + } +} + // RegisterHandlers 注册事件处理函数 func (r *QQRobot) RegisterHandlers() { // TODO: re: 添加其他事件的处理 @2021-10-02 05:35:37 From 5adb09a8087e42de96e156c8ebf2791fd63d2894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 31 Oct 2021 16:06:29 +0800 Subject: [PATCH 086/612] fix: lint --- qqrobot/check_settlements.go | 8 +++++--- qqrobot/config.go | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qqrobot/check_settlements.go b/qqrobot/check_settlements.go index b6691c054..8086d083c 100644 --- a/qqrobot/check_settlements.go +++ b/qqrobot/check_settlements.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" ) +// SettleResponse 结算结果 type SettleResponse struct { Code int `json:"code"` Msg string `json:"msg"` @@ -19,9 +20,10 @@ type SettleResponse struct { Data []SettleInfo `json:"data"` } +// SettleInfo 单条结算记录 type SettleInfo struct { - Id string `json:"id"` - Uid string `json:"uid"` + ID string `json:"id"` + UID string `json:"uid"` Batch interface{} `json:"batch"` Auto string `json:"auto"` Type string `json:"type"` @@ -100,7 +102,7 @@ func (r *QQRobot) checkSettlements() { // getLatestSettleInfo 获取最近的结算信息 func (r *QQRobot) getLatestSettleInfo() (*SettleInfo, error) { - resp, err := r.httpClient.Get(r.Config.NotifySettle.ApiUrl) + resp, err := r.httpClient.Get(r.Config.NotifySettle.APIUrl) if err != nil { return nil, err } diff --git a/qqrobot/config.go b/qqrobot/config.go index 2fe7ef65e..1aa80a6a7 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -190,7 +190,7 @@ type NotifyUpdateRule struct { // NotifySettleConfig 通知结算配置 type NotifySettleConfig struct { CheckInterval int64 `toml:"check_interval"` // 检查更新的间隔(秒),建议3600s - ApiUrl string `toml:"api_url"` // 结算api的url + APIUrl string `toml:"api_url"` // 结算api的url NotifyQQ int64 `toml:"notify_qq"` // 要通知的QQ StartMessage string `toml:"start_message"` // 开始结算的消息,参数:$real_money$=结算金额, $settle_time$=对应结算操作的时间 FinishMessage string `toml:"end_message"` // 完成结算的消息,参数:$real_money$=结算金额, $settle_time$=对应结算操作的时间 From 44f09d85501b1ed6ef8489164109263da5ba1b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 31 Oct 2021 16:07:53 +0800 Subject: [PATCH 087/612] =?UTF-8?q?ci:=20=E6=96=B0=E5=A2=9E=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E4=B8=80=E9=94=AElint=E7=9A=84=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=EF=BC=8C=E6=96=B9=E4=BE=BF=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _lint.bat | 1 + 1 file changed, 1 insertion(+) create mode 100644 _lint.bat diff --git a/_lint.bat b/_lint.bat new file mode 100644 index 000000000..121bcf675 --- /dev/null +++ b/_lint.bat @@ -0,0 +1 @@ +golangci-lint run From 987b9051c7f119565cd13ec3dca1fa211c96b976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 31 Oct 2021 17:47:43 +0800 Subject: [PATCH 088/612] =?UTF-8?q?fix:=20=E7=BB=93=E7=AE=97=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E6=A8=A1=E6=9D=BF=E4=B8=8D=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_settlements.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/check_settlements.go b/qqrobot/check_settlements.go index 8086d083c..18d3ea5b7 100644 --- a/qqrobot/check_settlements.go +++ b/qqrobot/check_settlements.go @@ -89,7 +89,7 @@ func (r *QQRobot) checkSettlements() { // 发送结算消息 reply := message.NewSendingMessage() - msg := r.Config.NotifySettle.StartMessage + msg := r.Config.NotifySettle.FinishMessage msg = strings.ReplaceAll(msg, templateargsRealMoney, settleInfo.RealMoney) msg = strings.ReplaceAll(msg, templateargsSettleTime, settleInfo.EndTime) reply.Append(message.NewText(msg)) From 29b5c4942b9ba78042b718714597bf9a7b8d4de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 31 Oct 2021 20:07:47 +0800 Subject: [PATCH 089/612] =?UTF-8?q?fix:=20=E7=BB=93=E6=9D=9F=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E6=9B=B4=E6=96=B0=E9=94=99=E4=BA=86=E5=8F=98=E9=87=8F?= =?UTF-8?q?=EF=BC=8C=E5=AF=BC=E8=87=B4=E4=B8=8D=E5=81=9C=E5=8F=91=E7=BB=93?= =?UTF-8?q?=E7=AE=97=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_settlements.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/check_settlements.go b/qqrobot/check_settlements.go index 18d3ea5b7..2601f7e02 100644 --- a/qqrobot/check_settlements.go +++ b/qqrobot/check_settlements.go @@ -96,7 +96,7 @@ func (r *QQRobot) checkSettlements() { r.cqBot.SendPrivateMessage(r.Config.NotifySettle.NotifyQQ, 0, reply) // 更新通知时间 - r.lastSettleStartTime = endTime + r.lastSettleFinishTime = endTime } } From 5c76d772bc1eef82b266ce04971fef2b3ad53808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 1 Nov 2021 02:31:00 +0800 Subject: [PATCH 090/612] =?UTF-8?q?refactor:=20=E9=80=82=E9=85=8D=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97=E5=B0=81=E8=A3=85=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 5 +---- go.sum | 35 ++--------------------------------- qqrobot/ai_chat.go | 1 + qqrobot/check_update.go | 3 ++- qqrobot/command.go | 1 + qqrobot/config.go | 1 + qqrobot/food.go | 1 + qqrobot/log.go | 16 +++------------- qqrobot/qq_robot.go | 5 ++++- qqrobot/rule.go | 2 ++ qqrobot/util.go | 4 ++-- 11 files changed, 20 insertions(+), 54 deletions(-) diff --git a/go.mod b/go.mod index 43ecd938e..07c2527d0 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/Microsoft/go-winio v0.5.0 github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a github.com/dustin/go-humanize v1.0.0 - github.com/fzls/logger v1.0.1 + github.com/fzls/logger v1.1.0 github.com/gabriel-vasile/mimetype v1.3.1 github.com/gookit/color v1.4.2 github.com/gorilla/websocket v1.4.2 @@ -57,9 +57,6 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/willf/bitset v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.19.1 // indirect golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect diff --git a/go.sum b/go.sum index ce2de6985..b52d2578e 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,6 @@ github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXG github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a h1:D2zG/7w7q5QbDeHSx9n4lS9cv9MGBqEjNTzeVO5g3Mo= github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a/go.mod h1:EpB6wQ+iaIqLRxVrs6si2J3XGv6wHXMrRXNUnJFaoww= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -23,8 +21,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fzls/logger v1.0.1 h1:UTBiEYod+5By25wg2fNGeEYMXZ/+5kEJTbs1hU/H8xg= -github.com/fzls/logger v1.0.1/go.mod h1:elehpMQ+FfNfBzRUrEfx3idkTcRQ4NJZ+UMvDyQ50Xg= +github.com/fzls/logger v1.1.0 h1:IHqvezhTz3koCzKgPqz1bL6esWoyXb7H9Ti/HHXMAM4= +github.com/fzls/logger v1.1.0/go.mod h1:FBZoU1PuXNCQBNr19NZjUWCq1vV6/FoGwc3D9q6of/0= github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -67,7 +65,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -89,7 +86,6 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -136,7 +132,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= @@ -180,23 +175,7 @@ github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -206,10 +185,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -220,7 +196,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -242,7 +217,6 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -264,12 +238,8 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -313,7 +283,6 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= diff --git a/qqrobot/ai_chat.go b/qqrobot/ai_chat.go index f8eaee342..c38fc4c4f 100644 --- a/qqrobot/ai_chat.go +++ b/qqrobot/ai_chat.go @@ -4,6 +4,7 @@ import ( "strconv" "strings" + logger "github.com/sirupsen/logrus" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" tbp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp/v20190627" diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 4ecd8f484..41c63fbaf 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -11,6 +11,7 @@ import ( "github.com/Mrs4s/MiraiGo/message" "github.com/gookit/color" + logger "github.com/sirupsen/logrus" ) // 2021/10/02 5:21 by fzls @@ -150,7 +151,7 @@ func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) // 解析版本信息 matches := regGitVersion.FindAllStringSubmatch(htmlText, -1) if len(matches) == 0 { - logger.Debugf("getLatestGitVersion gitChangelogPage=%v can not find any match, html text=%v", gitChangelogPage, err, htmlText) + logger.Debugf("getLatestGitVersion gitChangelogPage=%v can not find any match, err=%v, html text=%v", gitChangelogPage, err, htmlText) return VersionNone, "" } diff --git a/qqrobot/command.go b/qqrobot/command.go index 0f8388f91..87c8041ee 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/Mrs4s/MiraiGo/message" + logger "github.com/sirupsen/logrus" ) // 2021/10/02 5:25 by fzls diff --git a/qqrobot/config.go b/qqrobot/config.go index 1aa80a6a7..b336fa0b1 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -6,6 +6,7 @@ import ( "github.com/BurntSushi/toml" "github.com/pkg/errors" + logger "github.com/sirupsen/logrus" ) // TencentAiChatAPI 腾讯ai聊天接口 diff --git a/qqrobot/food.go b/qqrobot/food.go index ccda0cb02..1c64f0415 100644 --- a/qqrobot/food.go +++ b/qqrobot/food.go @@ -11,6 +11,7 @@ import ( "github.com/Mrs4s/MiraiGo/message" "github.com/pkg/errors" + logger "github.com/sirupsen/logrus" ) // 2021/10/02 5:25 by fzls diff --git a/qqrobot/log.go b/qqrobot/log.go index 0be9600b7..373c4ed34 100644 --- a/qqrobot/log.go +++ b/qqrobot/log.go @@ -1,19 +1,9 @@ package qqrobot import ( - "fmt" - - mylogger "github.com/fzls/logger" + "github.com/fzls/logger" ) -var logger *mylogger.SugaredLogger - -func init() { - // 初始化日志 - var err error - logger, err = mylogger.NewLogger("logs", "qq_robot", mylogger.InfoLevel) - if err != nil { - fmt.Printf("new logger err=%v\n", err) - return - } +func initLogger() { + logger.InitLogger("logs", "qq_robot", "info", "debug") } diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 90c48e7f6..5e648e492 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -17,6 +17,7 @@ import ( "github.com/gookit/color" lru "github.com/hashicorp/golang-lru" "github.com/pkg/errors" + logger "github.com/sirupsen/logrus" tbp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp/v20190627" "github.com/Mrs4s/go-cqhttp/coolq" @@ -92,6 +93,8 @@ func NewQQRobot(cqRobot *coolq.CQBot, configPath string) *QQRobot { // Start 开始运行 func (r *QQRobot) Start() { + initLogger() + r.StartTime = time.Now() r.quitCtx, r.quitFunc = context.WithCancel(context.Background()) @@ -322,7 +325,7 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { maybeKilledWrongPerson := false // 误杀 if _, replied := rule.ProcessedMessages[source]; replied { maybeKilledWrongPerson = true - logger.Warnf("【似乎消息混了,不过没办法,继续处理吧-。-】", nowStr, config.Name, p(m)) + logger.Warn("【似乎消息混了,不过没办法,继续处理吧-。-】", nowStr, config.Name, p(m)) } // 是否不需要回复 if len(config.KeywordRegexes) != 0 && !hitKeyWords || diff --git a/qqrobot/rule.go b/qqrobot/rule.go index fa7adfa01..9f76aeb92 100644 --- a/qqrobot/rule.go +++ b/qqrobot/rule.go @@ -3,6 +3,8 @@ package qqrobot import ( "math/rand" "strings" + + logger "github.com/sirupsen/logrus" ) // 2020/06/01 2:42 by fzls diff --git a/qqrobot/util.go b/qqrobot/util.go index 7a71fde23..e052da66f 100644 --- a/qqrobot/util.go +++ b/qqrobot/util.go @@ -13,7 +13,7 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" "github.com/gookit/color" - log "github.com/sirupsen/logrus" + logger "github.com/sirupsen/logrus" "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" @@ -26,7 +26,7 @@ const ( func (r *QQRobot) tryAppendImageByURL(m *message.SendingMessage, imageURL string) { image, err := r._makeLocalImage(imageURL) if err != nil { - log.Errorf("_makeLocalImage err=%v", err) + logger.Errorf("_makeLocalImage err=%v", err) return } From bb5310b19252d03094223c71828a79000c8b16b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 1 Nov 2021 02:34:20 +0800 Subject: [PATCH 091/612] =?UTF-8?q?test:=20=E4=BF=AE=E6=AD=A3=E6=BC=8F?= =?UTF-8?q?=E6=8E=89=E7=9A=84=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot_test.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/qqrobot/qq_robot_test.go b/qqrobot/qq_robot_test.go index f876ad1b4..093de5b8b 100644 --- a/qqrobot/qq_robot_test.go +++ b/qqrobot/qq_robot_test.go @@ -4,8 +4,6 @@ import ( "fmt" "reflect" "testing" - - mylogger "github.com/fzls/logger" ) // 2020/10/30 17:39 by fzls @@ -41,14 +39,6 @@ func Test_convertChineseNumber(t *testing.T) { } func Test_getLatestGitVersion(t *testing.T) { - // 初始化日志 - var err error - logger, err = mylogger.NewLogger("logs", "qq_robot", mylogger.DebugLevel) - if err != nil { - fmt.Printf("new logger err=%v\n", err) - return - } - qqRobot := NewQQRobot(nil, "") version, updateMessage := qqRobot.getLatestGitVersion("https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD") t.Logf("version=%v, updateMessage如下:\n%v", version, updateMessage) From b80ee5f5fab87ef3777049cfa735d5a8b96b5df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 1 Nov 2021 02:50:31 +0800 Subject: [PATCH 092/612] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0log=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 +- go.sum | 4 ++-- qqrobot/log.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 07c2527d0..f1700601f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/Microsoft/go-winio v0.5.0 github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a github.com/dustin/go-humanize v1.0.0 - github.com/fzls/logger v1.1.0 + github.com/fzls/logger v1.1.1 github.com/gabriel-vasile/mimetype v1.3.1 github.com/gookit/color v1.4.2 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index b52d2578e..82f93e401 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fzls/logger v1.1.0 h1:IHqvezhTz3koCzKgPqz1bL6esWoyXb7H9Ti/HHXMAM4= -github.com/fzls/logger v1.1.0/go.mod h1:FBZoU1PuXNCQBNr19NZjUWCq1vV6/FoGwc3D9q6of/0= +github.com/fzls/logger v1.1.1 h1:9ZteZN4pxh16MVsjlNgCurQb6qBDxKYDKKq7zifWHIU= +github.com/fzls/logger v1.1.1/go.mod h1:FBZoU1PuXNCQBNr19NZjUWCq1vV6/FoGwc3D9q6of/0= github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= diff --git a/qqrobot/log.go b/qqrobot/log.go index 373c4ed34..a2d28e21c 100644 --- a/qqrobot/log.go +++ b/qqrobot/log.go @@ -5,5 +5,5 @@ import ( ) func initLogger() { - logger.InitLogger("logs", "qq_robot", "info", "debug") + logger.InitLogger("logs", "qq_robot", "debug") } From fd5801523bf8b90c83e42e69a99e273d1983d8c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 1 Nov 2021 02:51:39 +0800 Subject: [PATCH 093/612] =?UTF-8?q?fix:=20=E6=97=A5=E5=BF=97=E7=BA=A7?= =?UTF-8?q?=E5=88=AB=E6=94=B9=E5=9B=9Einfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/log.go b/qqrobot/log.go index a2d28e21c..4b291de15 100644 --- a/qqrobot/log.go +++ b/qqrobot/log.go @@ -5,5 +5,5 @@ import ( ) func initLogger() { - logger.InitLogger("logs", "qq_robot", "debug") + logger.InitLogger("logs", "qq_robot", "info") } From c2d49b6b093476dddef71e46de36a56e7e717e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 5 Nov 2021 17:59:55 +0800 Subject: [PATCH 094/612] =?UTF-8?q?feat:=20=E6=8A=8A=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E5=88=86=E5=89=B2=E7=9A=84=E9=80=BB=E8=BE=91=E5=85=88=E6=90=AC?= =?UTF-8?q?=E8=BF=87=E6=9D=A5=EF=BC=8C=E6=96=B9=E4=BE=BF=E9=9D=92=E9=BE=99?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E5=8F=91=E9=80=81=E8=B6=85=E9=95=BF=E7=9A=84?= =?UTF-8?q?=E6=80=BB=E7=BB=93=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/util.go | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 coolq/util.go diff --git a/coolq/util.go b/coolq/util.go new file mode 100644 index 000000000..bfc9c2b6f --- /dev/null +++ b/coolq/util.go @@ -0,0 +1,168 @@ +package coolq + +import ( + "strings" + + "github.com/Mrs4s/MiraiGo/message" +) + +// 单条消息发送的大小限制(预估) +const MaxMessageSize = 5000 + +// SplitLongMessage 将过长的消息分割为若干个适合发送的消息 +func SplitLongMessage(sendingMessage *message.SendingMessage) []*message.SendingMessage { + // 合并连续文本消息 + sendingMessage = mergeContinuousTextMessages(sendingMessage) + + // 分割过长元素 + sendingMessage = splitElements(sendingMessage) + + // 将元素分为多组,确保各组不超过单条消息的上限 + splitMessages := splitMessages(sendingMessage) + + return splitMessages +} + +// mergeContinuousTextMessages 预先将所有连续的文本消息合并为到一起,方便后续统一切割 +func mergeContinuousTextMessages(sendingMessage *message.SendingMessage) *message.SendingMessage { + // 检查下是否有连续的文本消息,若没有,则可以直接返回 + lastIsText := false + hasContinuousText := false + for _, msg := range sendingMessage.Elements { + if msg.Type() == message.Text { + if lastIsText { + // 有连续的文本消息,需要进行处理 + hasContinuousText = true + break + } + + // 遇到文本元素先存放起来,方便将连续的文本元素合并 + lastIsText = true + continue + } else { + lastIsText = false + } + } + if !hasContinuousText { + return sendingMessage + } + + // 存在连续的文本消息,需要进行合并处理 + textBuffer := strings.Builder{} + lastIsText = false + totalMessageCount := 0 + for _, msg := range sendingMessage.Elements { + if msgVal, ok := msg.(*message.TextElement); ok { + // 遇到文本元素先存放起来,方便将连续的文本元素合并 + textBuffer.WriteString(msgVal.Content) + lastIsText = true + continue + } + + // 如果之前的是文本元素(可能是多个合并起来的),则在这里将其实际放入消息中 + if lastIsText { + sendingMessage.Elements[totalMessageCount] = message.NewText(textBuffer.String()) + totalMessageCount += 1 + textBuffer.Reset() + } + lastIsText = false + + // 非文本元素则直接处理 + sendingMessage.Elements[totalMessageCount] = msg + totalMessageCount += 1 + } + // 处理最后几个元素是文本的情况 + if textBuffer.Len() != 0 { + sendingMessage.Elements[totalMessageCount] = message.NewText(textBuffer.String()) + totalMessageCount += 1 + textBuffer.Reset() + } + sendingMessage.Elements = sendingMessage.Elements[:totalMessageCount] + + return sendingMessage +} + +// splitElements 将原有消息的各个元素先尝试处理,如过长的文本消息按需分割为多个元素 +func splitElements(sendingMessage *message.SendingMessage) *message.SendingMessage { + // 检查下是否存在需要文本消息,若不存在,则直接返回 + needSplit := false + for _, msg := range sendingMessage.Elements { + if msgVal, ok := msg.(*message.TextElement); ok { + if textNeedSplit(msgVal.Content) { + needSplit = true + break + } + } + } + if !needSplit { + return sendingMessage + } + + // 开始尝试切割 + messageParts := message.NewSendingMessage() + + for _, msg := range sendingMessage.Elements { + switch msgVal := msg.(type) { + case *message.TextElement: + messageParts.Elements = append(messageParts.Elements, splitPlainMessage(msgVal.Content)...) + default: + messageParts.Append(msg) + } + } + + return messageParts +} + +// splitMessages 根据大小分为多个消息进行发送 +func splitMessages(sendingMessage *message.SendingMessage) []*message.SendingMessage { + var splitMessages []*message.SendingMessage + + messagePart := message.NewSendingMessage() + msgSize := 0 + for _, part := range sendingMessage.Elements { + estimateSize := message.EstimateLength([]message.IMessageElement{part}) + // 若当前分消息加上新的元素后大小会超限,且已经有元素(确保不会无限循环),则开始切分为新的一个元素 + if msgSize+estimateSize > MaxMessageSize && len(messagePart.Elements) > 0 { + splitMessages = append(splitMessages, messagePart) + + messagePart = message.NewSendingMessage() + msgSize = 0 + } + + // 加上新的元素 + messagePart.Append(part) + msgSize += estimateSize + } + // 将最后一个分片加上 + if len(messagePart.Elements) != 0 { + splitMessages = append(splitMessages, messagePart) + } + + return splitMessages +} + +func splitPlainMessage(content string) []message.IMessageElement { + if !textNeedSplit(content) { + return []message.IMessageElement{message.NewText(content)} + } + + splittedMessage := make([]message.IMessageElement, 0, (len(content)+MaxMessageSize-1)/MaxMessageSize) + + last := 0 + for runeIndex, runeValue := range content { + // 如果加上新的这个字符后,会超出大小,则从这个字符前分一次片 + if runeIndex+len(string(runeValue))-last > MaxMessageSize { + splittedMessage = append(splittedMessage, message.NewText(content[last:runeIndex])) + last = runeIndex + } + } + if last != len(content) { + splittedMessage = append(splittedMessage, message.NewText(content[last:len(content)])) + } + + return splittedMessage +} + +func textNeedSplit(content string) bool { + return len(content) > MaxMessageSize +} From 12e0129194fe402d6ea89020b9e8f272379d26c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 5 Nov 2021 18:01:48 +0800 Subject: [PATCH 095/612] =?UTF-8?q?feat:=20cq=E5=8F=91=E9=80=81=E7=BE=A4?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=97=B6=E5=A2=9E=E5=8A=A0=E5=A4=84=E7=90=86?= =?UTF-8?q?=E8=B6=85=E9=95=BF=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/api.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 571470827..2d7219468 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -425,7 +425,25 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b } } fixAt(elem) - mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem}) + + var mid int32 + + // 魔改一下,兼容过长的消息,使得青龙面板那边可以发送很多个账号的统计信息 + msg := &message.SendingMessage{Elements: elem} + msgLen := message.EstimateLength(msg.Elements) + if msgLen <= MaxMessageSize { + mid = bot.SendGroupMessage(groupID, msg) + } else { + // 特殊处理过长的消息,此时mid返回第一个消息 + parts := SplitLongMessage(msg) + for idx, part := range parts { + ret := bot.SendGroupMessage(groupID, part) + if idx == 0 { + mid = ret + } + } + } + if mid == -1 { return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") } From 3a3e06a7b661c86ae50ebe35b1c9d3f89d2cfe8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 5 Nov 2021 18:29:26 +0800 Subject: [PATCH 096/612] fix: lint --- coolq/util.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/coolq/util.go b/coolq/util.go index bfc9c2b6f..3c6c03bc4 100644 --- a/coolq/util.go +++ b/coolq/util.go @@ -6,7 +6,7 @@ import ( "github.com/Mrs4s/MiraiGo/message" ) -// 单条消息发送的大小限制(预估) +// MaxMessageSize 单条消息发送的大小限制(预估) const MaxMessageSize = 5000 // SplitLongMessage 将过长的消息分割为若干个适合发送的消息 @@ -62,19 +62,19 @@ func mergeContinuousTextMessages(sendingMessage *message.SendingMessage) *messag // 如果之前的是文本元素(可能是多个合并起来的),则在这里将其实际放入消息中 if lastIsText { sendingMessage.Elements[totalMessageCount] = message.NewText(textBuffer.String()) - totalMessageCount += 1 + totalMessageCount++ textBuffer.Reset() } lastIsText = false // 非文本元素则直接处理 sendingMessage.Elements[totalMessageCount] = msg - totalMessageCount += 1 + totalMessageCount++ } // 处理最后几个元素是文本的情况 if textBuffer.Len() != 0 { sendingMessage.Elements[totalMessageCount] = message.NewText(textBuffer.String()) - totalMessageCount += 1 + totalMessageCount++ textBuffer.Reset() } sendingMessage.Elements = sendingMessage.Elements[:totalMessageCount] @@ -157,7 +157,7 @@ func splitPlainMessage(content string) []message.IMessageElement { } } if last != len(content) { - splittedMessage = append(splittedMessage, message.NewText(content[last:len(content)])) + splittedMessage = append(splittedMessage, message.NewText(content[last:])) } return splittedMessage From e0330c9796e99ee49a61a41094213f0816c9df9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 6 Nov 2021 15:26:27 +0800 Subject: [PATCH 097/612] =?UTF-8?q?feat:=20=E5=8F=91=E9=80=81=E7=BE=A4?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=A4=B1=E8=B4=A5=E6=97=B6=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=89=93=E5=8D=B0=E7=BE=A4=E6=B6=88=E6=81=AF=E5=86=85=E5=AE=B9?= =?UTF-8?q?=EF=BC=8C=E6=96=B9=E4=BE=BF=E6=8E=92=E6=9F=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index f92744c2c..2d5e49de4 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -298,7 +298,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int bot.checkMedia(newElem) ret := bot.Client.SendGroupMessage(groupID, m, ForceFragmented) if ret == nil || ret.Id == -1 { - log.Warnf("群消息发送失败: 账号可能被风控.") + log.Warnf("群消息发送失败: 账号可能被风控.\ngroupID= %v 消息内容= %v", groupID, message.ToReadableString(m.Elements)) return -1 } return bot.InsertGroupMessage(ret) From dd077e39f3457d876a70707e31cd30a2f4ea47c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 6 Nov 2021 15:35:19 +0800 Subject: [PATCH 098/612] =?UTF-8?q?feat:=20=E6=9C=BA=E5=99=A8=E4=BA=BA?= =?UTF-8?q?=E5=8F=91=E9=80=81=E7=BE=A4=E6=B6=88=E6=81=AF=E6=97=B6=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E5=AE=8C=E6=95=B4=E4=BF=A1=E6=81=AF=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 2d7219468..f34568f45 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -447,7 +447,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b if mid == -1 { return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") } - log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(m.String()), mid) + log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, message.ToReadableString(elem), mid) return OK(global.MSG{"message_id": mid}) } From 28e49dee601564cfeeb8baaccaa913c61ea8fc3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 6 Nov 2021 16:37:28 +0800 Subject: [PATCH 099/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E7=BB=93=E7=AE=97=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_settlements.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/qqrobot/check_settlements.go b/qqrobot/check_settlements.go index 2601f7e02..880f7aa2f 100644 --- a/qqrobot/check_settlements.go +++ b/qqrobot/check_settlements.go @@ -8,6 +8,7 @@ import ( "github.com/Mrs4s/MiraiGo/message" "github.com/pkg/errors" + logger "github.com/sirupsen/logrus" ) // SettleResponse 结算结果 @@ -69,12 +70,7 @@ func (r *QQRobot) checkSettlements() { } // 发送结算消息 - reply := message.NewSendingMessage() - msg := r.Config.NotifySettle.StartMessage - msg = strings.ReplaceAll(msg, templateargsRealMoney, settleInfo.RealMoney) - msg = strings.ReplaceAll(msg, templateargsSettleTime, settleInfo.AddTime) - reply.Append(message.NewText(msg)) - r.cqBot.SendPrivateMessage(r.Config.NotifySettle.NotifyQQ, 0, reply) + r.sendSettleMessage(r.Config.NotifySettle.StartMessage, settleInfo.RealMoney, settleInfo.AddTime) // 更新通知时间 r.lastSettleStartTime = startTime @@ -88,18 +84,23 @@ func (r *QQRobot) checkSettlements() { } // 发送结算消息 - reply := message.NewSendingMessage() - msg := r.Config.NotifySettle.FinishMessage - msg = strings.ReplaceAll(msg, templateargsRealMoney, settleInfo.RealMoney) - msg = strings.ReplaceAll(msg, templateargsSettleTime, settleInfo.EndTime) - reply.Append(message.NewText(msg)) - r.cqBot.SendPrivateMessage(r.Config.NotifySettle.NotifyQQ, 0, reply) + r.sendSettleMessage(r.Config.NotifySettle.FinishMessage, settleInfo.RealMoney, settleInfo.EndTime) // 更新通知时间 r.lastSettleFinishTime = endTime } } +func (r *QQRobot) sendSettleMessage(templateMsg string, realMoney string, settleTime string) { + reply := message.NewSendingMessage() + msg := templateMsg + msg = strings.ReplaceAll(msg, templateargsRealMoney, realMoney) + msg = strings.ReplaceAll(msg, templateargsSettleTime, settleTime) + reply.Append(message.NewText(msg)) + r.cqBot.SendPrivateMessage(r.Config.NotifySettle.NotifyQQ, 0, reply) + logger.Infof("发送结算消息= %v", message.ToReadableString(reply.Elements)) +} + // getLatestSettleInfo 获取最近的结算信息 func (r *QQRobot) getLatestSettleInfo() (*SettleInfo, error) { resp, err := r.httpClient.Get(r.Config.NotifySettle.APIUrl) From ab238986162c1b29fe9189870029cbf6d6cf631a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 6 Nov 2021 20:20:15 +0800 Subject: [PATCH 100/612] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=E5=A4=9A=E4=B8=AA=E5=9B=BE=E7=89=87=E7=9A=84=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/config.go | 1 + qqrobot/qq_robot.go | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/qqrobot/config.go b/qqrobot/config.go index b336fa0b1..4e24222bb 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -118,6 +118,7 @@ type RuleConfig struct { AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 当触发该规则时,是否需要@全体成员 GuideContent string `toml:"guide_content"` // 内容 ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 + ImageURLList []string `toml:"image_url_list"` // 图片URL列表,若有,则会额外附加图片 RandomImageUrls []string `toml:"random_image_urls"` // 若配置,则从中随机一个作为图片发送,同时ImageUrl配置会被覆盖 CD int64 `toml:"cd"` // cd时长(秒),0表示不设定,若设定,在cd内触发规则时,若设置了cd内回复内容,则回复该内容,否则视为未触发 GuideContentInCD string `toml:"guide_content_in_cd"` // cd内触发规则时的回复内容 diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 5e648e492..e399e6f8c 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -594,6 +594,11 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { if imageURL != "" { r.tryAppendImageByURL(replies, imageURL) } + if len(config.ImageURLList) != 0 { + for _, image := range config.ImageURLList { + r.tryAppendImageByURL(replies, image) + } + } if maybeKilledWrongPerson { replies.Append(message.NewText("似乎前面有人代替你被误杀了。但是,正义的铁拳虽然会乱锤,却不会错过正确的人。宁可错杀三千,不可放过一人!(手动眼部红光特效)")) @@ -845,6 +850,11 @@ func (r *QQRobot) onMemberJoin(m *client.MemberJoinGroupEvent, rule *Rule) error if config.ImageURL != "" { r.tryAppendImageByURL(replies, config.ImageURL) } + if len(config.ImageURLList) != 0 { + for _, image := range config.ImageURLList { + r.tryAppendImageByURL(replies, image) + } + } if len(replies.Elements) != 0 { rspID := r.cqBot.SendGroupMessage(groupID, replies) From 8053dc69b14764487d1ec622e5b3691f93cbc082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 9 Nov 2021 21:59:52 +0800 Subject: [PATCH 101/612] =?UTF-8?q?ci:=20=E8=B0=83=E6=95=B4lint=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/golint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index 624634e4d..6bf576263 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -24,11 +24,11 @@ jobs: go test $(go list ./...) - name: Commit back - if: ${{ github.repository_owner == 'Mrs4s' && !github.event.pull_request }} + if: ${{ github.repository_owner == 'fzls' && !github.event.pull_request }} continue-on-error: true run: | git config --local user.name 'github-actions[bot]' - git config --local user.email '41898282+github-actions[bot]@users.noreply.github.com' + git config --local user.email 'github-actions[bot]@users.noreply.github.com' git add --all git commit -m "ci(chore): Fix stylings" git push From 0d60f38ee3c5a0d037f60d2bfd25341e1cc712a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 10 Nov 2021 12:45:39 +0800 Subject: [PATCH 102/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E9=9D=92?= =?UTF-8?q?=E9=BE=99=E7=BB=9F=E8=AE=A1=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + qqrobot/scripts/font/jet.ttf | Bin 0 -> 173264 bytes qqrobot/scripts/qinglong_bean_chart.py | 453 +++++++++++++++++++++++++ 3 files changed, 455 insertions(+) create mode 100644 qqrobot/scripts/font/jet.ttf create mode 100644 qqrobot/scripts/qinglong_bean_chart.py diff --git a/.gitignore b/.gitignore index c193f3538..659b95607 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ logs/ resource.syso !global/config/config.yml !qqrobot/config.toml +qqrobot/scripts/bean.jpeg +qqrobot/scripts/chart.jpeg \ No newline at end of file diff --git a/qqrobot/scripts/font/jet.ttf b/qqrobot/scripts/font/jet.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0cd1cb66bd193f2b178d0a69a7b0658d76b3a670 GIT binary patch literal 173264 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%0Abi#}=?x9#AzJ05!8Z#!udP+ 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 OwHaMwcmReuwf+ZCw$sx9 literal 0 HcmV?d00001 diff --git a/qqrobot/scripts/qinglong_bean_chart.py b/qqrobot/scripts/qinglong_bean_chart.py new file mode 100644 index 000000000..29ca4f7dd --- /dev/null +++ b/qqrobot/scripts/qinglong_bean_chart.py @@ -0,0 +1,453 @@ +import datetime +import json +import logging +import re +import time +from datetime import timedelta, timezone +from urllib.parse import urlencode + +import requests +import requests.adapters +from PIL import Image, ImageFont, ImageDraw +from prettytable import PrettyTable + +FONT_FILE = f'font/jet.ttf' +BEAN_IMG = f'bean.jpeg' +CHART_IMG = f'chart.jpeg' +AUTH_JSON = "D:/_codes/js/qinglong/data/config/auth.json" +QL_API_ADDR = "http://127.0.0.1:5700/api" +# QUICK_CHART_ADDR = "https://quickchart.io" +QUICK_CHART_ADDR = "http://127.0.0.1:5703" + +logger = logging.getLogger(__name__) + +SHA_TZ = timezone( + timedelta(hours=8), + name='Asia/Shanghai', +) +requests.adapters.DEFAULT_RETRIES = 5 +session = requests.session() +session.keep_alive = False + + +def get_bean(account_idx: int): + res = get_bean_data(account_idx) + if res['code'] == 200: + creat_bean_count(res['data'][3], res['data'][0], res['data'][1], res['data'][2][1:]) + print(f'您的账号 {account_idx} 收支情况 统计表格 已保存到 {BEAN_IMG}') + + +def get_chart(account_idx: int): + res = get_bean_data(int(account_idx)) + if res['code'] == 200: + creat_chart(res['data'][3], f'账号{str(account_idx)}', res['data'][0], res['data'][1], res['data'][2][1:]) + print(f'您的账号 {account_idx} 收支情况 统计图 已保存到 {CHART_IMG}') + + +def get_bean_data(i): + try: + cookies = get_cks(AUTH_JSON) + if cookies: + ck = cookies[i - 1] + beans_res = get_beans_7days(ck) + beantotal = get_total_beans(ck) + if beans_res['code'] != 200: + return beans_res + else: + beans_in, beans_out = [], [] + beanstotal = [int(beantotal), ] + for i in beans_res['data'][0]: + beantotal = int( + beantotal) - int(beans_res['data'][0][i]) - int(beans_res['data'][1][i]) + beans_in.append(int(beans_res['data'][0][i])) + beans_out.append(int(str(beans_res['data'][1][i]).replace('-', ''))) + beanstotal.append(beantotal) + return {'code': 200, 'data': [beans_in[::-1], beans_out[::-1], beanstotal[::-1], beans_res['data'][2][::-1]]} + except Exception as e: + logger.error(str(e)) + + +def get_cks(ckfile): + ck_reg = re.compile(r'pt_key=\S*?;.*?pt_pin=\S*?;') + + with open(ckfile, 'r', encoding='utf-8') as f: + auth = json.load(f) + lines = str(env_manage_QL('search', 'JD_COOKIE', auth['token'])) + + cookies = ck_reg.findall(lines) + for ck in cookies: + if ck == 'pt_key=xxxxxxxxxx;pt_pin=xxxx;': + cookies.remove(ck) + break + return cookies + + +def env_manage_QL(fun, envdata, token): + url = f'{QL_API_ADDR}/envs' + headers = { + 'Authorization': f'Bearer {token}' + } + try: + if fun == 'search': + params = { + 't': int(round(time.time() * 1000)), + 'searchValue': envdata + } + res = requests.get(url, params=params, headers=headers).json() + elif fun == 'add': + data = { + 'name': envdata['name'], + 'value': envdata['value'], + 'remarks': envdata['remarks'] if 'remarks' in envdata.keys() else '' + } + res = requests.post(url, json=[data], headers=headers).json() + elif fun == 'edit': + data = { + 'name': envdata['name'], + 'value': envdata['value'], + '_id': envdata['_id'], + 'remarks': envdata['remarks'] if 'remarks' in envdata.keys() else '' + } + res = requests.put(url, json=data, headers=headers).json() + elif fun == 'disable': + data = [envdata['_id']] + res = requests.put(url + '/disable', json=data, + headers=headers).json() + elif fun == 'enable': + data = [envdata['_id']] + res = requests.put(url + '/enable', json=data, + headers=headers).json() + elif fun == 'del': + data = [envdata['_id']] + res = requests.delete(url, json=data, headers=headers).json() + else: + res = {'code': 400, 'data': '未知功能'} + except Exception as e: + res = {'code': 400, 'data': str(e)} + finally: + return res + + +def get_beans_7days(ck): + try: + day_7 = True + page = 0 + headers = { + "Host": "api.m.jd.com", + "Connection": "keep-alive", + "charset": "utf-8", + "User-Agent": "Mozilla/5.0 (Linux; Android 10; MI 9 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2797 MMWEBSDK/201201 Mobile Safari/537.36 MMWEBID/7986 MicroMessenger/8.0.1840(0x2800003B) Process/appbrand4 WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android", + "Content-Type": "application/x-www-form-urlencoded;", + "Accept-Encoding": "gzip, compress, deflate, br", + "Cookie": ck, + "Referer": "https://servicewechat.com/wxa5bf5ee667d91626/141/page-frame.html", + } + days = [] + for i in range(0, 7): + days.append( + (datetime.date.today() - datetime.timedelta(days=i)).strftime("%Y-%m-%d")) + beans_in = {key: 0 for key in days} + beans_out = {key: 0 for key in days} + while day_7: + page = page + 1 + resp = session.get("https://api.m.jd.com/api", params=gen_params(page), + headers=headers, timeout=100).text + res = json.loads(resp) + if res['resultCode'] == 0: + for i in res['data']['list']: + for date in days: + if str(date) in i['createDate'] and i['amount'] > 0: + beans_in[str(date)] = beans_in[str( + date)] + i['amount'] + break + elif str(date) in i['createDate'] and i['amount'] < 0: + beans_out[str(date)] = beans_out[str( + date)] + i['amount'] + break + if i['createDate'].split(' ')[0] not in str(days): + day_7 = False + else: + return {'code': 400, 'data': res} + return {'code': 200, 'data': [beans_in, beans_out, days]} + except Exception as e: + logger.error(str(e)) + return {'code': 400, 'data': str(e)} + + +def gen_params(page): + body = gen_body(page) + params = { + "functionId": "jposTradeQuery", + "appid": "swat_miniprogram", + "client": "tjj_m", + "sdkName": "orderDetail", + "sdkVersion": "1.0.0", + "clientVersion": "3.1.3", + "timestamp": int(round(time.time() * 1000)), + "body": json.dumps(body) + } + return params + + +def gen_body(page): + body = { + "beginDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), + "endDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), + "pageNo": page, + "pageSize": 20, + } + return body + + +def get_total_beans(ck): + try: + headers = { + "Host": "wxapp.m.jd.com", + "Connection": "keep-alive", + "charset": "utf-8", + "User-Agent": "Mozilla/5.0 (Linux; Android 10; MI 9 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2797 MMWEBSDK/201201 Mobile Safari/537.36 MMWEBID/7986 MicroMessenger/8.0.1840(0x2800003B) Process/appbrand4 WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android", + "Content-Type": "application/x-www-form-urlencoded;", + "Accept-Encoding": "gzip, compress, deflate, br", + "Cookie": ck, + } + jurl = "https://wxapp.m.jd.com/kwxhome/myJd/home.json" + resp = session.get(jurl, headers=headers, timeout=100).text + res = json.loads(resp) + return res['user']['jingBean'] + except Exception as e: + logger.error(str(e)) + + +def creat_bean_count(date, beansin, beansout, beanstotal): + tb = PrettyTable() + tb.add_column('DATE', date) + tb.add_column('BEANSIN', beansin) + tb.add_column('BEANSOUT', beansout) + tb.add_column('TOTAL', beanstotal) + font = ImageFont.truetype(FONT_FILE, 18) + im = Image.new("RGB", (500, 260), (244, 244, 244)) + dr = ImageDraw.Draw(im) + dr.text((10, 5), str(tb), font=font, fill="#000000") + im.save(BEAN_IMG) + + +def creat_chart(xdata, title, bardata, bardata2, linedate): + qc = QuickChart() + qc.background_color = '#fff' + qc.width = "1000" + qc.height = "600" + qc.config = { + "type": "bar", + "data": { + "labels": xdata, + "datasets": [ + { + "label": "IN", + "backgroundColor": [ + "rgb(255, 99, 132)", + "rgb(255, 159, 64)", + "rgb(255, 205, 86)", + "rgb(75, 192, 192)", + "rgb(54, 162, 235)", + "rgb(153, 102, 255)", + "rgb(255, 99, 132)" + ], + "yAxisID": "y1", + "data": bardata + }, + { + "label": "OUT", + "backgroundColor": [ + "rgb(255, 99, 132)", + "rgb(255, 159, 64)", + "rgb(255, 205, 86)", + "rgb(75, 192, 192)", + "rgb(54, 162, 235)", + "rgb(153, 102, 255)", + "rgb(255, 99, 132)" + ], + "yAxisID": "y1", + "data": bardata2 + }, + { + "label": "TOTAL", + "type": "line", + "fill": False, + "backgroundColor": "rgb(201, 203, 207)", + "yAxisID": "y2", + "data": linedate + } + ] + }, + "options": { + "plugins": { + "datalabels": { + "anchor": 'end', + "align": -100, + "color": '#666', + "font": { + "size": 20, + } + }, + }, + "legend": { + "labels": { + "fontSize": 20, + "fontStyle": 'bold', + } + }, + "title": { + "display": True, + "text": f'{title} 收支情况', + "fontSize": 24, + }, + "scales": { + "xAxes": [{ + "ticks": { + "fontSize": 24, + } + }], + "yAxes": [ + { + "id": "y1", + "type": "linear", + "display": False, + "position": "left", + "ticks": { + "max": int(int(max([max(bardata), max(bardata2)]) + 100) * 2) + }, + "scaleLabel": { + "fontSize": 20, + "fontStyle": 'bold', + } + }, + { + "id": "y2", + "type": "linear", + "display": False, + "ticks": { + "min": int(min(linedate) * 2 - (max(linedate)) - 100), + "max": int(int(max(linedate))) + }, + "position": "right" + } + ] + } + } + } + qc.to_file(CHART_IMG) + + +class QuickChart: + def __init__(self): + self.config = None + self.width = 500 + self.height = 300 + self.background_color = '#ffffff' + self.device_pixel_ratio = 1.0 + self.format = 'png' + self.version = '2.9.4' + self.key = None + # self.scheme = 'https' + # self.host = 'quickchart.io' + + def is_valid(self): + return self.config is not None + + def get_url_base(self): + return QUICK_CHART_ADDR + + def get_url(self): + if not self.is_valid(): + raise RuntimeError( + 'You must set the `config` attribute before generating a url') + params = { + 'c': dump_json(self.config) if type(self.config) == dict else self.config, + 'w': self.width, + 'h': self.height, + 'bkg': self.background_color, + 'devicePixelRatio': self.device_pixel_ratio, + 'f': self.format, + 'v': self.version, + } + if self.key: + params['key'] = self.key + return '%s/chart?%s' % (self.get_url_base(), urlencode(params)) + + def _post(self, url): + try: + import requests + except: + raise RuntimeError('Could not find `requests` dependency') + + postdata = { + 'chart': dump_json(self.config) if type(self.config) == dict else self.config, + 'width': self.width, + 'height': self.height, + 'backgroundColor': self.background_color, + 'devicePixelRatio': self.device_pixel_ratio, + 'format': self.format, + 'version': self.version, + } + if self.key: + postdata['key'] = self.key + st = time.time() + resp = requests.post(url, json=postdata) + if resp.status_code != 200: + raise RuntimeError( + 'Invalid response code from chart creation endpoint') + return resp + + def get_short_url(self): + resp = self._post('%s/chart/create' % self.get_url_base()) + parsed = json.loads(resp.text) + if not parsed['success']: + raise RuntimeError( + 'Failure response status from chart creation endpoint') + return parsed['url'] + + def get_bytes(self): + resp = self._post('%s/chart' % self.get_url_base()) + return resp.content + + def to_file(self, path): + content = self.get_bytes() + with open(path, 'wb') as f: + f.write(content) + + +FUNCTION_DELIMITER_RE = re.compile('\"__BEGINFUNCTION__(.*?)__ENDFUNCTION__\"') + + +class QuickChartFunction: + def __init__(self, script): + self.script = script + + def __repr__(self): + return self.script + + +def serialize(obj): + if isinstance(obj, QuickChartFunction): + return '__BEGINFUNCTION__' + obj.script + '__ENDFUNCTION__' + if isinstance(obj, (datetime.date, datetime.datetime)): + return obj.isoformat() + return obj.__dict__ + + +def dump_json(obj): + ret = json.dumps(obj, default=serialize, separators=(',', ':')) + ret = FUNCTION_DELIMITER_RE.sub( + lambda match: json.loads('"' + match.group(1) + '"'), ret) + return ret + + +def demo(): + get_bean(1) + get_chart(1) + + +if __name__ == '__main__': + # re: 增加解析参数,确定调用哪个函数,以及账号id + demo() + pass From 1a0bb311fbf60532e7d5e49d9e1111a1608b2323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 10 Nov 2021 13:20:56 +0800 Subject: [PATCH 103/612] =?UTF-8?q?Revert=20"feat:=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E9=9D=92=E9=BE=99=E7=BB=9F=E8=AE=A1=E8=84=9A=E6=9C=AC"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0d60f38ee3c5a0d037f60d2bfd25341e1cc712a9. --- .gitignore | 2 - qqrobot/scripts/font/jet.ttf | Bin 173264 -> 0 bytes qqrobot/scripts/qinglong_bean_chart.py | 453 ------------------------- 3 files changed, 455 deletions(-) delete mode 100644 qqrobot/scripts/font/jet.ttf delete mode 100644 qqrobot/scripts/qinglong_bean_chart.py diff --git a/.gitignore b/.gitignore index 659b95607..c193f3538 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,3 @@ logs/ resource.syso !global/config/config.yml !qqrobot/config.toml -qqrobot/scripts/bean.jpeg -qqrobot/scripts/chart.jpeg \ No newline at end of file diff --git a/qqrobot/scripts/font/jet.ttf b/qqrobot/scripts/font/jet.ttf deleted file mode 100644 index 0cd1cb66bd193f2b178d0a69a7b0658d76b3a670..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173264 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%0Abi#}=?x9#AzJ05!8Z#!udP+ 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 OwHaMwcmReuwf+ZCw$sx9 diff --git a/qqrobot/scripts/qinglong_bean_chart.py b/qqrobot/scripts/qinglong_bean_chart.py deleted file mode 100644 index 29ca4f7dd..000000000 --- a/qqrobot/scripts/qinglong_bean_chart.py +++ /dev/null @@ -1,453 +0,0 @@ -import datetime -import json -import logging -import re -import time -from datetime import timedelta, timezone -from urllib.parse import urlencode - -import requests -import requests.adapters -from PIL import Image, ImageFont, ImageDraw -from prettytable import PrettyTable - -FONT_FILE = f'font/jet.ttf' -BEAN_IMG = f'bean.jpeg' -CHART_IMG = f'chart.jpeg' -AUTH_JSON = "D:/_codes/js/qinglong/data/config/auth.json" -QL_API_ADDR = "http://127.0.0.1:5700/api" -# QUICK_CHART_ADDR = "https://quickchart.io" -QUICK_CHART_ADDR = "http://127.0.0.1:5703" - -logger = logging.getLogger(__name__) - -SHA_TZ = timezone( - timedelta(hours=8), - name='Asia/Shanghai', -) -requests.adapters.DEFAULT_RETRIES = 5 -session = requests.session() -session.keep_alive = False - - -def get_bean(account_idx: int): - res = get_bean_data(account_idx) - if res['code'] == 200: - creat_bean_count(res['data'][3], res['data'][0], res['data'][1], res['data'][2][1:]) - print(f'您的账号 {account_idx} 收支情况 统计表格 已保存到 {BEAN_IMG}') - - -def get_chart(account_idx: int): - res = get_bean_data(int(account_idx)) - if res['code'] == 200: - creat_chart(res['data'][3], f'账号{str(account_idx)}', res['data'][0], res['data'][1], res['data'][2][1:]) - print(f'您的账号 {account_idx} 收支情况 统计图 已保存到 {CHART_IMG}') - - -def get_bean_data(i): - try: - cookies = get_cks(AUTH_JSON) - if cookies: - ck = cookies[i - 1] - beans_res = get_beans_7days(ck) - beantotal = get_total_beans(ck) - if beans_res['code'] != 200: - return beans_res - else: - beans_in, beans_out = [], [] - beanstotal = [int(beantotal), ] - for i in beans_res['data'][0]: - beantotal = int( - beantotal) - int(beans_res['data'][0][i]) - int(beans_res['data'][1][i]) - beans_in.append(int(beans_res['data'][0][i])) - beans_out.append(int(str(beans_res['data'][1][i]).replace('-', ''))) - beanstotal.append(beantotal) - return {'code': 200, 'data': [beans_in[::-1], beans_out[::-1], beanstotal[::-1], beans_res['data'][2][::-1]]} - except Exception as e: - logger.error(str(e)) - - -def get_cks(ckfile): - ck_reg = re.compile(r'pt_key=\S*?;.*?pt_pin=\S*?;') - - with open(ckfile, 'r', encoding='utf-8') as f: - auth = json.load(f) - lines = str(env_manage_QL('search', 'JD_COOKIE', auth['token'])) - - cookies = ck_reg.findall(lines) - for ck in cookies: - if ck == 'pt_key=xxxxxxxxxx;pt_pin=xxxx;': - cookies.remove(ck) - break - return cookies - - -def env_manage_QL(fun, envdata, token): - url = f'{QL_API_ADDR}/envs' - headers = { - 'Authorization': f'Bearer {token}' - } - try: - if fun == 'search': - params = { - 't': int(round(time.time() * 1000)), - 'searchValue': envdata - } - res = requests.get(url, params=params, headers=headers).json() - elif fun == 'add': - data = { - 'name': envdata['name'], - 'value': envdata['value'], - 'remarks': envdata['remarks'] if 'remarks' in envdata.keys() else '' - } - res = requests.post(url, json=[data], headers=headers).json() - elif fun == 'edit': - data = { - 'name': envdata['name'], - 'value': envdata['value'], - '_id': envdata['_id'], - 'remarks': envdata['remarks'] if 'remarks' in envdata.keys() else '' - } - res = requests.put(url, json=data, headers=headers).json() - elif fun == 'disable': - data = [envdata['_id']] - res = requests.put(url + '/disable', json=data, - headers=headers).json() - elif fun == 'enable': - data = [envdata['_id']] - res = requests.put(url + '/enable', json=data, - headers=headers).json() - elif fun == 'del': - data = [envdata['_id']] - res = requests.delete(url, json=data, headers=headers).json() - else: - res = {'code': 400, 'data': '未知功能'} - except Exception as e: - res = {'code': 400, 'data': str(e)} - finally: - return res - - -def get_beans_7days(ck): - try: - day_7 = True - page = 0 - headers = { - "Host": "api.m.jd.com", - "Connection": "keep-alive", - "charset": "utf-8", - "User-Agent": "Mozilla/5.0 (Linux; Android 10; MI 9 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2797 MMWEBSDK/201201 Mobile Safari/537.36 MMWEBID/7986 MicroMessenger/8.0.1840(0x2800003B) Process/appbrand4 WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android", - "Content-Type": "application/x-www-form-urlencoded;", - "Accept-Encoding": "gzip, compress, deflate, br", - "Cookie": ck, - "Referer": "https://servicewechat.com/wxa5bf5ee667d91626/141/page-frame.html", - } - days = [] - for i in range(0, 7): - days.append( - (datetime.date.today() - datetime.timedelta(days=i)).strftime("%Y-%m-%d")) - beans_in = {key: 0 for key in days} - beans_out = {key: 0 for key in days} - while day_7: - page = page + 1 - resp = session.get("https://api.m.jd.com/api", params=gen_params(page), - headers=headers, timeout=100).text - res = json.loads(resp) - if res['resultCode'] == 0: - for i in res['data']['list']: - for date in days: - if str(date) in i['createDate'] and i['amount'] > 0: - beans_in[str(date)] = beans_in[str( - date)] + i['amount'] - break - elif str(date) in i['createDate'] and i['amount'] < 0: - beans_out[str(date)] = beans_out[str( - date)] + i['amount'] - break - if i['createDate'].split(' ')[0] not in str(days): - day_7 = False - else: - return {'code': 400, 'data': res} - return {'code': 200, 'data': [beans_in, beans_out, days]} - except Exception as e: - logger.error(str(e)) - return {'code': 400, 'data': str(e)} - - -def gen_params(page): - body = gen_body(page) - params = { - "functionId": "jposTradeQuery", - "appid": "swat_miniprogram", - "client": "tjj_m", - "sdkName": "orderDetail", - "sdkVersion": "1.0.0", - "clientVersion": "3.1.3", - "timestamp": int(round(time.time() * 1000)), - "body": json.dumps(body) - } - return params - - -def gen_body(page): - body = { - "beginDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), - "endDate": datetime.datetime.utcnow().replace(tzinfo=timezone.utc).astimezone(SHA_TZ).strftime("%Y-%m-%d %H:%M:%S"), - "pageNo": page, - "pageSize": 20, - } - return body - - -def get_total_beans(ck): - try: - headers = { - "Host": "wxapp.m.jd.com", - "Connection": "keep-alive", - "charset": "utf-8", - "User-Agent": "Mozilla/5.0 (Linux; Android 10; MI 9 Build/QKQ1.190825.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2797 MMWEBSDK/201201 Mobile Safari/537.36 MMWEBID/7986 MicroMessenger/8.0.1840(0x2800003B) Process/appbrand4 WeChat/arm64 Weixin NetType/4G Language/zh_CN ABI/arm64 MiniProgramEnv/android", - "Content-Type": "application/x-www-form-urlencoded;", - "Accept-Encoding": "gzip, compress, deflate, br", - "Cookie": ck, - } - jurl = "https://wxapp.m.jd.com/kwxhome/myJd/home.json" - resp = session.get(jurl, headers=headers, timeout=100).text - res = json.loads(resp) - return res['user']['jingBean'] - except Exception as e: - logger.error(str(e)) - - -def creat_bean_count(date, beansin, beansout, beanstotal): - tb = PrettyTable() - tb.add_column('DATE', date) - tb.add_column('BEANSIN', beansin) - tb.add_column('BEANSOUT', beansout) - tb.add_column('TOTAL', beanstotal) - font = ImageFont.truetype(FONT_FILE, 18) - im = Image.new("RGB", (500, 260), (244, 244, 244)) - dr = ImageDraw.Draw(im) - dr.text((10, 5), str(tb), font=font, fill="#000000") - im.save(BEAN_IMG) - - -def creat_chart(xdata, title, bardata, bardata2, linedate): - qc = QuickChart() - qc.background_color = '#fff' - qc.width = "1000" - qc.height = "600" - qc.config = { - "type": "bar", - "data": { - "labels": xdata, - "datasets": [ - { - "label": "IN", - "backgroundColor": [ - "rgb(255, 99, 132)", - "rgb(255, 159, 64)", - "rgb(255, 205, 86)", - "rgb(75, 192, 192)", - "rgb(54, 162, 235)", - "rgb(153, 102, 255)", - "rgb(255, 99, 132)" - ], - "yAxisID": "y1", - "data": bardata - }, - { - "label": "OUT", - "backgroundColor": [ - "rgb(255, 99, 132)", - "rgb(255, 159, 64)", - "rgb(255, 205, 86)", - "rgb(75, 192, 192)", - "rgb(54, 162, 235)", - "rgb(153, 102, 255)", - "rgb(255, 99, 132)" - ], - "yAxisID": "y1", - "data": bardata2 - }, - { - "label": "TOTAL", - "type": "line", - "fill": False, - "backgroundColor": "rgb(201, 203, 207)", - "yAxisID": "y2", - "data": linedate - } - ] - }, - "options": { - "plugins": { - "datalabels": { - "anchor": 'end', - "align": -100, - "color": '#666', - "font": { - "size": 20, - } - }, - }, - "legend": { - "labels": { - "fontSize": 20, - "fontStyle": 'bold', - } - }, - "title": { - "display": True, - "text": f'{title} 收支情况', - "fontSize": 24, - }, - "scales": { - "xAxes": [{ - "ticks": { - "fontSize": 24, - } - }], - "yAxes": [ - { - "id": "y1", - "type": "linear", - "display": False, - "position": "left", - "ticks": { - "max": int(int(max([max(bardata), max(bardata2)]) + 100) * 2) - }, - "scaleLabel": { - "fontSize": 20, - "fontStyle": 'bold', - } - }, - { - "id": "y2", - "type": "linear", - "display": False, - "ticks": { - "min": int(min(linedate) * 2 - (max(linedate)) - 100), - "max": int(int(max(linedate))) - }, - "position": "right" - } - ] - } - } - } - qc.to_file(CHART_IMG) - - -class QuickChart: - def __init__(self): - self.config = None - self.width = 500 - self.height = 300 - self.background_color = '#ffffff' - self.device_pixel_ratio = 1.0 - self.format = 'png' - self.version = '2.9.4' - self.key = None - # self.scheme = 'https' - # self.host = 'quickchart.io' - - def is_valid(self): - return self.config is not None - - def get_url_base(self): - return QUICK_CHART_ADDR - - def get_url(self): - if not self.is_valid(): - raise RuntimeError( - 'You must set the `config` attribute before generating a url') - params = { - 'c': dump_json(self.config) if type(self.config) == dict else self.config, - 'w': self.width, - 'h': self.height, - 'bkg': self.background_color, - 'devicePixelRatio': self.device_pixel_ratio, - 'f': self.format, - 'v': self.version, - } - if self.key: - params['key'] = self.key - return '%s/chart?%s' % (self.get_url_base(), urlencode(params)) - - def _post(self, url): - try: - import requests - except: - raise RuntimeError('Could not find `requests` dependency') - - postdata = { - 'chart': dump_json(self.config) if type(self.config) == dict else self.config, - 'width': self.width, - 'height': self.height, - 'backgroundColor': self.background_color, - 'devicePixelRatio': self.device_pixel_ratio, - 'format': self.format, - 'version': self.version, - } - if self.key: - postdata['key'] = self.key - st = time.time() - resp = requests.post(url, json=postdata) - if resp.status_code != 200: - raise RuntimeError( - 'Invalid response code from chart creation endpoint') - return resp - - def get_short_url(self): - resp = self._post('%s/chart/create' % self.get_url_base()) - parsed = json.loads(resp.text) - if not parsed['success']: - raise RuntimeError( - 'Failure response status from chart creation endpoint') - return parsed['url'] - - def get_bytes(self): - resp = self._post('%s/chart' % self.get_url_base()) - return resp.content - - def to_file(self, path): - content = self.get_bytes() - with open(path, 'wb') as f: - f.write(content) - - -FUNCTION_DELIMITER_RE = re.compile('\"__BEGINFUNCTION__(.*?)__ENDFUNCTION__\"') - - -class QuickChartFunction: - def __init__(self, script): - self.script = script - - def __repr__(self): - return self.script - - -def serialize(obj): - if isinstance(obj, QuickChartFunction): - return '__BEGINFUNCTION__' + obj.script + '__ENDFUNCTION__' - if isinstance(obj, (datetime.date, datetime.datetime)): - return obj.isoformat() - return obj.__dict__ - - -def dump_json(obj): - ret = json.dumps(obj, default=serialize, separators=(',', ':')) - ret = FUNCTION_DELIMITER_RE.sub( - lambda match: json.loads('"' + match.group(1) + '"'), ret) - return ret - - -def demo(): - get_bean(1) - get_chart(1) - - -if __name__ == '__main__': - # re: 增加解析参数,确定调用哪个函数,以及账号id - demo() - pass From bf5f0a82b69510be018567899d31427f8caa85aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 10 Nov 2021 15:25:18 +0800 Subject: [PATCH 104/612] =?UTF-8?q?feat:=20=E5=B0=86=E5=88=86=E7=89=87?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E5=90=91=E4=B8=8B=E7=A7=BB=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E5=9C=A8=E5=9B=BE=E7=89=87=E7=AD=89=E5=A4=9A=E5=AA=92?= =?UTF-8?q?=E4=BD=93=E6=B6=88=E6=81=AF=E5=AE=9E=E9=99=85=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=90=8E=E5=86=8D=E8=AE=A1=E7=AE=97=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/api.go | 20 +------------------- coolq/bot.go | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index f34568f45..64be1b889 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -425,25 +425,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b } } fixAt(elem) - - var mid int32 - - // 魔改一下,兼容过长的消息,使得青龙面板那边可以发送很多个账号的统计信息 - msg := &message.SendingMessage{Elements: elem} - msgLen := message.EstimateLength(msg.Elements) - if msgLen <= MaxMessageSize { - mid = bot.SendGroupMessage(groupID, msg) - } else { - // 特殊处理过长的消息,此时mid返回第一个消息 - parts := SplitLongMessage(msg) - for idx, part := range parts { - ret := bot.SendGroupMessage(groupID, part) - if idx == 0 { - mid = ret - } - } - } - + mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem}) if mid == -1 { return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") } diff --git a/coolq/bot.go b/coolq/bot.go index 2d5e49de4..c0230e078 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -296,7 +296,24 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int } m.Elements = newElem bot.checkMedia(newElem) - ret := bot.Client.SendGroupMessage(groupID, m, ForceFragmented) + + var ret *message.GroupMessage + + // 魔改一下,兼容过长的消息,使得青龙面板那边可以发送很多个账号的统计信息 + msgLen := message.EstimateLength(m.Elements) + if msgLen <= MaxMessageSize { + ret = bot.Client.SendGroupMessage(groupID, m, ForceFragmented) + } else { + // 特殊处理过长的消息,此时mid返回第一个消息 + parts := SplitLongMessage(m) + for idx, part := range parts { + _ret := bot.Client.SendGroupMessage(groupID, part, ForceFragmented) + if idx == 0 { + ret = _ret + } + } + } + if ret == nil || ret.Id == -1 { log.Warnf("群消息发送失败: 账号可能被风控.\ngroupID= %v 消息内容= %v", groupID, message.ToReadableString(m.Elements)) return -1 From 48def2e5f243c7f1b8a03f17c64ef398b9f04b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 10 Nov 2021 16:31:56 +0800 Subject: [PATCH 105/612] =?UTF-8?q?feat:=20=E5=88=86=E7=89=87=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E6=B6=88=E6=81=AF=E5=8F=91=E9=80=81=E4=B9=8B=E9=97=B4?= =?UTF-8?q?=E7=AD=89=E5=BE=85500ms=EF=BC=8C=E5=B0=BD=E9=87=8F=E6=94=B6?= =?UTF-8?q?=E5=8F=91=E7=A1=AE=E4=BF=9D=E9=A1=BA=E5=BA=8F=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/coolq/bot.go b/coolq/bot.go index c0230e078..911e5486e 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -311,6 +311,10 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int if idx == 0 { ret = _ret } + if idx < len(parts)-1 { + // 等待一会,尽量确保收到的顺序与发送一致 + time.Sleep(500 * time.Millisecond) + } } } From 0055cad49aa1887cddb2830749eea416e0a8af62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 12 Nov 2021 23:10:26 +0800 Subject: [PATCH 106/612] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E5=85=AC=E5=91=8A=E7=9A=84=E6=AD=A3=E5=88=99=E8=A1=A8?= =?UTF-8?q?=E8=BE=BE=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 41c63fbaf..4310d79f6 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -101,7 +101,7 @@ func (r *QQRobot) initCheckUpdateVersionMap() { } var regGitVersion = regexp.MustCompile(`([vV][0-9.]+)(\s+\d+\.\d+\.\d+)`) -var regUpdateInfo = regexp.MustCompile(`(更新公告)\s*
      ((\s|\S)+?)
    `) +var regUpdateInfo = regexp.MustCompile(`(更新公告)\s*((\s|\S)+?)`) var regUpdateMessages = regexp.MustCompile("
  • (.+?)
  • ") // VersionNone 默认版本号 From b26e908f750d32b11a639338890f4ae50e12d061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 14 Nov 2021 16:19:03 +0800 Subject: [PATCH 107/612] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E5=86=B2?= =?UTF-8?q?=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 2 +- global/config/config.yml | 91 ----------------- go.mod | 44 ++++----- go.sum | 207 ++++++++------------------------------- main.go | 3 +- modules/config/config.go | 2 +- 6 files changed, 63 insertions(+), 286 deletions(-) delete mode 100644 global/config/config.yml diff --git a/coolq/bot.go b/coolq/bot.go index 4aed9d152..d206a8620 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -239,7 +239,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int return -1 } m.Elements = newElem - bot.checkMedia(newElem) + bot.checkMedia(newElem, groupID) var ret *message.GroupMessage diff --git a/global/config/config.yml b/global/config/config.yml deleted file mode 100644 index fd692bfd0..000000000 --- a/global/config/config.yml +++ /dev/null @@ -1,91 +0,0 @@ -# go-cqhttp 默认配置文件 - -account: # 账号相关 - uin: 1233456 # QQ账号 - password: '' # 密码为空时使用扫码登录 - encrypt: false # 是否开启密码加密 - status: 0 # 在线状态 请参考 https://docs.go-cqhttp.org/guide/config.html#在线状态 - relogin: # 重连设置 - delay: 3 # 首次重连延迟, 单位秒 - interval: 3 # 重连间隔 - max-times: 0 # 最大重连次数, 0为无限制 - - # 是否使用服务器下发的新地址进行重连 - # 注意, 此设置可能导致在海外服务器上连接情况更差 - use-sso-address: true - - fast-start: true # 是否快速启动 - -heartbeat: - # 心跳频率, 单位秒 - # -1 为关闭心跳 - interval: 5 - -message: - # 上报数据类型 - # 可选: string,array - post-format: string - # 是否忽略无效的CQ码, 如果为假将原样发送 - ignore-invalid-cqcode: false - # 是否强制分片发送消息 - # 分片发送将会带来更快的速度 - # 但是兼容性会有些问题 - force-fragment: false - # 是否将url分片发送 - fix-url: false - # 下载图片等请求网络代理 - proxy-rewrite: '' - # 是否上报自身消息 - report-self-message: false - # 移除服务端的Reply附带的At - remove-reply-at: false - # 为Reply附加更多信息 - extra-reply-data: false - # 跳过 Mime 扫描, 忽略错误数据 - skip-mime-scan: false - -output: - # 日志等级 trace,debug,info,warn,error - log-level: warn - # 日志时效 单位天. 超过这个时间之前的日志将会被自动删除. 设置为 0 表示永久保留. - log-aging: 15 - # 是否在每次启动时强制创建全新的文件储存日志. 为 false 的情况下将会在上次启动时创建的日志文件续写 - log-force-new: true - # 是否启用 DEBUG - debug: false # 开启调试模式 - -# 默认中间件锚点 -default-middlewares: &default - # 访问密钥, 强烈推荐在公网的服务器设置 - access-token: '' - # 事件过滤器文件目录 - filter: '' - # API限速设置 - # 该设置为全局生效 - # 原 cqhttp 虽然启用了 rate_limit 后缀, 但是基本没插件适配 - # 目前该限速设置为令牌桶算法, 请参考: - # https://baike.baidu.com/item/%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95/6597000?fr=aladdin - rate-limit: - enabled: false # 是否启用限速 - frequency: 1 # 令牌回复频率, 单位秒 - bucket: 1 # 令牌桶大小 - -database: # 数据库相关设置 - leveldb: - # 是否启用内置leveldb数据库 - # 启用将会增加10-20MB的内存占用和一定的磁盘空间 - # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 - enable: true - - # 媒体文件缓存, 删除此项则使用缓存文件(旧版行为) - cache: - image: data/image.db - video: data/video.db - -# 连接服务列表 -servers: - # 添加方式,同一连接方式可添加多个,具体配置说明请查看文档 - #- http: # http 通信 - #- ws: # 正向 Websocket - #- ws-reverse: # 反向 Websocket - #- pprof: #性能分析服务器 diff --git a/go.mod b/go.mod index 2cfefcd8f..df49c2e39 100644 --- a/go.mod +++ b/go.mod @@ -2,33 +2,33 @@ module github.com/Mrs4s/go-cqhttp go 1.17 -replace github.com/Mrs4s/go-cqhttp => github.com/fzls/qq_robot_go v1.0.0-beta7-fix2 +replace github.com/Mrs4s/go-cqhttp => github.com/fzls/qq_robot_go v1.0.0-beta8 replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2.0 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f - github.com/BurntSushi/toml v0.4.1 - github.com/Microsoft/go-winio v0.5.0 - github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a + github.com/BurntSushi/toml v0.3.1 + github.com/Microsoft/go-winio v0.5.1 + github.com/Mrs4s/MiraiGo v0.0.0-20211112183036-d3a21e577b02 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/fzls/logger v1.1.1 - github.com/gabriel-vasile/mimetype v1.3.1 - github.com/gookit/color v1.4.2 + github.com/gabriel-vasile/mimetype v1.4.0 + github.com/gookit/color v1.5.0 github.com/hashicorp/golang-lru v0.5.4 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/klauspost/compress v1.13.6 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/mattn/go-colorable v0.1.11 github.com/pkg/errors v0.9.1 - github.com/segmentio/asm v1.0.1 + github.com/segmentio/asm v1.1.0 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264 - github.com/tidwall/gjson v1.9.1 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 + github.com/tidwall/gjson v1.11.0 github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 go.mongodb.org/mongo-driver v1.7.2 @@ -41,23 +41,19 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gin-gonic/gin v1.7.0 // indirect - github.com/go-playground/validator/v10 v10.9.0 // indirect github.com/go-stack/stack v1.8.0 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/uuid v1.1.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/klauspost/cpuid/v2 v2.0.6 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect - github.com/maruel/rs v1.0.0 // indirect + github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect - github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect - github.com/tidwall/match v1.0.3 // indirect + github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/willf/bitset v1.2.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect @@ -65,14 +61,12 @@ require ( github.com/xdg-go/stringprep v1.0.2 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect + golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect golang.org/x/text v0.3.6 // indirect google.golang.org/protobuf v1.27.1 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - modernc.org/libc v1.11.22 // indirect - modernc.org/mathutil v1.4.1 // indirect - modernc.org/memory v1.0.5 // indirect + modernc.org/libc v1.8.1 // indirect + modernc.org/mathutil v1.2.2 // indirect + modernc.org/memory v1.0.4 // indirect ) diff --git a/go.sum b/go.sum index 133080966..f1c77ea23 100644 --- a/go.sum +++ b/go.sum @@ -1,48 +1,36 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a h1:D2zG/7w7q5QbDeHSx9n4lS9cv9MGBqEjNTzeVO5g3Mo= -github.com/Mrs4s/MiraiGo v0.0.0-20210928143029-dff336b05a9a/go.mod h1:EpB6wQ+iaIqLRxVrs6si2J3XGv6wHXMrRXNUnJFaoww= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Mrs4s/MiraiGo v0.0.0-20211112183036-d3a21e577b02 h1:D2a93R7eugdAfuZtVXYznjqGrKlfvtWQ0YTHnwJYq8U= +github.com/Mrs4s/MiraiGo v0.0.0-20211112183036-d3a21e577b02/go.mod h1:UOO08UBb5mVisfwrjduINu5GF9+gmsn33f2tnr83xnE= github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fzls/logger v1.1.1 h1:9ZteZN4pxh16MVsjlNgCurQb6qBDxKYDKKq7zifWHIU= github.com/fzls/logger v1.1.1/go.mod h1:FBZoU1PuXNCQBNr19NZjUWCq1vV6/FoGwc3D9q6of/0= -github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= -github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= +github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= +github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU= -github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A= -github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -75,39 +63,24 @@ github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk= -github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -125,27 +98,21 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uia github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= @@ -154,9 +121,8 @@ github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4 h1:u9jwvcKbQpghIXgNl/EOL8hzhAFXh4ePrEP493W3tNA= github.com/maruel/rs v0.0.0-20150922171536-2c81c4312fe4/go.mod h1:kcRFpEzolcEklV6rD7W95mG49/sbdX/PlFmd7ni3RvA= -github.com/maruel/rs v1.0.0 h1:NoRR74bfxQy8CzWdkwYb5Rz+H93sl30GSJIvf6ADZZg= -github.com/maruel/rs v1.0.0/go.mod h1:F3JiIVIlJdcXIW+Ep10ZeiPAwKwSA5IQ6QTpILEE/aM= github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -173,24 +139,19 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/segmentio/asm v1.0.1 h1:g9VK62hXylgXI4yJV+dLTu/1j7kTxG9bkUSYBxL9dpg= -github.com/segmentio/asm v1.0.1/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4= +github.com/segmentio/asm v1.1.0 h1:fkVr8k5J4sKoFjTGVD6r1yKvDKqmvrEh3K7iyVxgBs8= +github.com/segmentio/asm v1.1.0/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -211,17 +172,15 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264 h1:USf7I8ohzoqCwAcCHz5rz0hD57mmNXZgrLfxFLx1WmA= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.264/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264 h1:kGT41c6hLBFfiTjacWDpq62ePSnx1/9+MXrpMZII2Uk= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.264/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= -github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= -github.com/tidwall/gjson v1.9.1 h1:wrrRk7TyL7MmKanNRck/Mcr3VU1sdMvJHvJXzqBIUNo= -github.com/tidwall/gjson v1.9.1/go.mod h1:jydLKE7s8J0+1/5jC4eXcuFlzKizGrCKvLmBVX/5oXc= -github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= -github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 h1:2yISRb1sPRr0lUye5M/Co4uvyj7Y1JfF/7nq2vayF4g= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 h1:m7y3kw3JfAmyIiz/mwWEcuV+WayjrxrfZpiqW94uJU4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= +github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4= +github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 h1:BWVtt2VBY+lmVDu9MGKqLGKl04B+iRHcrW1Ptyi/8tg= @@ -232,7 +191,6 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4= github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= @@ -243,45 +201,28 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHg github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.mongodb.org/mongo-driver v1.7.2 h1:pFttQyIiJUHEn50YfZgC9ECjITMT44oiN36uArf/OFg= go.mongodb.org/mongo-driver v1.7.2/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -292,7 +233,6 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -300,8 +240,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -317,44 +255,18 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -362,56 +274,17 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= -modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= -modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= -modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= -modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= -modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= -modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= -modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= -modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= -modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= -modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= -modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= -modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= -modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= -modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= -modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= -modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= -modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= -modernc.org/libc v1.11.22 h1:u2ek1qgzki0RUDI2kW3EC9VKHP9LXKxKnoQLZmIeCbA= -modernc.org/libc v1.11.22/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= -modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/main.go b/main.go index 61f8efd04..5fe274d2d 100644 --- a/main.go +++ b/main.go @@ -343,7 +343,8 @@ func main() { } cli.SetOnlineStatus(allowStatus[base.Account.Status]) - servers.Run(coolq.NewQQBot(cli)) + bot := coolq.NewQQBot(cli) + servers.Run(bot) log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") diff --git a/modules/config/config.go b/modules/config/config.go index 95e8de8e1..72a39b0af 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -16,7 +16,7 @@ import ( ) // defaultConfig 默认配置文件 -//go:embed config.yml +//go:embed default_config.yml var defaultConfig string // Reconnect 重连配置 From b02af984b635633aaabf063e28355addbcf754b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 14 Nov 2021 16:19:11 +0800 Subject: [PATCH 108/612] fix: lint --- coolq/converter.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/coolq/converter.go b/coolq/converter.go index 7e722495b..634f8b657 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -1,11 +1,13 @@ package coolq import ( + "strconv" + "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" - "github.com/Mrs4s/go-cqhttp/global" log "github.com/sirupsen/logrus" - "strconv" + + "github.com/Mrs4s/go-cqhttp/global" ) func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG { From 8151d5a9d67b7a6e9aad158fd88afd8c2663a9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 14 Nov 2021 16:28:18 +0800 Subject: [PATCH 109/612] =?UTF-8?q?feat:=20=E8=A1=A5=E5=85=85=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E5=85=B3=E4=BA=8Edns=E7=9A=84=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index cde2ae555..684a735e5 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,16 @@ 复制qqrobot/config.toml到qq_robot.exe所在目录 然后打开qq_robot/config.go和config.toml,按照注释,自行调整配置,并添加各种规则 +# 注意 +## 腾讯云部署 +需要修改dns为 +101.6.6.6 +223.6.6.6 +114.114.114.114 + +不能使用8.8.8.8,已经被腾讯云污染了 +具体可见 https://github.com/Mrs4s/go-cqhttp/issues/1115#issuecomment-949298739 + # TODO - [ ] 看看是否有更多事件值得接入,目前仅处理了群聊、私聊和加群这三个事件 From aa712ed4acd4914e09b034a9bcc3c02e74e3d50f Mon Sep 17 00:00:00 2001 From: sam01101 Date: Tue, 16 Nov 2021 19:25:59 +0800 Subject: [PATCH 110/612] feat: Update bug template --- .github/ISSUE_TEMPLATE/bug--.md | 46 ------ .github/ISSUE_TEMPLATE/bug-report.yaml | 189 +++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 46 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug--.md create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yaml diff --git a/.github/ISSUE_TEMPLATE/bug--.md b/.github/ISSUE_TEMPLATE/bug--.md deleted file mode 100644 index aa54100a1..000000000 --- a/.github/ISSUE_TEMPLATE/bug--.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Bug汇报 -about: 遇到了bug? 你可以在这里开始汇报(仅限软件本体问题) -title: '' -labels: bug? -assignees: '' - ---- - - - -**环境信息** - -go-cqhttp版本: -运行环境: -连接方式: -使用协议: - -**在最新的release版本中能否复现** - -**bug内容** - - -**复现方法** - diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml new file mode 100644 index 000000000..3a7eff21f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -0,0 +1,189 @@ +name: 回报错误 +description: 在使用 go-cqhttp 的过程中遇到了错误 +title: '[Bug]: ' +labels: [ "bug?" ] + +body: + # User's README and agreement + - type: markdown + attributes: + value: | + ## 感谢您愿意填写错误回报! + ## 以下是一些注意事项,请务必阅读让我们能够更容易处理 + + ### ❗ | 确定没有相同问题的ISSUE已被提出. (教程: https://github.com/Mrs4s/go-cqhttp/issues/633) + ### 🌎| 请准确填写环境信息 + ### ❔ | 打开DEBUG模式复现,并提供出现问题前后至少 10 秒的完整日志内容。请自行删除日志内存在的个人信息及敏感内容。 + ### ⚠ | 如果涉及内存泄漏/CPU占用异常请打开DEBUG模式并下载pprof性能分析. + + ## 如果您不知道如何有效、精准地表述,我们建议您先阅读《提问的智慧》 + 链接: [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md) + --- + - type: checkboxes + id: terms + attributes: + label: 请确保您已阅读以上注意事项,并勾选下方的确认框。 + options: + - label: "我已经仔细阅读上述教程和 [\"提问前需知\"](https://github.com/Mrs4s/go-cqhttp/issues/633)" + required: true + - label: 我已知晓并同意,如果我不遵循以下格式提交 Issue,或者我使用的并非最新版本,或者我没有提供足够的环境信息,我的 Issue 可能会被无条件自动关闭或/并锁定。 + required: true + - label: 我已知晓并同意,此处仅用于汇报程序中存在的问题。若这个 Issue 是关于其他非程序本身问题,则我的 Issue 可能会被无条件自动关闭或/并锁定。(这些问题应当在 Discussion 板块提出。) + required: true + + # User's data + - type: markdown + attributes: + value: | + ## 环境信息 + 请根据实际使用环境修改以下信息。 + + # Env | go-cqhttp Version + - type: dropdown + id: env-gocq-ver + attributes: + label: go-cqhttp 版本 + description: 选择正在使用的版本 (未列出的版本为已不支持,请更新到最新版本。) + options: + - v1.0.0-beta8-fix1 + - v1.0.0-beta8 + - v1.0.0-beta7-fix2 + - v1.0.0-beta7-fix1 + - v1.0.0-beta7 + - v1.0.0-beta6 + - v1.0.0-beta5 + - v1.0.0-beta4 + - v1.0.0-beta3 + - v1.0.0-beta2 + - v1.0.0-beta1 + - 更老的版本(请在下面填写) + validations: + required: true + - type: input + id: env-gocq-ver-other + attributes: + label: 旧的 go-cqhttp 版本 (请注意,我们对更低版本的 go-cqhttp 不提供完全支持;您的 Issue 有可能会被关闭。) + + # Env | VM Version + - type: dropdown + id: env-vm-ver + attributes: + label: 运行环境 + description: 选择运行 go-cqhttp 的系统版本 + options: + - Windows (64) + - Windows (32/x84) + - MacOS + - Linux + - Ubuntu + - CentOS + - ArchLinux + - UNIX (Android) + - 其它(请在下面填写) + validations: + required: true + - type: input + id: env-vm-ver-other + attributes: + label: 其它系统版本 + + # Env | VM Arch + - type: dropdown + id: env-vm-arch + attributes: + label: 运行架构 + description: (可选) 选择运行 go-cqhttp 的系统架构 + options: + - AMD + - x86 + - ARM [32] (别名:AArch32 / ARMv7) + - ARM [64] (别名:AArch64 / ARMv8) + - 其它(请在下面填写) + - type: input + id: env-vm-arch-other + attributes: + label: 其它系统架构 + + # Env | Connection type + - type: dropdown + id: env-conn-type + attributes: + label: 连接方式 + description: 选择对接机器人的连接方式 + options: + - HTTP + - WebSocket (正向) + - WebSocket (反向) + - LambdaServer + validations: + required: true + + # Env | Protocol + - type: dropdown + id: env-protocol + attributes: + label: 使用协议 + description: 选择使用的协议 + options: + - 0 | iPad + - 1 | Android Phone + - 2 | Android Watch + - 3 | MacOS + - 4 | 企点 + validations: + required: true + + # Optional | Reproduce possible + - type: checkboxes + id: reproduce-possible + attributes: + label: 在最新的 Release 版本中能否复现 + options: + - label: 能 + + # Input | Reproduce + - type: textarea + id: reproduce-steps + attributes: + label: 重现步骤 + description: | + 我们需要执行哪些操作才能让 bug 出现? + 简洁清晰的重现步骤能够帮助我们更迅速地定位问题所在。 + validations: + required: true + + # Input | Expected result + - type: textarea + id: expected + attributes: + label: 期望的结果是什么? + validations: + required: true + + # Input | Actual result + - type: textarea + id: actual + attributes: + label: 实际的结果是什么? + validations: + required: true + + # Optional | Reproduce code + - type: textarea + id: reproduce-code + attributes: + label: 简单的复现代码/链接(可选) + render: golang + + # Optional | Logging + - type: textarea + id: logging + attributes: + label: 日志记录(可选) + render: golang + + # Optional | Extra description + - type: textarea + id: extra-desc + attributes: + label: 补充说明(可选) From ea2bda523f6557e2f1c3c98d9107c525bb6759f7 Mon Sep 17 00:00:00 2001 From: wasupandceacar Date: Tue, 16 Nov 2021 21:36:23 -0500 Subject: [PATCH 111/612] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20HTTP=20POST=20?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E7=B1=BB=E5=9E=8B=E4=B8=BA=20json=20(#1168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/http.go b/server/http.go index 3b72a7bb6..3d6f5211e 100644 --- a/server/http.go +++ b/server/http.go @@ -239,6 +239,7 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { req, _ := http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) req.Header.Set("X-Self-ID", strconv.FormatInt(c.bot.Client.Uin, 10)) req.Header.Set("User-Agent", "CQHttp/4.15.0") + req.Header.Set("Content-Type", "application/json") if c.secret != "" { mac := hmac.New(sha1.New, []byte(c.secret)) _, _ = mac.Write(e.JSONBytes()) From dba2bf288108e3f562599878bf53f9da7137f9a8 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 17 Nov 2021 11:41:28 +0800 Subject: [PATCH 112/612] fix: fix ws-reverse connect info --- server/websocket.go | 130 ++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 71 deletions(-) diff --git a/server/websocket.go b/server/websocket.go index 5d75b9207..525d4694a 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -39,15 +39,15 @@ type webSocketServer struct { // websocketClient WebSocket客户端实例 type websocketClient struct { - bot *coolq.CQBot - conf *config.WebsocketReverse - - mu sync.Mutex - + bot *coolq.CQBot + mu sync.Mutex universal *wsConn event *wsConn - token string - filter string + + token string + filter string + reconnectInterval time.Duration + limiter api.Handler } type wsConn struct { @@ -81,10 +81,8 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) { mux.HandleFunc("/event", s.event) mux.HandleFunc("/api", s.api) mux.HandleFunc("/", s.any) - go func() { - log.Infof("CQ WebSocket 服务器已启动: %v", addr) - log.Fatal(http.ListenAndServe(addr, &mux)) - }() + log.Infof("CQ WebSocket 服务器已启动: %v", addr) + log.Fatal(http.ListenAndServe(addr, &mux)) } // runWSClient 运行一个反向向WS client @@ -100,26 +98,33 @@ func runWSClient(b *coolq.CQBot, node yaml.Node) { c := &websocketClient{ bot: b, - conf: &conf, token: conf.AccessToken, filter: conf.Filter, } filter.Add(c.filter) - if c.conf.Universal != "" { + if conf.ReconnectInterval != 0 { + c.reconnectInterval = time.Duration(conf.ReconnectInterval) * time.Millisecond + } + if conf.RateLimit.Enabled { + c.limiter = rateLimit(conf.RateLimit.Frequency, conf.RateLimit.Bucket) + } + + if conf.Universal != "" { c.connect("Universal", conf.Universal, &c.universal) - } else { - if c.conf.API != "" { - c.connect("API", conf.API, nil) - } - if c.conf.Event != "" { - c.connect("Event", conf.Event, &c.event) - } + c.bot.OnEventPush(c.onBotPushEvent("Universal", conf.Universal, &c.universal)) + return // 连接到 Universal 后, 不再连接其他 + } + if conf.API != "" { + c.connect("API", conf.API, nil) + } + if conf.Event != "" { + c.connect("Event", conf.Event, &c.event) + c.bot.OnEventPush(c.onBotPushEvent("Event", conf.Event, &c.event)) } - c.bot.OnEventPush(c.onBotPushEvent) } func (c *websocketClient) connect(typ, url string, conptr **wsConn) { - log.Infof("开始尝试连接到反向WebSocket %s服务器: %v", typ, c.conf.API) + log.Infof("开始尝试连接到反向WebSocket %s服务器: %v", typ, url) header := http.Header{ "X-Client-Role": []string{typ}, "X-Self-ID": []string{strconv.FormatInt(c.bot.Client.Uin, 10)}, @@ -130,9 +135,9 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { } conn, _, err := websocket.Dial(context.Background(), url, &websocket.DialOptions{HTTPHeader: header}) // nolint if err != nil { - log.Warnf("连接到反向WebSocket %s服务器 %v 时出现错误: %v", typ, c.conf.API, err) - if c.conf.ReconnectInterval != 0 { - time.Sleep(time.Millisecond * time.Duration(c.conf.ReconnectInterval)) + log.Warnf("连接到反向WebSocket %s服务器 %v 时出现错误: %v", typ, url, err) + if c.reconnectInterval != 0 { + time.Sleep(c.reconnectInterval) c.connect(typ, url, conptr) } return @@ -147,37 +152,32 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { } } - log.Infof("已连接到反向WebSocket %s服务器 %v", typ, c.conf.API) + log.Infof("已连接到反向WebSocket %s服务器 %v", typ, url) wrappedConn := &wsConn{Conn: conn, apiCaller: api.NewCaller(c.bot)} - if c.conf.RateLimit.Enabled { - wrappedConn.apiCaller.Use(rateLimit(c.conf.RateLimit.Frequency, c.conf.RateLimit.Bucket)) + if c.limiter != nil { + wrappedConn.apiCaller.Use(c.limiter) } if conptr != nil { *conptr = wrappedConn } - switch typ { - case "API": - go c.listenAPI(wrappedConn, false) - case "Universal": - go c.listenAPI(wrappedConn, true) - } + go c.listenAPI(typ, url, wrappedConn) } -func (c *websocketClient) listenAPI(conn *wsConn, u bool) { +func (c *websocketClient) listenAPI(typ, url string, conn *wsConn) { defer func() { _ = conn.Close(websocket.StatusNormalClosure, "") }() conn.Conn.SetReadLimit(1024 * 1024 * 128) for { buffer := global.NewBuffer() t, reader, err := conn.Conn.Reader(context.Background()) if err != nil { - log.Warnf("监听反向WS API时出现错误: %v", err) + log.Warnf("监听反向WS %s时出现错误: %v", typ, err) break } _, err = buffer.ReadFrom(reader) if err != nil { - log.Warnf("监听反向WS API时出现错误: %v", err) + log.Warnf("监听反向WS %s时出现错误: %v", typ, err) break } if t == websocket.MessageText { @@ -189,49 +189,37 @@ func (c *websocketClient) listenAPI(conn *wsConn, u bool) { global.PutBuffer(buffer) } } - if c.conf.ReconnectInterval != 0 { - time.Sleep(time.Millisecond * time.Duration(c.conf.ReconnectInterval)) - if !u { - go c.connect("API", c.conf.API, nil) + if c.reconnectInterval != 0 { + time.Sleep(c.reconnectInterval) + if typ == "API" { // Universal 不重连,避免多次重连 + go c.connect(typ, url, nil) } } } -func (c *websocketClient) onBotPushEvent(e *coolq.Event) { - filter := filter.Find(c.filter) - if filter != nil && !filter.Eval(gjson.Parse(e.JSONString())) { - log.Debugf("上报Event %s 到 WS服务器 时被过滤.", e.JSONBytes()) - return - } - push := func(conn *wsConn, reconnect func()) { - log.Debugf("向WS服务器推送Event: %s", e.JSONBytes()) +func (c *websocketClient) onBotPushEvent(typ, url string, conn **wsConn) func(e *coolq.Event) { + return func(e *coolq.Event) { + c.mu.Lock() + defer c.mu.Unlock() + + flt := filter.Find(c.filter) + if flt != nil && !flt.Eval(gjson.Parse(e.JSONString())) { + log.Debugf("上报Event %s 到 WS服务器 时被过滤.", e.JSONBytes()) + return + } + + log.Debugf("向反向WS %s服务器推送Event: %s", typ, e.JSONBytes()) ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) defer cancel() - if err := conn.Write(ctx, websocket.MessageText, e.JSONBytes()); err != nil { - log.Warnf("向WS服务器推送 Event 时出现错误: %v", err) - _ = conn.Close(websocket.StatusNormalClosure, "") - if c.conf.ReconnectInterval != 0 { - time.Sleep(time.Millisecond * time.Duration(c.conf.ReconnectInterval)) - reconnect() + if err := (*conn).Write(ctx, websocket.MessageText, e.JSONBytes()); err != nil { + log.Warnf("向反向WS %s服务器推送 Event 时出现错误: %v", typ, err) + _ = (*conn).Close(websocket.StatusNormalClosure, "") + if c.reconnectInterval != 0 { + time.Sleep(c.reconnectInterval) + c.connect(typ, url, conn) } } } - - connect := func(typ, url string, conptr **wsConn) func() { - return func() { - c.connect(typ, url, conptr) - } - } - - c.mu.Lock() - defer c.mu.Unlock() - - if c.event != nil { - push(c.event, connect("Event", c.conf.Event, &c.event)) - } - if c.universal != nil { - push(c.universal, connect("Universal", c.conf.Universal, &c.universal)) - } } func (s *webSocketServer) event(w http.ResponseWriter, r *http.Request) { From c478870870f188a0eaab2ef509c0f00aad91b2fd Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 17 Nov 2021 11:49:33 +0800 Subject: [PATCH 113/612] fix: don't listenAPI in Event connection bug introduced by last commit --- server/websocket.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/websocket.go b/server/websocket.go index 525d4694a..6ffb8374c 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -162,7 +162,9 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { *conptr = wrappedConn } - go c.listenAPI(typ, url, wrappedConn) + if typ != "Event" { + go c.listenAPI(typ, url, wrappedConn) + } } func (c *websocketClient) listenAPI(typ, url string, conn *wsConn) { @@ -347,8 +349,8 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { } func (s *webSocketServer) onBotPushEvent(e *coolq.Event) { - filter := filter.Find(s.filter) - if filter != nil && !filter.Eval(gjson.Parse(e.JSONString())) { + flt := filter.Find(s.filter) + if flt != nil && !flt.Eval(gjson.Parse(e.JSONString())) { log.Debugf("上报Event %s 到 WS客户端 时被过滤.", e.JSONBytes()) return } From 7b02f8b67072f22ef8f614780f0959eeb967cc07 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 17 Nov 2021 21:45:51 +0800 Subject: [PATCH 114/612] fix(server): create new request for every post trial Updates #1169 --- server/http.go | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/server/http.go b/server/http.go index 3d6f5211e..2aca120de 100644 --- a/server/http.go +++ b/server/http.go @@ -236,17 +236,26 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { } client := http.Client{Timeout: time.Second * time.Duration(c.timeout)} - req, _ := http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) - req.Header.Set("X-Self-ID", strconv.FormatInt(c.bot.Client.Uin, 10)) - req.Header.Set("User-Agent", "CQHttp/4.15.0") - req.Header.Set("Content-Type", "application/json") - if c.secret != "" { - mac := hmac.New(sha1.New, []byte(c.secret)) - _, _ = mac.Write(e.JSONBytes()) - req.Header.Set("X-Signature", "sha1="+hex.EncodeToString(mac.Sum(nil))) - } - if c.apiPort != 0 { - req.Header.Set("X-API-Port", strconv.FormatInt(int64(c.apiPort), 10)) + + // see https://stackoverflow.com/questions/31337891/net-http-http-contentlength-222-with-body-length-0 + // we should create a new request for every single post trial + createRequest := func() (*http.Request, error) { + req, err := http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) + if err != nil { + return nil, err + } + req.Header.Set("X-Self-ID", strconv.FormatInt(c.bot.Client.Uin, 10)) + req.Header.Set("User-Agent", "CQHttp/4.15.0") + req.Header.Set("Content-Type", "application/json") + if c.secret != "" { + mac := hmac.New(sha1.New, []byte(c.secret)) + _, _ = mac.Write(e.JSONBytes()) + req.Header.Set("X-Signature", "sha1="+hex.EncodeToString(mac.Sum(nil))) + } + if c.apiPort != 0 { + req.Header.Set("X-API-Port", strconv.FormatInt(int64(c.apiPort), 10)) + } + return req, nil } var res *http.Response @@ -254,6 +263,12 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { const maxAttemptTimes = 5 for i := 0; i <= maxAttemptTimes; i++ { + req, err := createRequest() + if err != nil { + log.Warnf("上报 Event 数据到 %v 时创建请求失败: %v", c.addr, err) + return + } + res, err = client.Do(req) if err == nil { //goland:noinspection GoDeferInLoop From bb7f83201e7d49033e62fecc066dd35f1474a600 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 17 Nov 2021 22:11:15 +0800 Subject: [PATCH 115/612] feat(http): enable v12 style http endpoint --- coolq/cqcode.go | 8 -------- internal/base/feature.go | 8 -------- server/http.go | 39 ++++++++++++++++----------------------- 3 files changed, 16 insertions(+), 39 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 41c421941..d97d707f9 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -928,16 +928,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou return nil, err } return message.NewFace(int32(id)), nil - case "mention": - if !base.AcceptOneBot12Message { - return nil, errors.New("unsupported onebot 12 style") - } - fallthrough case "at": qq := d["qq"] - if base.AcceptOneBot12Message && qq == "" { - qq = d["user_id"] - } if qq == "all" { return message.AtAll(), nil } diff --git a/internal/base/feature.go b/internal/base/feature.go index 0dce6f1e8..f27c0a091 100644 --- a/internal/base/feature.go +++ b/internal/base/feature.go @@ -29,11 +29,3 @@ var ( func nocheck(_ io.ReadSeeker) (bool, string) { return true, "" } - -// todo: enable all in v1.1.0 - -// onebot 12 feature -const ( - AcceptOneBot12HTTPEndPoint = false - AcceptOneBot12Message = false -) diff --git a/server/http.go b/server/http.go index 2aca120de..2c4c81108 100644 --- a/server/http.go +++ b/server/http.go @@ -24,7 +24,6 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -121,7 +120,7 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request } var response global.MSG - if base.AcceptOneBot12HTTPEndPoint && request.URL.Path == "/" { + if request.URL.Path == "/" { action := strings.TrimSuffix(ctx.Get("action").Str, "_async") log.Debugf("HTTPServer接收到API调用: %v", action) response = s.api.Call(action, ctx.Get("params")) @@ -236,26 +235,17 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { } client := http.Client{Timeout: time.Second * time.Duration(c.timeout)} - - // see https://stackoverflow.com/questions/31337891/net-http-http-contentlength-222-with-body-length-0 - // we should create a new request for every single post trial - createRequest := func() (*http.Request, error) { - req, err := http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) - if err != nil { - return nil, err - } - req.Header.Set("X-Self-ID", strconv.FormatInt(c.bot.Client.Uin, 10)) - req.Header.Set("User-Agent", "CQHttp/4.15.0") - req.Header.Set("Content-Type", "application/json") - if c.secret != "" { - mac := hmac.New(sha1.New, []byte(c.secret)) - _, _ = mac.Write(e.JSONBytes()) - req.Header.Set("X-Signature", "sha1="+hex.EncodeToString(mac.Sum(nil))) - } - if c.apiPort != 0 { - req.Header.Set("X-API-Port", strconv.FormatInt(int64(c.apiPort), 10)) - } - return req, nil + header := make(http.Header) + header.Set("X-Self-ID", strconv.FormatInt(c.bot.Client.Uin, 10)) + header.Set("User-Agent", "CQHttp/4.15.0") + header.Set("Content-Type", "application/json") + if c.secret != "" { + mac := hmac.New(sha1.New, []byte(c.secret)) + _, _ = mac.Write(e.JSONBytes()) + header.Set("X-Signature", "sha1="+hex.EncodeToString(mac.Sum(nil))) + } + if c.apiPort != 0 { + header.Set("X-API-Port", strconv.FormatInt(int64(c.apiPort), 10)) } var res *http.Response @@ -263,11 +253,14 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { const maxAttemptTimes = 5 for i := 0; i <= maxAttemptTimes; i++ { - req, err := createRequest() + // see https://stackoverflow.com/questions/31337891/net-http-http-contentlength-222-with-body-length-0 + // we should create a new request for every single post trial + req, err := http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) if err != nil { log.Warnf("上报 Event 数据到 %v 时创建请求失败: %v", c.addr, err) return } + req.Header = header res, err = client.Do(req) if err == nil { From c6119cf9ea3802f3cc69131367d90fd9ebf96536 Mon Sep 17 00:00:00 2001 From: sam01101 Date: Thu, 18 Nov 2021 09:58:46 +0800 Subject: [PATCH 116/612] del: Removed issuebot due to issue template (#1174) --- .github/workflows/issuebot.yml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .github/workflows/issuebot.yml diff --git a/.github/workflows/issuebot.yml b/.github/workflows/issuebot.yml deleted file mode 100644 index b61969dee..000000000 --- a/.github/workflows/issuebot.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Issuebot - -on: - issues: - types: [opened, edited] - -jobs: - new_issue: - name: Run Issuebot on new Issue - runs-on: ubuntu-latest - steps: - - uses: wfjsw/actions-gocqhttp-issue-prefilter@master From c2c1fb00e59c616481a0f1292a09b49418ac4229 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 18 Nov 2021 13:13:52 +0800 Subject: [PATCH 117/612] fix: fix set_group_special_title param Fixes #1178 --- coolq/api.go | 1 + modules/api/api.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 8b937e163..b96971665 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -797,6 +797,7 @@ func (bot *CQBot) CQSetGroupCard(groupID, userID int64, card string) global.MSG // // https://git.io/Jtz10 // @route(set_group_special_title) +// @rename(title->special_title) func (bot *CQBot) CQSetGroupSpecialTitle(groupID, userID int64, title string) global.MSG { if g := bot.Client.FindGroup(groupID); g != nil { if m := g.FindMember(userID); m != nil { diff --git a/modules/api/api.go b/modules/api/api.go index af21228c8..60b425c36 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -262,7 +262,7 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "set_group_special_title": p0 := p.Get("group_id").Int() p1 := p.Get("user_id").Int() - p2 := p.Get("title").String() + p2 := p.Get("special_title").String() return c.bot.CQSetGroupSpecialTitle(p0, p1, p2) case "set_group_whole_ban": p0 := p.Get("group_id").Int() From a2d1e88ed5e9bc5f42336fb25ce7ff9d3a09a43a Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 18 Nov 2021 14:23:17 +0800 Subject: [PATCH 118/612] fix: use gorilla/websocket --- server/websocket.go | 69 +++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/server/websocket.go b/server/websocket.go index 6ffb8374c..144170e36 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -2,7 +2,6 @@ package server import ( "bytes" - "context" "encoding/json" "fmt" "net/http" @@ -13,10 +12,10 @@ import ( "time" "github.com/Mrs4s/MiraiGo/utils" + "github.com/gorilla/websocket" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "gopkg.in/yaml.v3" - "nhooyr.io/websocket" "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" @@ -55,6 +54,12 @@ type wsConn struct { apiCaller *api.Caller } +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + // runWSServer 运行一个正向WS server func runWSServer(b *coolq.CQBot, node yaml.Node) { var conf config.WebsocketServer @@ -133,7 +138,7 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { if c.token != "" { header["Authorization"] = []string{"Token " + c.token} } - conn, _, err := websocket.Dial(context.Background(), url, &websocket.DialOptions{HTTPHeader: header}) // nolint + conn, _, err := websocket.DefaultDialer.Dial(url, header) // nolint if err != nil { log.Warnf("连接到反向WebSocket %s服务器 %v 时出现错误: %v", typ, url, err) if c.reconnectInterval != 0 { @@ -146,7 +151,7 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { switch typ { case "Event", "Universal": handshake := fmt.Sprintf(`{"meta_event_type":"lifecycle","post_type":"meta_event","self_id":%d,"sub_type":"connect","time":%d}`, c.bot.Client.Uin, time.Now().Unix()) - err = conn.Write(context.Background(), websocket.MessageText, []byte(handshake)) + err = conn.WriteMessage(websocket.TextMessage, []byte(handshake)) if err != nil { log.Warnf("反向WebSocket 握手时出现错误: %v", err) } @@ -168,11 +173,10 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { } func (c *websocketClient) listenAPI(typ, url string, conn *wsConn) { - defer func() { _ = conn.Close(websocket.StatusNormalClosure, "") }() - conn.Conn.SetReadLimit(1024 * 1024 * 128) + defer func() { _ = conn.Close() }() for { buffer := global.NewBuffer() - t, reader, err := conn.Conn.Reader(context.Background()) + t, reader, err := conn.Conn.NextReader() if err != nil { log.Warnf("监听反向WS %s时出现错误: %v", typ, err) break @@ -182,7 +186,7 @@ func (c *websocketClient) listenAPI(typ, url string, conn *wsConn) { log.Warnf("监听反向WS %s时出现错误: %v", typ, err) break } - if t == websocket.MessageText { + if t == websocket.TextMessage { go func(buffer *bytes.Buffer) { defer global.PutBuffer(buffer) conn.handleRequest(c.bot, buffer.Bytes()) @@ -211,11 +215,9 @@ func (c *websocketClient) onBotPushEvent(typ, url string, conn **wsConn) func(e } log.Debugf("向反向WS %s服务器推送Event: %s", typ, e.JSONBytes()) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) - defer cancel() - if err := (*conn).Write(ctx, websocket.MessageText, e.JSONBytes()); err != nil { + if err := (*conn).WriteMessage(websocket.TextMessage, e.JSONBytes()); err != nil { log.Warnf("向反向WS %s服务器推送 Event 时出现错误: %v", typ, err) - _ = (*conn).Close(websocket.StatusNormalClosure, "") + _ = (*conn).Close() if c.reconnectInterval != 0 { time.Sleep(c.reconnectInterval) c.connect(typ, url, conn) @@ -231,23 +233,22 @@ func (s *webSocketServer) event(w http.ResponseWriter, r *http.Request) { w.WriteHeader(status) return } - opts := &websocket.AcceptOptions{InsecureSkipVerify: true} - c, err := websocket.Accept(w, r, opts) + + c, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Warnf("处理 WebSocket 请求时出现错误: %v", err) return } - err = c.Write(context.Background(), websocket.MessageText, []byte(s.handshake)) + + err = c.WriteMessage(websocket.TextMessage, []byte(s.handshake)) if err != nil { log.Warnf("WebSocket 握手时出现错误: %v", err) - _ = c.Close(websocket.StatusNormalClosure, "") + _ = c.Close() return } log.Infof("接受 WebSocket 连接: %v (/event)", r.RemoteAddr) - conn := &wsConn{Conn: c, apiCaller: api.NewCaller(s.bot)} - s.mu.Lock() s.eventConn = append(s.eventConn, conn) s.mu.Unlock() @@ -260,18 +261,19 @@ func (s *webSocketServer) api(w http.ResponseWriter, r *http.Request) { w.WriteHeader(status) return } - opts := &websocket.AcceptOptions{InsecureSkipVerify: true} - c, err := websocket.Accept(w, r, opts) + + c, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Warnf("处理 WebSocket 请求时出现错误: %v", err) return } + log.Infof("接受 WebSocket 连接: %v (/api)", r.RemoteAddr) conn := &wsConn{Conn: c, apiCaller: api.NewCaller(s.bot)} if s.conf.RateLimit.Enabled { conn.apiCaller.Use(rateLimit(s.conf.RateLimit.Frequency, s.conf.RateLimit.Bucket)) } - go s.listenAPI(conn) + s.listenAPI(conn) } func (s *webSocketServer) any(w http.ResponseWriter, r *http.Request) { @@ -281,18 +283,20 @@ func (s *webSocketServer) any(w http.ResponseWriter, r *http.Request) { w.WriteHeader(status) return } - opts := &websocket.AcceptOptions{InsecureSkipVerify: true} - c, err := websocket.Accept(w, r, opts) + + c, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Warnf("处理 WebSocket 请求时出现错误: %v", err) return } - err = c.Write(context.Background(), websocket.MessageText, []byte(s.handshake)) + + err = c.WriteMessage(websocket.TextMessage, []byte(s.handshake)) if err != nil { log.Warnf("WebSocket 握手时出现错误: %v", err) - _ = c.Close(websocket.StatusNormalClosure, "") + _ = c.Close() return } + log.Infof("接受 WebSocket 连接: %v (/)", r.RemoteAddr) conn := &wsConn{Conn: c, apiCaller: api.NewCaller(s.bot)} if s.conf.RateLimit.Enabled { @@ -305,11 +309,10 @@ func (s *webSocketServer) any(w http.ResponseWriter, r *http.Request) { } func (s *webSocketServer) listenAPI(c *wsConn) { - defer func() { _ = c.Close(websocket.StatusNormalClosure, "") }() - c.Conn.SetReadLimit(1024 * 1024 * 128) + defer func() { _ = c.Close() }() for { buffer := global.NewBuffer() - t, reader, err := c.Reader(context.Background()) + t, reader, err := c.NextReader() if err != nil { break } @@ -318,7 +321,7 @@ func (s *webSocketServer) listenAPI(c *wsConn) { break } - if t == websocket.MessageText { + if t == websocket.TextMessage { go func(buffer *bytes.Buffer) { defer global.PutBuffer(buffer) c.handleRequest(s.bot, buffer.Bytes()) @@ -333,7 +336,7 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { defer func() { if err := recover(); err != nil { log.Printf("处置WS命令时发生无法恢复的异常:%v\n%s", err, debug.Stack()) - _ = c.Close(websocket.StatusInternalError, fmt.Sprint(err)) + _ = c.Close() } }() j := gjson.Parse(utils.B2S(payload)) @@ -343,7 +346,7 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { if j.Get("echo").Exists() { ret["echo"] = j.Get("echo").Value() } - writer, _ := c.Writer(context.Background(), websocket.MessageText) + writer, _ := c.NextWriter(websocket.TextMessage) _ = json.NewEncoder(writer).Encode(ret) _ = writer.Close() } @@ -362,8 +365,8 @@ func (s *webSocketServer) onBotPushEvent(e *coolq.Event) { for i := 0; i < len(s.eventConn); i++ { conn := s.eventConn[i] log.Debugf("向WS客户端推送Event: %s", e.JSONBytes()) - if err := conn.Write(context.Background(), websocket.MessageText, e.JSONBytes()); err != nil { - _ = conn.Close(websocket.StatusNormalClosure, "") + if err := conn.WriteMessage(websocket.TextMessage, e.JSONBytes()); err != nil { + _ = conn.Close() conn = nil continue } From 150ce2950a4523f44df7fcc34ab45365d7b6ea29 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 18 Nov 2021 21:31:46 +0800 Subject: [PATCH 119/612] fix: allow sending guild short video Fixes #1176 Fixes #1170 --- coolq/bot.go | 23 ++++++++++++++++++++++- go.mod | 6 ++---- go.sum | 48 ++---------------------------------------------- 3 files changed, 26 insertions(+), 51 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 1f9306a5e..384697c35 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -199,6 +199,18 @@ func (bot *CQBot) UploadLocalImageAsGuildChannel(guildID, channelID uint64, img return bot.Client.GuildService.UploadGuildImage(guildID, channelID, img.Stream) } +func (bot *CQBot) uploadGuildVideo(i *LocalVideoElement, guildID, channelID uint64) (*message.ShortVideoElement, error) { + video, err := os.Open(i.File) + if err != nil { + return nil, err + } + defer func() { _ = video.Close() }() + _, _ = video.Seek(0, io.SeekStart) + _, _ = i.thumb.Seek(0, io.SeekStart) + n, err := bot.Client.UploadGuildShortVideo(guildID, channelID, video, i.thumb) + return n, err +} + // SendGroupMessage 发送群消息 func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int32 { newElem := make([]message.IMessageElement, 0, len(m.Elements)) @@ -352,7 +364,16 @@ func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message. continue } e = n - case *LocalVideoElement, *LocalVoiceElement, *PokeElement, *message.MusicShareElement: + + case *LocalVideoElement: + n, err := bot.uploadGuildVideo(i, guildID, channelID) + if err != nil { + log.Warnf("警告: 频道 %d 消息%s上传失败: %v", channelID, e.Type().String(), err) + continue + } + e = n + + case *LocalVoiceElement, *PokeElement, *message.MusicShareElement: log.Warnf("警告: 频道暂不支持发送 %v 消息", i.Type().String()) continue } diff --git a/go.mod b/go.mod index d413303a4..ec37bb9e4 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,12 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211114170854-511e8c41edd2 + github.com/Mrs4s/MiraiGo v0.0.0-20211118132817-19e205000410 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98 + github.com/gorilla/websocket v1.4.2 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/klauspost/compress v1.13.6 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible @@ -26,7 +27,6 @@ require ( golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b - nhooyr.io/websocket v1.8.7 ) require ( @@ -35,7 +35,6 @@ require ( github.com/gocq/rs v1.0.1 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/websocket v1.4.2 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect @@ -49,7 +48,6 @@ require ( github.com/xdg-go/scram v1.0.2 // indirect github.com/xdg-go/stringprep v1.0.2 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 // indirect diff --git a/go.sum b/go.sum index 4f64c2e3d..e74f4fbd3 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211114170854-511e8c41edd2 h1:4Lbei62HbTfp90vV9zRzbe6TkMlns7QMqSeeyjJuauU= -github.com/Mrs4s/MiraiGo v0.0.0-20211114170854-511e8c41edd2/go.mod h1:UOO08UBb5mVisfwrjduINu5GF9+gmsn33f2tnr83xnE= +github.com/Mrs4s/MiraiGo v0.0.0-20211118132817-19e205000410 h1:a4DRiEqbOPQICQhwis+8sCDwsEev3lmeR8sXQ0oRnMk= +github.com/Mrs4s/MiraiGo v0.0.0-20211118132817-19e205000410/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -17,17 +17,6 @@ github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= @@ -55,35 +44,23 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98 h1:NJDZEa7gibUa0w4tie8qKeQFKdeKFUbecWyQDPdRx40= github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98/go.mod h1:E5TBHc60dsWtOL7sbXCb3P9i4xrj2J7Zm5sEJftIc1w= github.com/gocq/rs v1.0.1 h1:ng7nhXmnx3SnfM0DOqmbP6GmQp1xGwRG9XmBiLFDWuM= github.com/gocq/rs v1.0.1/go.mod h1:8oaQnRvqn1fMh8i5zsetgQo03OUXksJV1k+dpmExxcY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -92,14 +69,11 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -112,8 +86,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= @@ -127,10 +99,6 @@ github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= @@ -165,7 +133,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -178,10 +145,6 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4= github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -204,8 +167,6 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -251,13 +212,11 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -284,7 +243,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -376,5 +334,3 @@ modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= From 72fec4762291f0a0de1f20ec394b502da2ab0dbc Mon Sep 17 00:00:00 2001 From: openwrt2223 <92192602+openwrt2223@users.noreply.github.com> Date: Fri, 19 Nov 2021 00:44:27 +0800 Subject: [PATCH 120/612] Update modules/config/config.go https://github.com/Mrs4s/go-cqhttp/issues/1169#issuecomment-970192648 --- modules/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/config/config.go b/modules/config/config.go index db9571cf4..d8707f8c5 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -296,7 +296,7 @@ const httpDefault = ` # HTTP 通信设置 post: #- url: '' # 地址 # secret: '' # 密钥 - #- url: 127.0.0.1:5701 # 地址 + #- url: http://127.0.0.1:5701/ # 地址 # secret: '' # 密钥 ` From fd7806c31334a2be4e20982b02d46aa8d6ece7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 19 Nov 2021 14:41:36 +0800 Subject: [PATCH 121/612] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9Eeditor=20conf?= =?UTF-8?q?ig=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..d046cc6aa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab + +[*.yml] +indent_size = 2 From 7e573f9be6868bf29e5bbeeacaaf63b0370aa1a5 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 19 Nov 2021 23:20:42 +0800 Subject: [PATCH 122/612] feat: support animated sticker message --- coolq/cqcode.go | 18 ++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index d97d707f9..494d17bb0 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -244,6 +244,11 @@ func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []glob "data": map[string]string{"data": o.Content, "resid": strconv.FormatInt(int64(o.Id), 10)}, } } + case *message.AnimatedSticker: + m = global.MSG{ + "type": "sticker", + "data": map[string]string{"id": strconv.FormatInt(int64(o.ID), 10)}, + } default: continue } @@ -361,6 +366,8 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. } case *message.LightAppElement: write(`[CQ:json,data=%s]`, CQCodeEscapeValue(o.Content)) + case *message.AnimatedSticker: + write(`[CQ:sticker,id=%d]`, o.ID) } } r = sb.String() // 内部已拷贝 @@ -466,6 +473,11 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { "data": global.MSG{"data": o.Content, "resid": o.Id}, } } + case *message.AnimatedSticker: + m = global.MSG{ + "type": "sticker", + "data": global.MSG{"id": o.ID}, + } default: continue } @@ -922,6 +934,12 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou } } return &message.VoiceElement{Data: data}, nil + case "sticker": + id, err := strconv.Atoi(d["id"]) + if err != nil { + return nil, err + } + return &message.AnimatedSticker{ID: int32(id)}, nil case "face": id, err := strconv.Atoi(d["id"]) if err != nil { diff --git a/go.mod b/go.mod index ec37bb9e4..79f6fbab2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211118132817-19e205000410 + github.com/Mrs4s/MiraiGo v0.0.0-20211119150245-3216c69d469d github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index e74f4fbd3..fb92163fb 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211118132817-19e205000410 h1:a4DRiEqbOPQICQhwis+8sCDwsEev3lmeR8sXQ0oRnMk= -github.com/Mrs4s/MiraiGo v0.0.0-20211118132817-19e205000410/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= +github.com/Mrs4s/MiraiGo v0.0.0-20211119150245-3216c69d469d h1:H0XKbw4aZMBxZAb8InemvoDPtOeWrIESwTg4ReVlC5o= +github.com/Mrs4s/MiraiGo v0.0.0-20211119150245-3216c69d469d/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 2fc7f995f28c6e9adde4be20af72cc9a4bdf806d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 20 Nov 2021 18:45:35 +0800 Subject: [PATCH 123/612] coolq: make animated sticker subset of face message --- coolq/cqcode.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 494d17bb0..f9db432e9 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -246,8 +246,8 @@ func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []glob } case *message.AnimatedSticker: m = global.MSG{ - "type": "sticker", - "data": map[string]string{"id": strconv.FormatInt(int64(o.ID), 10)}, + "type": "face", + "data": map[string]string{"id": strconv.FormatInt(int64(o.ID), 10), "type": "sticker"}, } default: continue @@ -367,7 +367,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. case *message.LightAppElement: write(`[CQ:json,data=%s]`, CQCodeEscapeValue(o.Content)) case *message.AnimatedSticker: - write(`[CQ:sticker,id=%d]`, o.ID) + write(`[CQ:face,id=%d,type=sticker]`, o.ID) } } r = sb.String() // 内部已拷贝 @@ -475,8 +475,8 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { } case *message.AnimatedSticker: m = global.MSG{ - "type": "sticker", - "data": global.MSG{"id": o.ID}, + "type": "face", + "data": global.MSG{"id": o.ID, "type": "sticker"}, } default: continue @@ -934,17 +934,14 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou } } return &message.VoiceElement{Data: data}, nil - case "sticker": - id, err := strconv.Atoi(d["id"]) - if err != nil { - return nil, err - } - return &message.AnimatedSticker{ID: int32(id)}, nil case "face": id, err := strconv.Atoi(d["id"]) if err != nil { return nil, err } + if d["type"] == "sticker" { + return &message.AnimatedSticker{ID: int32(id)}, nil + } return message.NewFace(int32(id)), nil case "at": qq := d["qq"] From 67fe1f661f8ea1c27f2e76195c0fc394ebf6317d Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 20 Nov 2021 20:49:40 +0800 Subject: [PATCH 124/612] template: fix typo --- .github/ISSUE_TEMPLATE/bug-report.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 3a7eff21f..1cb2aee5e 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -94,7 +94,7 @@ body: label: 运行架构 description: (可选) 选择运行 go-cqhttp 的系统架构 options: - - AMD + - AMD64 - x86 - ARM [32] (别名:AArch32 / ARMv7) - ARM [64] (别名:AArch64 / ARMv8) From fadc460f4af98c63b46064ee270ee24212d62e34 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 20 Nov 2021 20:58:51 +0800 Subject: [PATCH 125/612] update template --- .github/ISSUE_TEMPLATE/bug-report.yaml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 1cb2aee5e..9e2f15613 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -56,13 +56,9 @@ body: - v1.0.0-beta3 - v1.0.0-beta2 - v1.0.0-beta1 - - 更老的版本(请在下面填写) + - 更老的版本 validations: required: true - - type: input - id: env-gocq-ver-other - attributes: - label: 旧的 go-cqhttp 版本 (请注意,我们对更低版本的 go-cqhttp 不提供完全支持;您的 Issue 有可能会被关闭。) # Env | VM Version - type: dropdown @@ -79,13 +75,9 @@ body: - CentOS - ArchLinux - UNIX (Android) - - 其它(请在下面填写) + - 其它(请在下方说明) validations: required: true - - type: input - id: env-vm-ver-other - attributes: - label: 其它系统版本 # Env | VM Arch - type: dropdown @@ -98,11 +90,7 @@ body: - x86 - ARM [32] (别名:AArch32 / ARMv7) - ARM [64] (别名:AArch64 / ARMv8) - - 其它(请在下面填写) - - type: input - id: env-vm-arch-other - attributes: - label: 其它系统架构 + - 其它 # Env | Connection type - type: dropdown From d8445c2d8dd505505f7af61ad8de459bea82b771 Mon Sep 17 00:00:00 2001 From: Ink33 Date: Sat, 20 Nov 2021 22:52:23 +0800 Subject: [PATCH 126/612] fix typo --- db/leveldb/leveldb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/leveldb/leveldb.go b/db/leveldb/leveldb.go index ec76d438c..3e787e412 100644 --- a/db/leveldb/leveldb.go +++ b/db/leveldb/leveldb.go @@ -48,7 +48,7 @@ func (ldb *LevelDBImpl) Open() error { WriteBuffer: 128 * opt.KiB, }) if err != nil { - return errors.Wrap(err, "open level ldb error") + return errors.Wrap(err, "open leveldb error") } ldb.db = d return nil From 6b9f94c0f7b173d62d9374988585760ca90330d8 Mon Sep 17 00:00:00 2001 From: ACodinghusky <46753037+ACodinghusky@users.noreply.github.com> Date: Sat, 20 Nov 2021 23:23:50 +0800 Subject: [PATCH 127/612] Update config.yml template Update config.yml template --- docs/config.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/config.md b/docs/config.md index c593eaf0a..d3a2df226 100644 --- a/docs/config.md +++ b/docs/config.md @@ -15,11 +15,10 @@ account: # 账号相关 uin: 1233456 # QQ账号 password: '' # 密码为空时使用扫码登录 encrypt: false # 是否开启密码加密 - status: 0 # 在线状态,详情请查看下方的在线状态表 + status: 0 # 在线状态 请参考 https://docs.go-cqhttp.org/guide/config.html#在线状态 relogin: # 重连设置 - # disabled: false - delay: 3 # 重连延迟, 单位秒 - interval: 0 # 重连间隔 + delay: 3 # 首次重连延迟, 单位秒 + interval: 3 # 重连间隔 max-times: 0 # 最大重连次数, 0为无限制 # 是否使用服务器下发的新地址进行重连 @@ -27,11 +26,11 @@ account: # 账号相关 use-sso-address: true heartbeat: - # disabled: false # 是否开启心跳事件上报 # 心跳频率, 单位秒 # -1 为关闭心跳 interval: 5 +message: # 上报数据类型 # 可选: string,array post-format: string @@ -57,6 +56,10 @@ heartbeat: output: # 日志等级 trace,debug,info,warn,error log-level: warn + # 日志时效 单位天. 超过这个时间之前的日志将会被自动删除. 设置为 0 表示永久保留. + log-aging: 15 + # 是否在每次启动时强制创建全新的文件储存日志. 为 false 的情况下将会在上次启动时创建的日志文件续写 + log-force-new: true # 是否启用 DEBUG debug: false # 开启调试模式 @@ -76,6 +79,14 @@ default-middlewares: &default frequency: 1 # 令牌回复频率, 单位秒 bucket: 1 # 令牌桶大小 +database: # 数据库相关设置 + leveldb: + # 是否启用内置leveldb数据库 + # 启用将会增加10-20MB的内存占用和一定的磁盘空间 + # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 + enable: true + +# 连接服务列表 servers: # HTTP 通信设置 - http: From b82bde54fd48811d7e19ef4fe9cab91a03dd43c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 02:44:56 +0800 Subject: [PATCH 128/612] =?UTF-8?q?fix:=20=E5=A4=84=E7=90=86rule=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E4=BC=9A=E8=A2=AB=E5=B9=B6=E8=A1=8C=E5=A4=84=E7=90=86?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 4 ++++ qqrobot/rule.go | 3 +++ 2 files changed, 7 insertions(+) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index e399e6f8c..683b9ce80 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -239,6 +239,10 @@ func (r *QQRobot) OnTempMessage(client *client.QQClient, m *client.TempMessageEv } func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { + // rule内部有些状态可能会改变,这里加锁保护一下 + rule.Locker.Lock() + defer rule.Locker.Unlock() + config := rule.Config nowStr := r.currentTime() nowUnix := time.Now().Unix() diff --git a/qqrobot/rule.go b/qqrobot/rule.go index 9f76aeb92..0370eacc1 100644 --- a/qqrobot/rule.go +++ b/qqrobot/rule.go @@ -3,6 +3,7 @@ package qqrobot import ( "math/rand" "strings" + "sync" logger "github.com/sirupsen/logrus" ) @@ -23,6 +24,8 @@ type FoodImage struct { // Rule 规则,附带一些缓存数据 type Rule struct { + Locker sync.RWMutex // mirai-go的分发是并发的,这里需要保护下多个消息同时尝试应用当前规则的情况 + Config RuleConfig ProcessedMessages map[int64]struct{} SiteToFoodPage map[string]int64 // 最新食谱页面的页码数 From 8ccb7ea46e49b4ba882c741860dff4ec9dc89832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 02:51:53 +0800 Subject: [PATCH 129/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E4=B8=80=E9=94=AE=E6=8B=89=E5=8F=96=E6=9C=80=E6=96=B0?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E7=84=B6=E5=90=8E=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E5=90=8E=E8=BF=90=E8=A1=8C=E7=9A=84=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 qqrobot/_build_latest_version_and_run.sh diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh new file mode 100644 index 000000000..9b86c7e9f --- /dev/null +++ b/qqrobot/_build_latest_version_and_run.sh @@ -0,0 +1,5 @@ +git pull + +go build -v -o qq_robot . + +./qq_robot From 2bf4d8b0881ec765790908155abd71b359fe7a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 02:52:30 +0800 Subject: [PATCH 130/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=BF=AB=E9=80=9F=E5=90=AF=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index 9b86c7e9f..7de1a86c5 100644 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -2,4 +2,4 @@ git pull go build -v -o qq_robot . -./qq_robot +./qq_robot faststart From cf4ba42f829a37e618505900ebb70184ce4600b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 02:54:43 +0800 Subject: [PATCH 131/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=89=A7=E8=A1=8C=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 qqrobot/_build_latest_version_and_run.sh diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh old mode 100644 new mode 100755 From 3f7c79b4f5be628089c183c87d81fc776d6e8ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 02:56:05 +0800 Subject: [PATCH 132/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E6=89=A7=E8=A1=8C=E7=9A=84=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index 7de1a86c5..73c0265ce 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -1,3 +1,5 @@ +set -x + git pull go build -v -o qq_robot . From d90f875cd61607c70c47f28ef68cc0b471992cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 02:56:12 +0800 Subject: [PATCH 133/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index 73c0265ce..bbd731774 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -1,7 +1,10 @@ set -x +# 拉取代码 git pull +# 构建新版本 go build -v -o qq_robot . +# 运行 ./qq_robot faststart From f4aea339fb50942b38575b5f18072d90a0770166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 03:12:34 +0800 Subject: [PATCH 134/612] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.mod b/go.mod index 991ef0650..05861e3ae 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,6 @@ go 1.17 replace github.com/Mrs4s/go-cqhttp => github.com/fzls/qq_robot_go v1.0.0-beta8 -replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2.0 - require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/BurntSushi/toml v0.3.1 From 3d5f7df398a0844df765cdb850d120949f4f447c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 03:17:41 +0800 Subject: [PATCH 135/612] =?UTF-8?q?refactor:=20=E5=B0=86=E9=AD=94=E6=94=B9?= =?UTF-8?q?=E5=90=8E=E6=96=B0=E5=A2=9E=E7=9A=84=E7=9B=B4=E6=8E=A5=E5=92=8C?= =?UTF-8?q?=E9=97=B4=E6=8E=A5=E4=BE=9D=E8=B5=96=E5=8D=95=E7=8B=AC=E5=88=97?= =?UTF-8?q?=E5=87=BA=EF=BC=8C=E5=87=8F=E5=B0=91=E5=90=8E=E7=BB=AD=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E4=BB=A3=E7=A0=81=E6=97=B6=E5=86=B2=E7=AA=81=E7=9A=84?= =?UTF-8?q?=E6=A6=82=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 05861e3ae..311a8ec53 100644 --- a/go.mod +++ b/go.mod @@ -4,18 +4,29 @@ go 1.17 replace github.com/Mrs4s/go-cqhttp => github.com/fzls/qq_robot_go v1.0.0-beta8 +// 魔改后需要额外引入的依赖项,单独列出,避免后面又冲突 require ( - github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/BurntSushi/toml v0.3.1 + github.com/fzls/logger v1.1.1 + github.com/gookit/color v1.5.0 + github.com/hashicorp/golang-lru v0.5.4 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 +) + +require ( + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect +) + +// 以下为go-cqhttp原本的依赖 +require ( + github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 github.com/Mrs4s/MiraiGo v0.0.0-20211114170854-511e8c41edd2 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 - github.com/fzls/logger v1.1.1 github.com/gabriel-vasile/mimetype v1.4.0 github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98 - github.com/gookit/color v1.5.0 - github.com/hashicorp/golang-lru v0.5.4 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/klauspost/compress v1.13.6 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible @@ -25,8 +36,6 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 github.com/tidwall/gjson v1.11.0 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 go.mongodb.org/mongo-driver v1.7.4 @@ -56,7 +65,6 @@ require ( github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.0.2 // indirect github.com/xdg-go/stringprep v1.0.2 // indirect - github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect From a870607c9c7c37d2f83e90002a5ce8aa75d93708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 15:03:35 +0800 Subject: [PATCH 136/612] =?UTF-8?q?feat:=20=E9=80=9A=E8=BF=87=E5=AD=90?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E6=9D=A5=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E4=BB=8E=E8=80=8C=E4=B8=8D=E5=BF=85=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E6=8B=96=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 3 +++ qqrobot/_build_latest_version_and_run.sh | 4 ++++ qqrobot/setting | 1 + 3 files changed, 8 insertions(+) create mode 100644 .gitmodules create mode 160000 qqrobot/setting diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..cb08dcd9e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "qqrobot/setting"] + path = qqrobot/setting + url = https://github.com/fzls/qq_robot_go_setting.git diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index bbd731774..679cdf3de 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -6,5 +6,9 @@ git pull # 构建新版本 go build -v -o qq_robot . +# 从私有仓库子模块拉取最新配置,并复制到项目根目录 +git submodule update --recursive +cp qqrobot/setting/{config.toml, config.yml, device.json} . + # 运行 ./qq_robot faststart diff --git a/qqrobot/setting b/qqrobot/setting new file mode 160000 index 000000000..3204e8ca4 --- /dev/null +++ b/qqrobot/setting @@ -0,0 +1 @@ +Subproject commit 3204e8ca4b1b46e462c1c86665a1544ca00f0cc2 From d35bb1b07585f747723f98a99c21e1f5fbd1cb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 15:14:48 +0800 Subject: [PATCH 137/612] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index 679cdf3de..72e7de649 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -1,14 +1,13 @@ set -x -# 拉取代码 -git pull +# 拉取代码,包括子模块 +git pull --recurse-submodules + +# 从私有仓库子模块复制最新配置到项目根目录 +cp qqrobot/setting/{config.toml, config.yml, device.json} . # 构建新版本 go build -v -o qq_robot . -# 从私有仓库子模块拉取最新配置,并复制到项目根目录 -git submodule update --recursive -cp qqrobot/setting/{config.toml, config.yml, device.json} . - # 运行 ./qq_robot faststart From 2d4ffe982003e8d66f3eb3714c53d5f098b26043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 15:16:16 +0800 Subject: [PATCH 138/612] =?UTF-8?q?fix:=20=E4=B8=8D=E5=BA=94=E8=AF=A5?= =?UTF-8?q?=E6=9C=89=E7=A9=BA=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index 72e7de649..0ed25acac 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -4,7 +4,7 @@ set -x git pull --recurse-submodules # 从私有仓库子模块复制最新配置到项目根目录 -cp qqrobot/setting/{config.toml, config.yml, device.json} . +cp qqrobot/setting/{config.toml,config.yml,device.json} . # 构建新版本 go build -v -o qq_robot . From 39174c6e32f72b921a0d2c7d549a66b983bff493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 15:18:07 +0800 Subject: [PATCH 139/612] =?UTF-8?q?fix:=20=E7=9B=B4=E6=8E=A5=E5=88=86?= =?UTF-8?q?=E5=BC=80=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index 0ed25acac..d788183b3 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -4,7 +4,9 @@ set -x git pull --recurse-submodules # 从私有仓库子模块复制最新配置到项目根目录 -cp qqrobot/setting/{config.toml,config.yml,device.json} . +cp qqrobot/setting/config.toml . +cp qqrobot/setting/config.yml . +cp qqrobot/setting/device.json . # 构建新版本 go build -v -o qq_robot . From a45309d522654c878b8e5ad4f48f246da1060308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 15:19:35 +0800 Subject: [PATCH 140/612] =?UTF-8?q?revert:=20=E4=B8=8D=E7=94=A8=E5=88=86?= =?UTF-8?q?=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index d788183b3..0ed25acac 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -4,9 +4,7 @@ set -x git pull --recurse-submodules # 从私有仓库子模块复制最新配置到项目根目录 -cp qqrobot/setting/config.toml . -cp qqrobot/setting/config.yml . -cp qqrobot/setting/device.json . +cp qqrobot/setting/{config.toml,config.yml,device.json} . # 构建新版本 go build -v -o qq_robot . From d8d138c6a3b8b6b61ad3bd7a86d4966d2301412f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 15:26:33 +0800 Subject: [PATCH 141/612] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=8B=89=E5=8F=96=E5=AD=90=E4=BB=93=E5=BA=93=E6=9C=80=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=EF=BC=8C=E8=80=8C=E4=B8=8D=E6=98=AF=E4=BB=93?= =?UTF-8?q?=E5=BA=93=E5=86=85=E6=A0=87=E8=AE=B0=E7=9A=84=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index 0ed25acac..3f1062a2d 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -1,13 +1,14 @@ set -x -# 拉取代码,包括子模块 -git pull --recurse-submodules - -# 从私有仓库子模块复制最新配置到项目根目录 -cp qqrobot/setting/{config.toml,config.yml,device.json} . +# 拉取代码 +git pull # 构建新版本 go build -v -o qq_robot . +# 拉取私有仓库子模块的最新版本,并将最新配置复制到项目根目录 +git submodule update --recursive --remote +cp qqrobot/setting/{config.toml,config.yml,device.json} . + # 运行 ./qq_robot faststart From e8605315a2ace9ee4dc243653ad8e765f1561f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Nov 2021 16:00:29 +0800 Subject: [PATCH 142/612] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E5=AD=90?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/setting | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/setting b/qqrobot/setting index 3204e8ca4..1b19baa00 160000 --- a/qqrobot/setting +++ b/qqrobot/setting @@ -1 +1 @@ -Subproject commit 3204e8ca4b1b46e462c1c86665a1544ca00f0cc2 +Subproject commit 1b19baa00e9ee7b9d8c51f1f84b8e12808a77105 From e73417bebf423757313844c54ab829946067ef50 Mon Sep 17 00:00:00 2001 From: Bluefissure Date: Mon, 22 Nov 2021 12:27:40 -0600 Subject: [PATCH 143/612] feat: Add guild role APIs chore: lint code refactor: rename by global.MSG refactor: CQSetGuildMemberRole fix: regenerate api --- coolq/api.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 2 ++ modules/api/api.go | 27 ++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 8b937e163..9e26c0956 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -154,6 +154,77 @@ func (bot *CQBot) CQGetGuildMembers(guildID uint64) global.MSG { }) } +// CQGetGuildRoles 获取频道角色列表 +// @route(get_guild_roles) +func (bot *CQBot) CQGetGuildRoles(guildID uint64) global.MSG { + roles, err := bot.Client.GuildService.GetGuildRoles(guildID) + if err != nil { + log.Errorf("获取频道 %v 角色列表时出现错误: %v", guildID, err) + return Failed(100, "API_ERROR", err.Error()) + } + return OK(global.MSG{ + "roles": roles, + }) +} + +// CQCreateGuildRole 创建频道角色 +// @route(create_guild_role) +func (bot *CQBot) CQCreateGuildRole(guildID uint64, name string, color uint32, independent bool, initialUsers gjson.Result) global.MSG { + userSlice := []uint64{} + if initialUsers.IsArray() { + for _, user := range initialUsers.Array() { + userSlice = append(userSlice, user.Uint()) + } + } + role, err := bot.Client.GuildService.CreateGuildRole(guildID, name, color, independent, userSlice) + if err != nil { + log.Errorf("创建频道 %v 角色时出现错误: %v", guildID, err) + return Failed(100, "API_ERROR", err.Error()) + } + return OK(global.MSG{ + "role": role, + }) +} + +// CQDeleteGuildRole 删除频道角色 +// @route(delete_guild_role) +func (bot *CQBot) CQDeleteGuildRole(guildID uint64, roleID uint64) global.MSG { + err := bot.Client.GuildService.DeleteGuildRole(guildID, roleID) + if err != nil { + log.Errorf("删除频道 %v 角色时出现错误: %v", guildID, err) + return Failed(100, "API_ERROR", err.Error()) + } + return OK(nil) +} + +// CQSetGuildMemberRole 设置用户在频道中的角色 +// @route(set_guild_member_role) +func (bot *CQBot) CQSetGuildMemberRole(guildID uint64, set bool, roleID uint64, users gjson.Result) global.MSG { + userSlice := []uint64{} + if users.IsArray() { + for _, user := range users.Array() { + userSlice = append(userSlice, user.Uint()) + } + } + err := bot.Client.GuildService.SetUserRoleInGuild(guildID, set, roleID, userSlice) + if err != nil { + log.Errorf("设置用户在频道 %v 中的角色时出现错误: %v", guildID, err) + return Failed(100, "API_ERROR", err.Error()) + } + return OK(nil) +} + +// CQModifyRoleInGuild 修改频道角色 +// @route(modify_role_in_guild) +func (bot *CQBot) CQModifyRoleInGuild(guildID uint64, roleID uint64, name string, color uint32, indepedent bool) global.MSG { + err := bot.Client.GuildService.ModifyRoleInGuild(guildID, roleID, name, color, indepedent) + if err != nil { + log.Errorf("修改频道 %v 角色时出现错误: %v", guildID, err) + return Failed(100, "API_ERROR", err.Error()) + } + return OK(nil) +} + // CQGetFriendList 获取好友列表 // // https://git.io/Jtz1L diff --git a/go.mod b/go.mod index d413303a4..011170201 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211114170854-511e8c41edd2 + github.com/Mrs4s/MiraiGo v0.0.0-20211122140519-ec4cd4a6f3fe github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 4f64c2e3d..10a9169e2 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6 github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Mrs4s/MiraiGo v0.0.0-20211114170854-511e8c41edd2 h1:4Lbei62HbTfp90vV9zRzbe6TkMlns7QMqSeeyjJuauU= github.com/Mrs4s/MiraiGo v0.0.0-20211114170854-511e8c41edd2/go.mod h1:UOO08UBb5mVisfwrjduINu5GF9+gmsn33f2tnr83xnE= +github.com/Mrs4s/MiraiGo v0.0.0-20211122140519-ec4cd4a6f3fe h1:ydfvvxZsVwbqzyfmQ5ExOhQhiZr/5IyjauBIBnupkFA= +github.com/Mrs4s/MiraiGo v0.0.0-20211122140519-ec4cd4a6f3fe/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/modules/api/api.go b/modules/api/api.go index af21228c8..cb9298371 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -45,6 +45,13 @@ func (c *Caller) call(action string, p Getter) global.MSG { p1 := p.Get("parent_id").String() p2 := p.Get("name").String() return c.bot.CQGroupFileCreateFolder(p0, p1, p2) + case "create_guild_role": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("name").String() + p2 := uint32(p.Get("color").Int()) + p3 := p.Get("independent").Bool() + p4 := p.Get("initial_users") + return c.bot.CQCreateGuildRole(p0, p1, p2, p3, p4) case "delete_essence_msg": p0 := int32(p.Get("message_id").Int()) return c.bot.CQDeleteEssenceMessage(p0) @@ -60,6 +67,10 @@ func (c *Caller) call(action string, p Getter) global.MSG { p0 := p.Get("group_id").Int() p1 := p.Get("folder_id").String() return c.bot.CQGroupFileDeleteFolder(p0, p1) + case "delete_guild_role": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("role_id").Uint() + return c.bot.CQDeleteGuildRole(p0, p1) case "delete_msg": p0 := int32(p.Get("message_id").Int()) return c.bot.CQDeleteMessage(p0) @@ -135,6 +146,9 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "get_guild_meta_by_guest": p0 := p.Get("guild_id").Uint() return c.bot.CQGetGuildMetaByGuest(p0) + case "get_guild_roles": + p0 := p.Get("guild_id").Uint() + return c.bot.CQGetGuildRoles(p0) case "get_guild_service_profile": return c.bot.CQGetGuildServiceProfile() case "get_image": @@ -160,6 +174,13 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "mark_msg_as_read": p0 := int32(p.Get("message_id").Int()) return c.bot.CQMarkMessageAsRead(p0) + case "modify_role_in_guild": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("role_id").Uint() + p2 := p.Get("name").String() + p3 := uint32(p.Get("color").Int()) + p4 := p.Get("indepedent").Bool() + return c.bot.CQModifyRoleInGuild(p0, p1, p2, p3, p4) case "ocr_image", ".ocr_image": p0 := p.Get("image").String() return c.bot.CQOcrImage(p0) @@ -271,6 +292,12 @@ func (c *Caller) call(action string, p Getter) global.MSG { p1 = pt.Bool() } return c.bot.CQSetGroupWholeBan(p0, p1) + case "set_guild_member_role": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("set").Bool() + p2 := p.Get("role_id").Uint() + p3 := p.Get("users") + return c.bot.CQSetGuildMemberRole(p0, p1, p2, p3) case "upload_group_file": p0 := p.Get("group_id").Int() p1 := p.Get("file").String() From b4d92fcae3a21feb55be7cad71e436da2dcf4f3d Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 23 Nov 2021 23:36:32 +0800 Subject: [PATCH 144/612] fix: guild event use string-id type --- coolq/converter.go | 17 +++++++++++++---- coolq/cqcode.go | 4 ++-- coolq/event.go | 42 +++++++++++++++++++++--------------------- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/coolq/converter.go b/coolq/converter.go index 634f8b657..03cb8a778 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -52,7 +52,7 @@ func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG func convertGuildMemberInfo(m *client.GuildMemberInfo) global.MSG { return global.MSG{ - "tiny_id": m.TinyId, + "tiny_id": fU64(m.TinyId), "title": m.Title, "nickname": m.Nickname, "role": m.Role, @@ -144,12 +144,12 @@ func convertChannelInfo(c *client.ChannelInfo) global.MSG { }) } return global.MSG{ - "channel_id": c.ChannelId, + "channel_id": fU64(c.ChannelId), "channel_type": c.ChannelType, "channel_name": c.ChannelName, - "owner_guild_id": c.Meta.GuildId, + "owner_guild_id": fU64(c.Meta.GuildId), "creator_id": c.Meta.CreatorUin, - "creator_tiny_id": c.Meta.CreatorTinyId, + "creator_tiny_id": fU64(c.Meta.CreatorTinyId), "create_time": c.Meta.CreateTime, "current_slow_mode": c.Meta.CurrentSlowMode, "talk_permission": c.Meta.TalkPermission, @@ -157,3 +157,12 @@ func convertChannelInfo(c *client.ChannelInfo) global.MSG { "slow_modes": slowModes, } } + +func fU64(v uint64) string { + return strconv.FormatUint(v, 10) +} + +func sU64(v string) uint64 { + r, _ := strconv.ParseUint(v, 10, 64) + return r +} diff --git a/coolq/cqcode.go b/coolq/cqcode.go index f9db432e9..7315cde6d 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -166,7 +166,7 @@ func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []glob } else { m = global.MSG{ "type": "at", - "data": map[string]string{"qq": strconv.FormatInt(o.Target, 10)}, + "data": map[string]string{"qq": strconv.FormatUint(uint64(o.Target), 10)}, } } case *message.RedBagElement: @@ -310,7 +310,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. write("[CQ:at,qq=all]") continue } - write("[CQ:at,qq=%d]", o.Target) + write("[CQ:at,qq=%d]", uint64(o.Target)) case *message.RedBagElement: write("[CQ:redbag,title=%s]", o.Title) case *message.ForwardElement: diff --git a/coolq/event.go b/coolq/event.go index 82ca7183e..1db0b295e 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -165,16 +165,16 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC "post_type": "message", "message_type": "guild", "sub_type": "channel", - "guild_id": m.GuildId, - "channel_id": m.ChannelId, + "guild_id": fU64(m.GuildId), + "channel_id": fU64(m.ChannelId), "message_id": fmt.Sprintf("%v-%v", m.Id, m.InternalId), - "user_id": m.Sender.TinyId, + "user_id": fU64(m.Sender.TinyId), "message": ToFormattedMessage(m.Elements, source, false), // todo: 增加对频道消息 Reply 的支持 "self_id": bot.Client.Uin, - "self_tiny_id": bot.Client.GuildService.TinyId, + "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "time": m.Time, "sender": global.MSG{ - "user_id": m.Sender.TinyId, + "user_id": fU64(m.Sender.TinyId), "nickname": m.Sender.Nickname, }, }) @@ -206,14 +206,14 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien "post_type": "notice", "notice_type": "message_reactions_updated", "message_sender_uin": e.MessageSenderUin, - "guild_id": e.GuildId, - "channel_id": e.ChannelId, + "guild_id": fU64(e.GuildId), + "channel_id": fU64(e.ChannelId), "message_id": fmt.Sprint(e.MessageId), // todo: 支持数据库后转换为数据库id - "operator_id": e.OperatorId, + "operator_id": fU64(e.OperatorId), "current_reactions": currentReactions, "time": time.Now().Unix(), "self_id": bot.Client.Uin, - "self_tiny_id": bot.Client.GuildService.TinyId, + "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, }) } @@ -227,12 +227,12 @@ func (bot *CQBot) guildChannelUpdatedEvent(c *client.QQClient, e *client.GuildCh bot.dispatchEventMessage(global.MSG{ "post_type": "notice", "notice_type": "channel_updated", - "guild_id": e.GuildId, - "channel_id": e.ChannelId, - "operator_id": e.OperatorId, + "guild_id": fU64(e.GuildId), + "channel_id": fU64(e.ChannelId), + "operator_id": fU64(e.OperatorId), "time": time.Now().Unix(), "self_id": bot.Client.Uin, - "self_tiny_id": bot.Client.GuildService.TinyId, + "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, "old_info": convertChannelInfo(e.OldChannelInfo), "new_info": convertChannelInfo(e.NewChannelInfo), @@ -252,11 +252,11 @@ func (bot *CQBot) guildChannelCreatedEvent(c *client.QQClient, e *client.GuildCh bot.dispatchEventMessage(global.MSG{ "post_type": "notice", "notice_type": "channel_created", - "guild_id": e.GuildId, - "channel_id": e.ChannelInfo.ChannelId, - "operator_id": e.OperatorId, + "guild_id": fU64(e.GuildId), + "channel_id": fU64(e.ChannelInfo.ChannelId), + "operator_id": fU64(e.OperatorId), "self_id": bot.Client.Uin, - "self_tiny_id": bot.Client.GuildService.TinyId, + "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, "time": time.Now().Unix(), "channel_info": convertChannelInfo(e.ChannelInfo), @@ -276,11 +276,11 @@ func (bot *CQBot) guildChannelDestroyedEvent(c *client.QQClient, e *client.Guild bot.dispatchEventMessage(global.MSG{ "post_type": "notice", "notice_type": "channel_destroyed", - "guild_id": e.GuildId, - "channel_id": e.ChannelInfo.ChannelId, - "operator_id": e.OperatorId, + "guild_id": fU64(e.GuildId), + "channel_id": fU64(e.ChannelInfo.ChannelId), + "operator_id": fU64(e.OperatorId), "self_id": bot.Client.Uin, - "self_tiny_id": bot.Client.GuildService.TinyId, + "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, "time": time.Now().Unix(), "channel_info": convertChannelInfo(e.ChannelInfo), From bb769941ab1e11a64213e792896b1fd8e3a3d55e Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 24 Nov 2021 00:30:57 +0800 Subject: [PATCH 145/612] fix: role api issues --- coolq/api.go | 23 +++++++++++++++++------ go.mod | 2 +- go.sum | 4 ++-- modules/api/api.go | 14 +++++++------- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 69cde3e4b..a8faf82e6 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -157,14 +157,25 @@ func (bot *CQBot) CQGetGuildMembers(guildID uint64) global.MSG { // CQGetGuildRoles 获取频道角色列表 // @route(get_guild_roles) func (bot *CQBot) CQGetGuildRoles(guildID uint64) global.MSG { - roles, err := bot.Client.GuildService.GetGuildRoles(guildID) + r, err := bot.Client.GuildService.GetGuildRoles(guildID) if err != nil { log.Errorf("获取频道 %v 角色列表时出现错误: %v", guildID, err) return Failed(100, "API_ERROR", err.Error()) } - return OK(global.MSG{ - "roles": roles, - }) + roles := make([]global.MSG, len(r)) + for i, role := range r { + roles[i] = global.MSG{ + "role_id": fU64(role.RoleId), + "role_name": role.RoleName, + "argb_color": role.ArgbColor, + "independent": role.Independent, + "member_count": role.Num, + "max_count": role.MaxNum, + "owned": role.Owned, + "disabled": role.Disabled, + } + } + return OK(roles) } // CQCreateGuildRole 创建频道角色 @@ -182,7 +193,7 @@ func (bot *CQBot) CQCreateGuildRole(guildID uint64, name string, color uint32, i return Failed(100, "API_ERROR", err.Error()) } return OK(global.MSG{ - "role": role, + "role_id": fU64(role), }) } @@ -215,7 +226,7 @@ func (bot *CQBot) CQSetGuildMemberRole(guildID uint64, set bool, roleID uint64, } // CQModifyRoleInGuild 修改频道角色 -// @route(modify_role_in_guild) +// @route(update_guild_role) func (bot *CQBot) CQModifyRoleInGuild(guildID uint64, roleID uint64, name string, color uint32, indepedent bool) global.MSG { err := bot.Client.GuildService.ModifyRoleInGuild(guildID, roleID, name, color, indepedent) if err != nil { diff --git a/go.mod b/go.mod index d02ea959f..e5d24ff5c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211122140519-ec4cd4a6f3fe + github.com/Mrs4s/MiraiGo v0.0.0-20211123160412-4c60c8ee4740 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index f6f841259..c8710b224 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211122140519-ec4cd4a6f3fe h1:ydfvvxZsVwbqzyfmQ5ExOhQhiZr/5IyjauBIBnupkFA= -github.com/Mrs4s/MiraiGo v0.0.0-20211122140519-ec4cd4a6f3fe/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= +github.com/Mrs4s/MiraiGo v0.0.0-20211123160412-4c60c8ee4740 h1:8i8ILE2q+jw8zhmTtsNRhwrufkJQJ2kOyVhncmAfhTg= +github.com/Mrs4s/MiraiGo v0.0.0-20211123160412-4c60c8ee4740/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/modules/api/api.go b/modules/api/api.go index c4a51965a..a84bbc7f5 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -174,13 +174,6 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "mark_msg_as_read": p0 := int32(p.Get("message_id").Int()) return c.bot.CQMarkMessageAsRead(p0) - case "modify_role_in_guild": - p0 := p.Get("guild_id").Uint() - p1 := p.Get("role_id").Uint() - p2 := p.Get("name").String() - p3 := uint32(p.Get("color").Int()) - p4 := p.Get("indepedent").Bool() - return c.bot.CQModifyRoleInGuild(p0, p1, p2, p3, p4) case "ocr_image", ".ocr_image": p0 := p.Get("image").String() return c.bot.CQOcrImage(p0) @@ -298,6 +291,13 @@ func (c *Caller) call(action string, p Getter) global.MSG { p2 := p.Get("role_id").Uint() p3 := p.Get("users") return c.bot.CQSetGuildMemberRole(p0, p1, p2, p3) + case "update_guild_role": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("role_id").Uint() + p2 := p.Get("name").String() + p3 := uint32(p.Get("color").Int()) + p4 := p.Get("indepedent").Bool() + return c.bot.CQModifyRoleInGuild(p0, p1, p2, p3, p4) case "upload_group_file": p0 := p.Get("group_id").Int() p1 := p.Get("file").String() From 30edae64b36dc8fbd62bad448b4c280965a969f3 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 24 Nov 2021 01:39:33 +0800 Subject: [PATCH 146/612] fix: guild api use string-id type --- coolq/api.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index a8faf82e6..09ecd068e 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -55,7 +55,7 @@ func (bot *CQBot) CQGetQiDianAccountInfo() global.MSG { func (bot *CQBot) CQGetGuildServiceProfile() global.MSG { return OK(global.MSG{ "nickname": bot.Client.GuildService.Nickname, - "tiny_id": bot.Client.GuildService.TinyId, + "tiny_id": fU64(bot.Client.GuildService.TinyId), "avatar_url": bot.Client.GuildService.AvatarUrl, }) } @@ -76,9 +76,9 @@ func (bot *CQBot) CQGetGuildList() global.MSG { } */ fs = append(fs, global.MSG{ - "guild_id": info.GuildId, + "guild_id": fU64(info.GuildId), "guild_name": info.GuildName, - "guild_display_id": info.GuildCode, + "guild_display_id": fU64(info.GuildCode), // "channels": channels, }) } @@ -94,7 +94,7 @@ func (bot *CQBot) CQGetGuildMetaByGuest(guildID uint64) global.MSG { return Failed(100, "API_ERROR", err.Error()) } return OK(global.MSG{ - "guild_id": meta.GuildId, + "guild_id": fU64(meta.GuildId), "guild_name": meta.GuildName, "guild_profile": meta.GuildProfile, "create_time": meta.CreateTime, @@ -102,7 +102,7 @@ func (bot *CQBot) CQGetGuildMetaByGuest(guildID uint64) global.MSG { "max_robot_count": meta.MaxRobotCount, "max_admin_count": meta.MaxAdminCount, "member_count": meta.MemberCount, - "owner_id": meta.OwnerId, + "owner_id": fU64(meta.OwnerId), }) } From 3e9920b31bf3af5b0a21b92413f2a47369964d30 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 27 Nov 2021 15:58:13 +0800 Subject: [PATCH 147/612] feat: get_topic_channel_feeds api --- coolq/api.go | 26 +++++++++++++++++++++++ coolq/converter.go | 43 ++++++++++++++++++++++++++++++++++++++ coolq/feed.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 7 ++++--- go.sum | 12 +++++------ modules/api/api.go | 4 ++++ 6 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 coolq/feed.go diff --git a/coolq/api.go b/coolq/api.go index 09ecd068e..8c8e14c02 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -236,6 +236,32 @@ func (bot *CQBot) CQModifyRoleInGuild(guildID uint64, roleID uint64, name string return OK(nil) } +// CQGetTopicChannelFeeds 获取话题频道帖子列表 +// @route(get_topic_channel_feeds) +func (bot *CQBot) CQGetTopicChannelFeeds(guildID, channelID uint64) global.MSG { + guild := bot.Client.GuildService.FindGuild(guildID) + if guild == nil { + return Failed(100, "GUILD_NOT_FOUND") + } + channel := guild.FindChannel(channelID) + if channel == nil { + return Failed(100, "CHANNEL_NOT_FOUND") + } + if channel.ChannelType != client.ChannelTypeTopic { + return Failed(100, "CHANNEL_TYPE_ERROR") + } + feeds, err := bot.Client.GuildService.GetTopicChannelFeeds(guildID, channelID) + if err != nil { + log.Errorf("获取频道 %v 帖子时出现错误: %v", channelID, err) + return Failed(100, "API_ERROR", err.Error()) + } + c := make([]global.MSG, 0, len(feeds)) + for _, feed := range feeds { + c = append(c, convertChannelFeedInfo(feed)) + } + return OK(c) +} + // CQGetFriendList 获取好友列表 // // https://git.io/Jtz1L diff --git a/coolq/converter.go b/coolq/converter.go index 03cb8a778..50a6a010c 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -1,6 +1,7 @@ package coolq import ( + "github.com/Mrs4s/MiraiGo/topic" "strconv" "github.com/Mrs4s/MiraiGo/client" @@ -158,6 +159,48 @@ func convertChannelInfo(c *client.ChannelInfo) global.MSG { } } +func convertChannelFeedInfo(f *topic.Feed) global.MSG { + m := global.MSG{ + "id": f.Id, + "title": f.Title, + "sub_title": f.SubTitle, + "create_time": f.CreateTime, + "guild_id": fU64(f.GuildId), + "channel_id": fU64(f.ChannelId), + "poster_info": global.MSG{ + "tiny_id": f.Poster.TinyIdStr, + "nickname": f.Poster.Nickname, + "icon_url": f.Poster.IconUrl, + }, + "contents": FeedContentsToArrayMessage(f.Contents), + } + images := make([]global.MSG, 0, len(f.Images)) + videos := make([]global.MSG, 0, len(f.Videos)) + for _, image := range f.Images { + images = append(images, global.MSG{ + "file_id": image.FileId, + "pattern_id": image.PatternId, + "url": image.Url, + "width": image.Width, + "height": image.Height, + }) + } + for _, video := range f.Videos { + videos = append(videos, global.MSG{ + "file_id": video.FileId, + "pattern_id": video.PatternId, + "url": video.Url, + "width": video.Width, + "height": video.Height, + }) + } + m["resource"] = global.MSG{ + "images": images, + "videos": videos, + } + return m +} + func fU64(v uint64) string { return strconv.FormatUint(v, 10) } diff --git a/coolq/feed.go b/coolq/feed.go new file mode 100644 index 000000000..6335fdffa --- /dev/null +++ b/coolq/feed.go @@ -0,0 +1,51 @@ +package coolq + +import ( + "github.com/Mrs4s/MiraiGo/topic" + "github.com/Mrs4s/go-cqhttp/global" +) + +func FeedContentsToArrayMessage(contents []topic.IFeedRichContentElement) []global.MSG { + r := make([]global.MSG, 0, len(contents)) + for _, e := range contents { + var m global.MSG + switch elem := e.(type) { + case *topic.TextElement: + m = global.MSG{ + "type": "text", + "data": global.MSG{"text": elem.Content}, + } + case *topic.AtElement: + m = global.MSG{ + "type": "at", + "data": global.MSG{"qq": elem.Id}, + } + case *topic.EmojiElement: + m = global.MSG{ + "type": "face", + "data": global.MSG{"id": elem.Id}, + } + case *topic.ChannelQuoteElement: + m = global.MSG{ + "type": "channel_quote", + "data": global.MSG{ + "guild_id": fU64(elem.GuildId), + "channel_id": fU64(elem.ChannelId), + "display_text": elem.DisplayText, + }, + } + case *topic.UrlQuoteElement: + m = global.MSG{ + "type": "url_quote", + "data": global.MSG{ + "url": elem.Url, + "display_text": elem.DisplayText, + }, + } + } + if m != nil { + r = append(r, m) + } + } + return r +} diff --git a/go.mod b/go.mod index e5d24ff5c..6ef76d336 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211123160412-4c60c8ee4740 + github.com/Mrs4s/MiraiGo v0.0.0-20211126180457-5c5e61d27903 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 @@ -30,10 +30,12 @@ require ( ) require ( + github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gocq/rs v1.0.1 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-cmp v0.5.5 // indirect github.com/google/uuid v1.3.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect @@ -48,11 +50,10 @@ require ( github.com/xdg-go/scram v1.0.2 // indirect github.com/xdg-go/stringprep v1.0.2 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect + golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 // indirect golang.org/x/text v0.3.7 // indirect - google.golang.org/protobuf v1.27.1 // indirect modernc.org/libc v1.11.70 // indirect modernc.org/mathutil v1.4.1 // indirect modernc.org/memory v1.0.5 // indirect diff --git a/go.sum b/go.sum index c8710b224..58fb1dfd4 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,10 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211123160412-4c60c8ee4740 h1:8i8ILE2q+jw8zhmTtsNRhwrufkJQJ2kOyVhncmAfhTg= -github.com/Mrs4s/MiraiGo v0.0.0-20211123160412-4c60c8ee4740/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= +github.com/Mrs4s/MiraiGo v0.0.0-20211126180457-5c5e61d27903 h1:09DWaPpfF9u/qwRNW8wmieCaySeXZmYLnfSbrow4Cbs= +github.com/Mrs4s/MiraiGo v0.0.0-20211126180457-5c5e61d27903/go.mod h1:ET7ZfE/OgwdZUBF+kdYlKUCAw+HavEet/5D4+lq/8Xc= +github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08 h1:VAR3nNyBAc+oPlLKihkLTP0GXf8bQi085y2pSrNzAF0= +github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08/go.mod h1:lM1Te2wqdBLPSF7QLaskSn1fGW3Tw2l0iFGsTHTxqIg= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -175,9 +177,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= +golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -232,7 +233,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= diff --git a/modules/api/api.go b/modules/api/api.go index a84bbc7f5..41b928139 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -167,6 +167,10 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "get_stranger_info": p0 := p.Get("user_id").Int() return c.bot.CQGetStrangerInfo(p0) + case "get_topic_channel_feeds": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("channel_id").Uint() + return c.bot.CQGetTopicChannelFeeds(p0, p1) case "get_unidirectional_friend_list": return c.bot.CQGetUnidirectionalFriendList() case "get_version_info": From bf06f50a83fcb85a04ba14dec623e9cc1c32e930 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 27 Nov 2021 16:01:09 +0800 Subject: [PATCH 148/612] add comments --- coolq/converter.go | 5 ----- coolq/feed.go | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/coolq/converter.go b/coolq/converter.go index 50a6a010c..bf085ddd1 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -204,8 +204,3 @@ func convertChannelFeedInfo(f *topic.Feed) global.MSG { func fU64(v uint64) string { return strconv.FormatUint(v, 10) } - -func sU64(v string) uint64 { - r, _ := strconv.ParseUint(v, 10, 64) - return r -} diff --git a/coolq/feed.go b/coolq/feed.go index 6335fdffa..28f6e4756 100644 --- a/coolq/feed.go +++ b/coolq/feed.go @@ -5,6 +5,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) +// FeedContentsToArrayMessage 将话题频道帖子内容转换为 Array Message func FeedContentsToArrayMessage(contents []topic.IFeedRichContentElement) []global.MSG { r := make([]global.MSG, 0, len(contents)) for _, e := range contents { From b4bd22ea11728f2e1a32de3250094f097f07db9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 27 Nov 2021 08:02:04 +0000 Subject: [PATCH 149/612] ci(chore): Fix stylings --- coolq/converter.go | 3 ++- coolq/feed.go | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/coolq/converter.go b/coolq/converter.go index bf085ddd1..bd53eb283 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -1,9 +1,10 @@ package coolq import ( - "github.com/Mrs4s/MiraiGo/topic" "strconv" + "github.com/Mrs4s/MiraiGo/topic" + "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" log "github.com/sirupsen/logrus" diff --git a/coolq/feed.go b/coolq/feed.go index 28f6e4756..0c362df7b 100644 --- a/coolq/feed.go +++ b/coolq/feed.go @@ -2,6 +2,7 @@ package coolq import ( "github.com/Mrs4s/MiraiGo/topic" + "github.com/Mrs4s/go-cqhttp/global" ) From fe1bfeb9489cb580b177b312bff3dc574abddc26 Mon Sep 17 00:00:00 2001 From: Arnie97 Date: Mon, 29 Nov 2021 12:58:50 +0800 Subject: [PATCH 150/612] Fix image..jpg (#1198) --- coolq/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 8c8e14c02..687ff47de 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1384,7 +1384,7 @@ func (bot *CQBot) CQGetImage(file string) global.MSG { "filename": r.ReadString(), "url": r.ReadString(), } - local := path.Join(global.CachePath, file+"."+path.Ext(msg["filename"].(string))) + local := path.Join(global.CachePath, file+path.Ext(msg["filename"].(string))) if !global.PathExists(local) { if body, err := global.HTTPGetReadCloser(msg["url"].(string)); err == nil { f, _ := os.OpenFile(local, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o0644) From fbf0d7d1e04591b282fd19b4b8c7d66fff5c2755 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 2 Dec 2021 01:07:01 +0800 Subject: [PATCH 151/612] update dep --- go.mod | 3 ++- go.sum | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6ef76d336..33f4f66d0 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211126180457-5c5e61d27903 + github.com/Mrs4s/MiraiGo v0.0.0-20211201170411-c0713ba75d7f github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 @@ -41,6 +41,7 @@ require ( github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/pierrec/lz4/v4 v4.1.11 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect diff --git a/go.sum b/go.sum index 58fb1dfd4..362a9ebf4 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211126180457-5c5e61d27903 h1:09DWaPpfF9u/qwRNW8wmieCaySeXZmYLnfSbrow4Cbs= -github.com/Mrs4s/MiraiGo v0.0.0-20211126180457-5c5e61d27903/go.mod h1:ET7ZfE/OgwdZUBF+kdYlKUCAw+HavEet/5D4+lq/8Xc= +github.com/Mrs4s/MiraiGo v0.0.0-20211201170411-c0713ba75d7f h1:kiNClkzBUTj7z0INLxyN8pRXuPSy0gPGv/dXpSKVfmE= +github.com/Mrs4s/MiraiGo v0.0.0-20211201170411-c0713ba75d7f/go.mod h1:xh9dqJAblNEQnZQSrMj216jF3GkrkoWOzkpBjGoZiiY= github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08 h1:VAR3nNyBAc+oPlLKihkLTP0GXf8bQi085y2pSrNzAF0= github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08/go.mod h1:lM1Te2wqdBLPSF7QLaskSn1fGW3Tw2l0iFGsTHTxqIg= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= @@ -108,6 +108,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pierrec/lz4/v4 v4.1.11 h1:LVs17FAZJFOjgmJXl9Tf13WfLUvZq7/RjfEJrnwZ9OE= +github.com/pierrec/lz4/v4 v4.1.11/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= From a06a891186a797a6a28d606871b10acbd515a633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sat, 4 Dec 2021 13:48:12 +0800 Subject: [PATCH 152/612] =?UTF-8?q?feat:=20=E5=B0=86=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E7=9A=84=E4=B8=BB=E4=BD=93=E9=83=A8=E5=88=86=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E5=88=B0=20cmd/gocq=20(#1215)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: move main into cmd/gocq * fix: make lint happy * fix: rename Boot to Main --- login.go => cmd/gocq/login.go | 2 +- cmd/gocq/main.go | 413 ++++++++++++++++++++++++++++++++++ main.go | 406 +-------------------------------- 3 files changed, 416 insertions(+), 405 deletions(-) rename login.go => cmd/gocq/login.go (99%) create mode 100644 cmd/gocq/main.go diff --git a/login.go b/cmd/gocq/login.go similarity index 99% rename from login.go rename to cmd/gocq/login.go index 85ade28fe..80ac10bcf 100644 --- a/login.go +++ b/cmd/gocq/login.go @@ -1,4 +1,4 @@ -package main +package gocq import ( "bufio" diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go new file mode 100644 index 000000000..30494ee0b --- /dev/null +++ b/cmd/gocq/main.go @@ -0,0 +1,413 @@ +// Package gocq 程序的主体部分 +package gocq + +import ( + "crypto/aes" + "crypto/md5" + "crypto/sha1" + "encoding/hex" + "os" + "path" + "sync" + "time" + + "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/MiraiGo/client" + para "github.com/fumiama/go-hide-param" + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + log "github.com/sirupsen/logrus" + "golang.org/x/crypto/pbkdf2" + "golang.org/x/term" + + "github.com/Mrs4s/go-cqhttp/coolq" + "github.com/Mrs4s/go-cqhttp/db" + "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/global/terminal" + "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/internal/cache" + "github.com/Mrs4s/go-cqhttp/internal/selfdiagnosis" + "github.com/Mrs4s/go-cqhttp/internal/selfupdate" + "github.com/Mrs4s/go-cqhttp/modules/servers" + "github.com/Mrs4s/go-cqhttp/server" +) + +// 允许通过配置文件设置的状态列表 +var allowStatus = [...]client.UserOnlineStatus{ + client.StatusOnline, client.StatusAway, client.StatusInvisible, client.StatusBusy, + client.StatusListening, client.StatusConstellation, client.StatusWeather, client.StatusMeetSpring, + client.StatusTimi, client.StatusEatChicken, client.StatusLoving, client.StatusWangWang, client.StatusCookedRice, + client.StatusStudy, client.StatusStayUp, client.StatusPlayBall, client.StatusSignal, client.StatusStudyOnline, + client.StatusGaming, client.StatusVacationing, client.StatusWatchingTV, client.StatusFitness, +} + +// Main 启动主程序 +func Main() { + base.Parse() + if !base.FastStart && terminal.RunningByDoubleClick() { + err := terminal.NoMoreDoubleClick() + if err != nil { + log.Errorf("遇到错误: %v", err) + time.Sleep(time.Second * 5) + } + return + } + switch { + case base.LittleH: + base.Help() + case base.LittleD: + server.Daemon() + case base.LittleWD != "": + base.ResetWorkingDir() + } + base.Init() + + rotateOptions := []rotatelogs.Option{ + rotatelogs.WithRotationTime(time.Hour * 24), + } + rotateOptions = append(rotateOptions, rotatelogs.WithMaxAge(base.LogAging)) + if base.LogForceNew { + rotateOptions = append(rotateOptions, rotatelogs.ForceNewFile()) + } + w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotateOptions...) + if err != nil { + log.Errorf("rotatelogs init err: %v", err) + panic(err) + } + + consoleFormatter := global.LogFormat{EnableColor: base.LogColorful} + fileFormatter := global.LogFormat{EnableColor: false} + log.AddHook(global.NewLocalHook(w, consoleFormatter, fileFormatter, global.GetLogLevel(base.LogLevel)...)) + + mkCacheDir := func(path string, _type string) { + if !global.PathExists(path) { + if err := os.MkdirAll(path, 0o755); err != nil { + log.Fatalf("创建%s缓存文件夹失败: %v", _type, err) + } + } + } + mkCacheDir(global.ImagePath, "图片") + mkCacheDir(global.VoicePath, "语音") + mkCacheDir(global.VideoPath, "视频") + mkCacheDir(global.CachePath, "发送图片") + mkCacheDir(path.Join(global.ImagePath, "guild-images"), "频道图片缓存") + cache.Init() + + db.Init() + if err := db.Open(); err != nil { + log.Fatalf("打开数据库失败: %v", err) + } + + var byteKey []byte + arg := os.Args + if len(arg) > 1 { + for i := range arg { + switch arg[i] { + case "update": + if len(arg) > i+1 { + selfupdate.SelfUpdate(arg[i+1]) + } else { + selfupdate.SelfUpdate("") + } + case "key": + p := i + 1 + if len(arg) > p { + byteKey = []byte(arg[p]) + para.Hide(p) + } + case "faststart": + base.FastStart = true + } + } + } + + if (base.Account.Uin == 0 || (base.Account.Password == "" && !base.Account.Encrypt)) && !global.PathExists("session.token") { + log.Warn("账号密码未配置, 将使用二维码登录.") + if !base.FastStart { + log.Warn("将在 5秒 后继续.") + time.Sleep(time.Second * 5) + } + } + + log.Info("当前版本:", base.Version) + if base.Debug { + log.SetLevel(log.DebugLevel) + log.SetReportCaller(true) + log.Warnf("已开启Debug模式.") + log.Debugf("开发交流群: 192548878") + } + log.Info("用户交流群: 721829413") + if !global.PathExists("device.json") { + log.Warn("虚拟设备信息不存在, 将自动生成随机设备.") + client.GenRandomDevice() + _ = os.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0o644) + log.Info("已生成设备信息并保存到 device.json 文件.") + } else { + log.Info("将使用 device.json 内的设备信息运行Bot.") + if err := client.SystemDeviceInfo.ReadJson([]byte(global.ReadAllText("device.json"))); err != nil { + log.Fatalf("加载设备信息失败: %v", err) + } + } + + if base.Account.Encrypt { + if !global.PathExists("password.encrypt") { + if base.Account.Password == "" { + log.Error("无法进行加密,请在配置文件中的添加密码后重新启动.") + readLine() + os.Exit(0) + } + log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)") + byteKey, _ = term.ReadPassword(int(os.Stdin.Fd())) + base.PasswordHash = md5.Sum([]byte(base.Account.Password)) + _ = os.WriteFile("password.encrypt", []byte(PasswordHashEncrypt(base.PasswordHash[:], byteKey)), 0o644) + log.Info("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.") + readLine() + os.Exit(0) + } else { + if base.Account.Password != "" { + log.Error("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.") + readLine() + os.Exit(0) + } + + if len(byteKey) == 0 { + log.Infof("密码加密已启用, 请输入Key对密码进行解密以继续: (Enter 提交)") + cancel := make(chan struct{}, 1) + state, _ := term.GetState(int(os.Stdin.Fd())) + go func() { + select { + case <-cancel: + return + case <-time.After(time.Second * 45): + log.Infof("解密key输入超时") + time.Sleep(3 * time.Second) + _ = term.Restore(int(os.Stdin.Fd()), state) + os.Exit(0) + } + }() + byteKey, _ = term.ReadPassword(int(os.Stdin.Fd())) + cancel <- struct{}{} + } else { + log.Infof("密码加密已启用, 使用运行时传递的参数进行解密,按 Ctrl+C 取消.") + } + + encrypt, _ := os.ReadFile("password.encrypt") + ph, err := PasswordHashDecrypt(string(encrypt), byteKey) + if err != nil { + log.Fatalf("加密存储的密码损坏,请尝试重新配置密码") + } + copy(base.PasswordHash[:], ph) + } + } else if len(base.Account.Password) > 0 { + base.PasswordHash = md5.Sum([]byte(base.Account.Password)) + } + if !base.FastStart { + log.Info("Bot将在5秒后登录并开始信息处理, 按 Ctrl+C 取消.") + time.Sleep(time.Second * 5) + } + log.Info("开始尝试登录并同步消息...") + log.Infof("使用协议: %v", func() string { + switch client.SystemDeviceInfo.Protocol { + case client.IPad: + return "iPad" + case client.AndroidPhone: + return "Android Phone" + case client.AndroidWatch: + return "Android Watch" + case client.MacOS: + return "MacOS" + case client.QiDian: + return "企点" + } + return "未知" + }()) + cli = newClient() + isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt + isTokenLogin := false + saveToken := func() { + base.AccountToken = cli.GenToken() + _ = os.WriteFile("session.token", base.AccountToken, 0o644) + } + if global.PathExists("session.token") { + token, err := os.ReadFile("session.token") + if err == nil { + if base.Account.Uin != 0 { + r := binary.NewReader(token) + cu := r.ReadInt64() + if cu != base.Account.Uin { + log.Warnf("警告: 配置文件内的QQ号 (%v) 与缓存内的QQ号 (%v) 不相同", base.Account.Uin, cu) + log.Warnf("1. 使用会话缓存继续.") + log.Warnf("2. 删除会话缓存并重启.") + log.Warnf("请选择: (5秒后自动选1)") + text := readLineTimeout(time.Second*5, "1") + if text == "2" { + _ = os.Remove("session.token") + log.Infof("缓存已删除.") + os.Exit(0) + } + } + } + if err = cli.TokenLogin(token); err != nil { + _ = os.Remove("session.token") + log.Warnf("恢复会话失败: %v , 尝试使用正常流程登录.", err) + time.Sleep(time.Second) + cli.Disconnect() + cli.Release() + cli = newClient() + } else { + isTokenLogin = true + } + } + } + if base.Account.Uin != 0 && base.PasswordHash != [16]byte{} { + cli.Uin = base.Account.Uin + cli.PasswordMd5 = base.PasswordHash + } + if !isTokenLogin { + if !isQRCodeLogin { + if err := commonLogin(); err != nil { + log.Fatalf("登录时发生致命错误: %v", err) + } + } else { + if err := qrcodeLogin(); err != nil { + log.Fatalf("登录时发生致命错误: %v", err) + } + } + } + var times uint = 1 // 重试次数 + var reLoginLock sync.Mutex + cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) { + reLoginLock.Lock() + defer reLoginLock.Unlock() + times = 1 + if cli.Online { + return + } + log.Warnf("Bot已离线: %v", e.Message) + time.Sleep(time.Second * time.Duration(base.Reconnect.Delay)) + for { + if base.Reconnect.Disabled { + log.Warnf("未启用自动重连, 将退出.") + os.Exit(1) + } + if times > base.Reconnect.MaxTimes && base.Reconnect.MaxTimes != 0 { + log.Fatalf("Bot重连次数超过限制, 停止") + } + times++ + if base.Reconnect.Interval > 0 { + log.Warnf("将在 %v 秒后尝试重连. 重连次数:%v/%v", base.Reconnect.Interval, times, base.Reconnect.MaxTimes) + time.Sleep(time.Second * time.Duration(base.Reconnect.Interval)) + } else { + time.Sleep(time.Second) + } + if cli.Online { + log.Infof("登录已完成") + break + } + log.Warnf("尝试重连...") + err := cli.TokenLogin(base.AccountToken) + if err == nil { + saveToken() + return + } + log.Warnf("快速重连失败: %v", err) + if isQRCodeLogin { + log.Fatalf("快速重连失败, 扫码登录无法恢复会话.") + } + log.Warnf("快速重连失败, 尝试普通登录. 这可能是因为其他端强行T下线导致的.") + time.Sleep(time.Second) + if err := commonLogin(); err != nil { + log.Errorf("登录时发生致命错误: %v", err) + } else { + saveToken() + break + } + } + }) + saveToken() + cli.AllowSlider = true + log.Infof("登录成功 欢迎使用: %v", cli.Nickname) + log.Info("开始加载好友列表...") + global.Check(cli.ReloadFriendList(), true) + log.Infof("共加载 %v 个好友.", len(cli.FriendList)) + log.Infof("开始加载群列表...") + global.Check(cli.ReloadGroupList(), true) + log.Infof("共加载 %v 个群.", len(cli.GroupList)) + if uint(base.Account.Status) >= uint(len(allowStatus)) { + base.Account.Status = 0 + } + cli.SetOnlineStatus(allowStatus[base.Account.Status]) + + servers.Run(coolq.NewQQBot(cli)) + log.Info("资源初始化完成, 开始处理信息.") + log.Info("アトリは、高性能ですから!") + + go selfupdate.CheckUpdate() + go func() { + time.Sleep(5 * time.Second) + go selfdiagnosis.NetworkDiagnosis(cli) + }() + + <-global.SetupMainSignalHandler() +} + +// PasswordHashEncrypt 使用key加密给定passwordHash +func PasswordHashEncrypt(passwordHash []byte, key []byte) string { + if len(passwordHash) != 16 { + panic("密码加密参数错误") + } + + key = pbkdf2.Key(key, key, 114514, 32, sha1.New) + + cipher, _ := aes.NewCipher(key) + result := make([]byte, 16) + cipher.Encrypt(result, passwordHash) + + return hex.EncodeToString(result) +} + +// PasswordHashDecrypt 使用key解密给定passwordHash +func PasswordHashDecrypt(encryptedPasswordHash string, key []byte) ([]byte, error) { + ciphertext, err := hex.DecodeString(encryptedPasswordHash) + if err != nil { + return nil, err + } + + key = pbkdf2.Key(key, key, 114514, 32, sha1.New) + + cipher, _ := aes.NewCipher(key) + result := make([]byte, 16) + cipher.Decrypt(result, ciphertext) + + return result, nil +} + +func newClient() *client.QQClient { + c := client.NewClientEmpty() + c.OnServerUpdated(func(bot *client.QQClient, e *client.ServerUpdatedEvent) bool { + if !base.UseSSOAddress { + log.Infof("收到服务器地址更新通知, 根据配置文件已忽略.") + return false + } + log.Infof("收到服务器地址更新通知, 将在下一次重连时应用. ") + return true + }) + if global.PathExists("address.txt") { + log.Infof("检测到 address.txt 文件. 将覆盖目标IP.") + addr := global.ReadAddrFile("address.txt") + if len(addr) > 0 { + c.SetCustomServer(addr) + } + log.Infof("读取到 %v 个自定义地址.", len(addr)) + } + c.OnLog(func(c *client.QQClient, e *client.LogEvent) { + switch e.Type { + case "INFO": + log.Info("Protocol -> " + e.Message) + case "ERROR": + log.Error("Protocol -> " + e.Message) + case "DEBUG": + log.Debug("Protocol -> " + e.Message) + } + }) + return c +} diff --git a/main.go b/main.go index 8dd8baa56..09cd2f0d0 100644 --- a/main.go +++ b/main.go @@ -1,33 +1,7 @@ package main import ( - "crypto/aes" - "crypto/md5" - "crypto/sha1" - "encoding/hex" - "os" - "path" - "sync" - "time" - - "github.com/Mrs4s/MiraiGo/binary" - "github.com/Mrs4s/MiraiGo/client" - para "github.com/fumiama/go-hide-param" - rotatelogs "github.com/lestrrat-go/file-rotatelogs" - log "github.com/sirupsen/logrus" - "golang.org/x/crypto/pbkdf2" - "golang.org/x/term" - - "github.com/Mrs4s/go-cqhttp/coolq" - "github.com/Mrs4s/go-cqhttp/db" - "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/global/terminal" - "github.com/Mrs4s/go-cqhttp/internal/base" - "github.com/Mrs4s/go-cqhttp/internal/cache" - "github.com/Mrs4s/go-cqhttp/internal/selfdiagnosis" - "github.com/Mrs4s/go-cqhttp/internal/selfupdate" - "github.com/Mrs4s/go-cqhttp/modules/servers" - "github.com/Mrs4s/go-cqhttp/server" + "github.com/Mrs4s/go-cqhttp/cmd/gocq" _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb _ "github.com/Mrs4s/go-cqhttp/modules/mime" // mime检查模块 @@ -35,382 +9,6 @@ import ( _ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块 ) -// 允许通过配置文件设置的状态列表 -var allowStatus = [...]client.UserOnlineStatus{ - client.StatusOnline, client.StatusAway, client.StatusInvisible, client.StatusBusy, - client.StatusListening, client.StatusConstellation, client.StatusWeather, client.StatusMeetSpring, - client.StatusTimi, client.StatusEatChicken, client.StatusLoving, client.StatusWangWang, client.StatusCookedRice, - client.StatusStudy, client.StatusStayUp, client.StatusPlayBall, client.StatusSignal, client.StatusStudyOnline, - client.StatusGaming, client.StatusVacationing, client.StatusWatchingTV, client.StatusFitness, -} - func main() { - base.Parse() - if !base.FastStart && terminal.RunningByDoubleClick() { - err := terminal.NoMoreDoubleClick() - if err != nil { - log.Errorf("遇到错误: %v", err) - time.Sleep(time.Second * 5) - } - return - } - switch { - case base.LittleH: - base.Help() - case base.LittleD: - server.Daemon() - case base.LittleWD != "": - base.ResetWorkingDir() - } - base.Init() - - rotateOptions := []rotatelogs.Option{ - rotatelogs.WithRotationTime(time.Hour * 24), - } - rotateOptions = append(rotateOptions, rotatelogs.WithMaxAge(base.LogAging)) - if base.LogForceNew { - rotateOptions = append(rotateOptions, rotatelogs.ForceNewFile()) - } - w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotateOptions...) - if err != nil { - log.Errorf("rotatelogs init err: %v", err) - panic(err) - } - - consoleFormatter := global.LogFormat{EnableColor: base.LogColorful} - fileFormatter := global.LogFormat{EnableColor: false} - log.AddHook(global.NewLocalHook(w, consoleFormatter, fileFormatter, global.GetLogLevel(base.LogLevel)...)) - - mkCacheDir := func(path string, _type string) { - if !global.PathExists(path) { - if err := os.MkdirAll(path, 0o755); err != nil { - log.Fatalf("创建%s缓存文件夹失败: %v", _type, err) - } - } - } - mkCacheDir(global.ImagePath, "图片") - mkCacheDir(global.VoicePath, "语音") - mkCacheDir(global.VideoPath, "视频") - mkCacheDir(global.CachePath, "发送图片") - mkCacheDir(path.Join(global.ImagePath, "guild-images"), "频道图片缓存") - cache.Init() - - db.Init() - if err := db.Open(); err != nil { - log.Fatalf("打开数据库失败: %v", err) - } - - var byteKey []byte - arg := os.Args - if len(arg) > 1 { - for i := range arg { - switch arg[i] { - case "update": - if len(arg) > i+1 { - selfupdate.SelfUpdate(arg[i+1]) - } else { - selfupdate.SelfUpdate("") - } - case "key": - p := i + 1 - if len(arg) > p { - byteKey = []byte(arg[p]) - para.Hide(p) - } - case "faststart": - base.FastStart = true - } - } - } - - if (base.Account.Uin == 0 || (base.Account.Password == "" && !base.Account.Encrypt)) && !global.PathExists("session.token") { - log.Warn("账号密码未配置, 将使用二维码登录.") - if !base.FastStart { - log.Warn("将在 5秒 后继续.") - time.Sleep(time.Second * 5) - } - } - - log.Info("当前版本:", base.Version) - if base.Debug { - log.SetLevel(log.DebugLevel) - log.SetReportCaller(true) - log.Warnf("已开启Debug模式.") - log.Debugf("开发交流群: 192548878") - } - log.Info("用户交流群: 721829413") - if !global.PathExists("device.json") { - log.Warn("虚拟设备信息不存在, 将自动生成随机设备.") - client.GenRandomDevice() - _ = os.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0o644) - log.Info("已生成设备信息并保存到 device.json 文件.") - } else { - log.Info("将使用 device.json 内的设备信息运行Bot.") - if err := client.SystemDeviceInfo.ReadJson([]byte(global.ReadAllText("device.json"))); err != nil { - log.Fatalf("加载设备信息失败: %v", err) - } - } - - if base.Account.Encrypt { - if !global.PathExists("password.encrypt") { - if base.Account.Password == "" { - log.Error("无法进行加密,请在配置文件中的添加密码后重新启动.") - readLine() - os.Exit(0) - } - log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)") - byteKey, _ = term.ReadPassword(int(os.Stdin.Fd())) - base.PasswordHash = md5.Sum([]byte(base.Account.Password)) - _ = os.WriteFile("password.encrypt", []byte(PasswordHashEncrypt(base.PasswordHash[:], byteKey)), 0o644) - log.Info("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.") - readLine() - os.Exit(0) - } else { - if base.Account.Password != "" { - log.Error("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.") - readLine() - os.Exit(0) - } - - if len(byteKey) == 0 { - log.Infof("密码加密已启用, 请输入Key对密码进行解密以继续: (Enter 提交)") - cancel := make(chan struct{}, 1) - state, _ := term.GetState(int(os.Stdin.Fd())) - go func() { - select { - case <-cancel: - return - case <-time.After(time.Second * 45): - log.Infof("解密key输入超时") - time.Sleep(3 * time.Second) - _ = term.Restore(int(os.Stdin.Fd()), state) - os.Exit(0) - } - }() - byteKey, _ = term.ReadPassword(int(os.Stdin.Fd())) - cancel <- struct{}{} - } else { - log.Infof("密码加密已启用, 使用运行时传递的参数进行解密,按 Ctrl+C 取消.") - } - - encrypt, _ := os.ReadFile("password.encrypt") - ph, err := PasswordHashDecrypt(string(encrypt), byteKey) - if err != nil { - log.Fatalf("加密存储的密码损坏,请尝试重新配置密码") - } - copy(base.PasswordHash[:], ph) - } - } else if len(base.Account.Password) > 0 { - base.PasswordHash = md5.Sum([]byte(base.Account.Password)) - } - if !base.FastStart { - log.Info("Bot将在5秒后登录并开始信息处理, 按 Ctrl+C 取消.") - time.Sleep(time.Second * 5) - } - log.Info("开始尝试登录并同步消息...") - log.Infof("使用协议: %v", func() string { - switch client.SystemDeviceInfo.Protocol { - case client.IPad: - return "iPad" - case client.AndroidPhone: - return "Android Phone" - case client.AndroidWatch: - return "Android Watch" - case client.MacOS: - return "MacOS" - case client.QiDian: - return "企点" - } - return "未知" - }()) - cli = newClient() - isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt - isTokenLogin := false - saveToken := func() { - base.AccountToken = cli.GenToken() - _ = os.WriteFile("session.token", base.AccountToken, 0o644) - } - if global.PathExists("session.token") { - token, err := os.ReadFile("session.token") - if err == nil { - if base.Account.Uin != 0 { - r := binary.NewReader(token) - cu := r.ReadInt64() - if cu != base.Account.Uin { - log.Warnf("警告: 配置文件内的QQ号 (%v) 与缓存内的QQ号 (%v) 不相同", base.Account.Uin, cu) - log.Warnf("1. 使用会话缓存继续.") - log.Warnf("2. 删除会话缓存并重启.") - log.Warnf("请选择: (5秒后自动选1)") - text := readLineTimeout(time.Second*5, "1") - if text == "2" { - _ = os.Remove("session.token") - log.Infof("缓存已删除.") - os.Exit(0) - } - } - } - if err = cli.TokenLogin(token); err != nil { - _ = os.Remove("session.token") - log.Warnf("恢复会话失败: %v , 尝试使用正常流程登录.", err) - time.Sleep(time.Second) - cli.Disconnect() - cli.Release() - cli = newClient() - } else { - isTokenLogin = true - } - } - } - if base.Account.Uin != 0 && base.PasswordHash != [16]byte{} { - cli.Uin = base.Account.Uin - cli.PasswordMd5 = base.PasswordHash - } - if !isTokenLogin { - if !isQRCodeLogin { - if err := commonLogin(); err != nil { - log.Fatalf("登录时发生致命错误: %v", err) - } - } else { - if err := qrcodeLogin(); err != nil { - log.Fatalf("登录时发生致命错误: %v", err) - } - } - } - var times uint = 1 // 重试次数 - var reLoginLock sync.Mutex - cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) { - reLoginLock.Lock() - defer reLoginLock.Unlock() - times = 1 - if cli.Online { - return - } - log.Warnf("Bot已离线: %v", e.Message) - time.Sleep(time.Second * time.Duration(base.Reconnect.Delay)) - for { - if base.Reconnect.Disabled { - log.Warnf("未启用自动重连, 将退出.") - os.Exit(1) - } - if times > base.Reconnect.MaxTimes && base.Reconnect.MaxTimes != 0 { - log.Fatalf("Bot重连次数超过限制, 停止") - } - times++ - if base.Reconnect.Interval > 0 { - log.Warnf("将在 %v 秒后尝试重连. 重连次数:%v/%v", base.Reconnect.Interval, times, base.Reconnect.MaxTimes) - time.Sleep(time.Second * time.Duration(base.Reconnect.Interval)) - } else { - time.Sleep(time.Second) - } - if cli.Online { - log.Infof("登录已完成") - break - } - log.Warnf("尝试重连...") - err := cli.TokenLogin(base.AccountToken) - if err == nil { - saveToken() - return - } - log.Warnf("快速重连失败: %v", err) - if isQRCodeLogin { - log.Fatalf("快速重连失败, 扫码登录无法恢复会话.") - } - log.Warnf("快速重连失败, 尝试普通登录. 这可能是因为其他端强行T下线导致的.") - time.Sleep(time.Second) - if err := commonLogin(); err != nil { - log.Errorf("登录时发生致命错误: %v", err) - } else { - saveToken() - break - } - } - }) - saveToken() - cli.AllowSlider = true - log.Infof("登录成功 欢迎使用: %v", cli.Nickname) - log.Info("开始加载好友列表...") - global.Check(cli.ReloadFriendList(), true) - log.Infof("共加载 %v 个好友.", len(cli.FriendList)) - log.Infof("开始加载群列表...") - global.Check(cli.ReloadGroupList(), true) - log.Infof("共加载 %v 个群.", len(cli.GroupList)) - if uint(base.Account.Status) >= uint(len(allowStatus)) { - base.Account.Status = 0 - } - cli.SetOnlineStatus(allowStatus[base.Account.Status]) - - servers.Run(coolq.NewQQBot(cli)) - log.Info("资源初始化完成, 开始处理信息.") - log.Info("アトリは、高性能ですから!") - - go selfupdate.CheckUpdate() - go func() { - time.Sleep(5 * time.Second) - go selfdiagnosis.NetworkDiagnosis(cli) - }() - - <-global.SetupMainSignalHandler() -} - -// PasswordHashEncrypt 使用key加密给定passwordHash -func PasswordHashEncrypt(passwordHash []byte, key []byte) string { - if len(passwordHash) != 16 { - panic("密码加密参数错误") - } - - key = pbkdf2.Key(key, key, 114514, 32, sha1.New) - - cipher, _ := aes.NewCipher(key) - result := make([]byte, 16) - cipher.Encrypt(result, passwordHash) - - return hex.EncodeToString(result) -} - -// PasswordHashDecrypt 使用key解密给定passwordHash -func PasswordHashDecrypt(encryptedPasswordHash string, key []byte) ([]byte, error) { - ciphertext, err := hex.DecodeString(encryptedPasswordHash) - if err != nil { - return nil, err - } - - key = pbkdf2.Key(key, key, 114514, 32, sha1.New) - - cipher, _ := aes.NewCipher(key) - result := make([]byte, 16) - cipher.Decrypt(result, ciphertext) - - return result, nil -} - -func newClient() *client.QQClient { - c := client.NewClientEmpty() - c.OnServerUpdated(func(bot *client.QQClient, e *client.ServerUpdatedEvent) bool { - if !base.UseSSOAddress { - log.Infof("收到服务器地址更新通知, 根据配置文件已忽略.") - return false - } - log.Infof("收到服务器地址更新通知, 将在下一次重连时应用. ") - return true - }) - if global.PathExists("address.txt") { - log.Infof("检测到 address.txt 文件. 将覆盖目标IP.") - addr := global.ReadAddrFile("address.txt") - if len(addr) > 0 { - c.SetCustomServer(addr) - } - log.Infof("读取到 %v 个自定义地址.", len(addr)) - } - c.OnLog(func(c *client.QQClient, e *client.LogEvent) { - switch e.Type { - case "INFO": - log.Info("Protocol -> " + e.Message) - case "ERROR": - log.Error("Protocol -> " + e.Message) - case "DEBUG": - log.Debug("Protocol -> " + e.Message) - } - }) - return c + gocq.Main() } From 1e3a9d61b4e8235a8205786086ee657ee0db19a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 5 Dec 2021 00:01:24 +0800 Subject: [PATCH 153/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E5=BD=93=E5=89=8D=E6=9C=BA=E5=99=A8=E4=BA=BAqq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 683b9ce80..ea05311c0 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -14,6 +14,7 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/gookit/color" lru "github.com/hashicorp/golang-lru" "github.com/pkg/errors" @@ -98,6 +99,8 @@ func (r *QQRobot) Start() { r.StartTime = time.Now() r.quitCtx, r.quitFunc = context.WithCancel(context.Background()) + logger.Infof("当前机器人账号:%v", base.Account.Uin) + r.notify(r.Config.Robot.OnStart) go r.updateTicker() go r.settleTicker() From d3bafeccc6f6afdaa53dc1b4a74b42762c7e4e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 5 Dec 2021 00:36:53 +0800 Subject: [PATCH 154/612] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=92=8C=E5=B9=BF=E6=92=AD=E6=B6=88=E6=81=AF=E5=9C=A8?= =?UTF-8?q?=E5=90=84=E4=B8=AA=E7=BE=A4=E5=8F=91=E9=80=81=E4=B9=8B=E9=97=B4?= =?UTF-8?q?=E5=BC=BA=E8=A1=8C=E9=97=B4=E9=9A=94=E4=B8=80=E7=A7=92=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E8=A2=AB=E9=A3=8E=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 2 ++ qqrobot/qq_robot.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 4310d79f6..330d5cb6a 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -32,6 +32,8 @@ func (r *QQRobot) checkUpdates() { nowStr := r.currentTime() for _, groupID := range rule.NotifyGroups { rspID := r.cqBot.SendGroupMessage(groupID, replies) + // 广播消息间强行间隔一秒 + time.Sleep(time.Second) if rspID == -1 { logger.Errorf("【%v Failed】 %v groupID=%v replies=%v err=%v", rule.Name, nowStr, groupID, replies, rspID) continue diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index ea05311c0..2e756d592 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -573,6 +573,8 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { for _, repeatToGroup := range config.RepeatToGroups { forwardRspID := r.cqBot.SendGroupMessage(repeatToGroup, repeatMessages) + // 广播消息间强行间隔一秒 + time.Sleep(time.Second) if forwardRspID == -1 { logger.Error(fmt.Sprintf("【RepeatToGroup(%v) Failed】", repeatToGroup), nowStr, config.Name, p(repeatMessages), forwardRspID) continue From dfec28dac6c3ff566fa15c401118a7d442c3b4bd Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 5 Dec 2021 22:40:44 +0800 Subject: [PATCH 155/612] server: fix concurrent write in ws --- server/websocket.go | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/server/websocket.go b/server/websocket.go index 144170e36..38fb96ae3 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -50,10 +50,21 @@ type websocketClient struct { } type wsConn struct { - *websocket.Conn + mu sync.Mutex + conn *websocket.Conn apiCaller *api.Caller } +func (c *wsConn) WriteText(b []byte) error { + c.mu.Lock() + defer c.mu.Unlock() + return c.conn.WriteMessage(websocket.TextMessage, b) +} + +func (c *wsConn) Close() error { + return c.conn.Close() +} + var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true @@ -158,7 +169,7 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { } log.Infof("已连接到反向WebSocket %s服务器 %v", typ, url) - wrappedConn := &wsConn{Conn: conn, apiCaller: api.NewCaller(c.bot)} + wrappedConn := &wsConn{conn: conn, apiCaller: api.NewCaller(c.bot)} if c.limiter != nil { wrappedConn.apiCaller.Use(c.limiter) } @@ -176,7 +187,7 @@ func (c *websocketClient) listenAPI(typ, url string, conn *wsConn) { defer func() { _ = conn.Close() }() for { buffer := global.NewBuffer() - t, reader, err := conn.Conn.NextReader() + t, reader, err := conn.conn.NextReader() if err != nil { log.Warnf("监听反向WS %s时出现错误: %v", typ, err) break @@ -215,7 +226,7 @@ func (c *websocketClient) onBotPushEvent(typ, url string, conn **wsConn) func(e } log.Debugf("向反向WS %s服务器推送Event: %s", typ, e.JSONBytes()) - if err := (*conn).WriteMessage(websocket.TextMessage, e.JSONBytes()); err != nil { + if err := (*conn).WriteText(e.JSONBytes()); err != nil { log.Warnf("向反向WS %s服务器推送 Event 时出现错误: %v", typ, err) _ = (*conn).Close() if c.reconnectInterval != 0 { @@ -248,7 +259,7 @@ func (s *webSocketServer) event(w http.ResponseWriter, r *http.Request) { } log.Infof("接受 WebSocket 连接: %v (/event)", r.RemoteAddr) - conn := &wsConn{Conn: c, apiCaller: api.NewCaller(s.bot)} + conn := &wsConn{conn: c, apiCaller: api.NewCaller(s.bot)} s.mu.Lock() s.eventConn = append(s.eventConn, conn) s.mu.Unlock() @@ -269,7 +280,7 @@ func (s *webSocketServer) api(w http.ResponseWriter, r *http.Request) { } log.Infof("接受 WebSocket 连接: %v (/api)", r.RemoteAddr) - conn := &wsConn{Conn: c, apiCaller: api.NewCaller(s.bot)} + conn := &wsConn{conn: c, apiCaller: api.NewCaller(s.bot)} if s.conf.RateLimit.Enabled { conn.apiCaller.Use(rateLimit(s.conf.RateLimit.Frequency, s.conf.RateLimit.Bucket)) } @@ -298,7 +309,7 @@ func (s *webSocketServer) any(w http.ResponseWriter, r *http.Request) { } log.Infof("接受 WebSocket 连接: %v (/)", r.RemoteAddr) - conn := &wsConn{Conn: c, apiCaller: api.NewCaller(s.bot)} + conn := &wsConn{conn: c, apiCaller: api.NewCaller(s.bot)} if s.conf.RateLimit.Enabled { conn.apiCaller.Use(rateLimit(s.conf.RateLimit.Frequency, s.conf.RateLimit.Bucket)) } @@ -312,7 +323,7 @@ func (s *webSocketServer) listenAPI(c *wsConn) { defer func() { _ = c.Close() }() for { buffer := global.NewBuffer() - t, reader, err := c.NextReader() + t, reader, err := c.conn.NextReader() if err != nil { break } @@ -346,7 +357,10 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { if j.Get("echo").Exists() { ret["echo"] = j.Get("echo").Value() } - writer, _ := c.NextWriter(websocket.TextMessage) + + c.mu.Lock() + defer c.mu.Unlock() + writer, _ := c.conn.NextWriter(websocket.TextMessage) _ = json.NewEncoder(writer).Encode(ret) _ = writer.Close() } @@ -365,7 +379,7 @@ func (s *webSocketServer) onBotPushEvent(e *coolq.Event) { for i := 0; i < len(s.eventConn); i++ { conn := s.eventConn[i] log.Debugf("向WS客户端推送Event: %s", e.JSONBytes()) - if err := conn.WriteMessage(websocket.TextMessage, e.JSONBytes()); err != nil { + if err := conn.WriteText(e.JSONBytes()); err != nil { _ = conn.Close() conn = nil continue From 0ead5921145ad4e5c70d975e198aabfbc6af0660 Mon Sep 17 00:00:00 2001 From: He Cheng <57560866+hecheng337@users.noreply.github.com> Date: Mon, 6 Dec 2021 21:07:06 +0800 Subject: [PATCH 156/612] update protobuf (#1217) Co-authored-by: hecheng337 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 33f4f66d0..8909ff3d0 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( ) require ( - github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08 // indirect + github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gocq/rs v1.0.1 // indirect diff --git a/go.sum b/go.sum index 362a9ebf4..d3d1849c0 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/Mrs4s/MiraiGo v0.0.0-20211201170411-c0713ba75d7f h1:kiNClkzBUTj7z0INL github.com/Mrs4s/MiraiGo v0.0.0-20211201170411-c0713ba75d7f/go.mod h1:xh9dqJAblNEQnZQSrMj216jF3GkrkoWOzkpBjGoZiiY= github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08 h1:VAR3nNyBAc+oPlLKihkLTP0GXf8bQi085y2pSrNzAF0= github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08/go.mod h1:lM1Te2wqdBLPSF7QLaskSn1fGW3Tw2l0iFGsTHTxqIg= +github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= +github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From aa2caac3f78a32e0627d4ed6d0a93c184b60b853 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 6 Dec 2021 22:30:40 +0800 Subject: [PATCH 157/612] feat: support expand env --- modules/config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/config/config.go b/modules/config/config.go index d8707f8c5..c49a1605f 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -149,11 +149,11 @@ type MongoDBConfig struct { func Parse(path string) *Config { fromEnv := os.Getenv("GCQ_UIN") != "" - file, err := os.Open(path) + file, err := os.ReadFile(path) config := &Config{} if err == nil { - defer func() { _ = file.Close() }() - if err = yaml.NewDecoder(file).Decode(config); err != nil && !fromEnv { + err = yaml.NewDecoder(strings.NewReader(os.ExpandEnv(string(file)))).Decode(config) + if err != nil && !fromEnv { log.Fatal("配置文件不合法!", err) } } else if !fromEnv { From 3eade331bfbbdd83361a1ac2319e5de61645f712 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 7 Dec 2021 13:50:51 +0800 Subject: [PATCH 158/612] update dep --- coolq/api.go | 20 ++------------------ coolq/event.go | 15 +++++---------- go.mod | 2 +- go.sum | 6 ++---- 4 files changed, 10 insertions(+), 33 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 687ff47de..372d52f4f 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -135,23 +135,7 @@ func (bot *CQBot) CQGetGuildMembers(guildID uint64) global.MSG { if guild == nil { return Failed(100, "GUILD_NOT_FOUND") } - members := make([]global.MSG, len(guild.Members)) - bots := make([]global.MSG, len(guild.Bots)) - admins := make([]global.MSG, len(guild.Admins)) - for i, m := range guild.Members { - members[i] = convertGuildMemberInfo(m) - } - for i, m := range guild.Bots { - bots[i] = convertGuildMemberInfo(m) - } - for i, m := range guild.Admins { - admins[i] = convertGuildMemberInfo(m) - } - return OK(global.MSG{ - "members": members, - "bots": bots, - "admins": admins, - }) + return OK(nil) // todo } // CQGetGuildRoles 获取频道角色列表 @@ -692,7 +676,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R fixAt := func(elem []message.IMessageElement) { for _, e := range elem { if at, ok := e.(*message.AtElement); ok && at.Target != 0 && at.Display == "" { - mem := guild.FindMember(uint64(at.Target)) + mem, _ := bot.Client.GuildService.GetGuildMemberProfileInfo(guildID, uint64(at.Target)) if mem != nil { at.Display = "@" + mem.Nickname } else { diff --git a/coolq/event.go b/coolq/event.go index 1db0b295e..82a6fe15f 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -148,12 +148,7 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC if guild == nil { return } - var channel *client.ChannelInfo - for _, c := range guild.Channels { - if c.ChannelId == m.ChannelId { - channel = c - } - } + channel := guild.FindChannel(m.ChannelId) source := MessageSource{ SourceType: MessageSourceGuildChannel, PrimaryID: m.GuildId, @@ -244,9 +239,9 @@ func (bot *CQBot) guildChannelCreatedEvent(c *client.QQClient, e *client.GuildCh if guild == nil { return } - member := guild.FindMember(e.OperatorId) + member, _ := c.GuildService.GetGuildMemberProfileInfo(e.GuildId, e.OperatorId) if member == nil { - member = &client.GuildMemberInfo{Nickname: "未知"} + member = &client.GuildUserProfile{Nickname: "未知"} } log.Infof("频道 %v(%v) 内用户 %v(%v) 创建了子频道 %v(%v)", guild.GuildName, guild.GuildId, member.Nickname, member.TinyId, e.ChannelInfo.ChannelName, e.ChannelInfo.ChannelId) bot.dispatchEventMessage(global.MSG{ @@ -268,9 +263,9 @@ func (bot *CQBot) guildChannelDestroyedEvent(c *client.QQClient, e *client.Guild if guild == nil { return } - member := guild.FindMember(e.OperatorId) + member, _ := c.GuildService.GetGuildMemberProfileInfo(e.GuildId, e.OperatorId) if member == nil { - member = &client.GuildMemberInfo{Nickname: "未知"} + member = &client.GuildUserProfile{Nickname: "未知"} } log.Infof("频道 %v(%v) 内用户 %v(%v) 删除了子频道 %v(%v)", guild.GuildName, guild.GuildId, member.Nickname, member.TinyId, e.ChannelInfo.ChannelName, e.ChannelInfo.ChannelId) bot.dispatchEventMessage(global.MSG{ diff --git a/go.mod b/go.mod index 8909ff3d0..be28ff068 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211201170411-c0713ba75d7f + github.com/Mrs4s/MiraiGo v0.0.0-20211207052901-2d17133a7b38 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index d3d1849c0..80f5e891c 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211201170411-c0713ba75d7f h1:kiNClkzBUTj7z0INLxyN8pRXuPSy0gPGv/dXpSKVfmE= -github.com/Mrs4s/MiraiGo v0.0.0-20211201170411-c0713ba75d7f/go.mod h1:xh9dqJAblNEQnZQSrMj216jF3GkrkoWOzkpBjGoZiiY= -github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08 h1:VAR3nNyBAc+oPlLKihkLTP0GXf8bQi085y2pSrNzAF0= -github.com/RomiChan/protobuf v0.0.0-20211126015646-ee3d29b19e08/go.mod h1:lM1Te2wqdBLPSF7QLaskSn1fGW3Tw2l0iFGsTHTxqIg= +github.com/Mrs4s/MiraiGo v0.0.0-20211207052901-2d17133a7b38 h1:IJg8tgGzHg/95Rav7zf33iB2Wx+pqiAnAanaM3Iw7Ug= +github.com/Mrs4s/MiraiGo v0.0.0-20211207052901-2d17133a7b38/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= From 26a7a1f0b659006439a3f17ff02f6ce3bbfdb665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 7 Dec 2021 22:39:30 +0800 Subject: [PATCH 159/612] feat(config): separate config & server (#1212) * feat(server): add RegisterCustom * feat(config): seprate config & server * fix: make lint happy * fix: make lint happy * fix: ParseEnv nil pointer error * typo(config): generateConfig hint * fix(config): panic on range overflow --- modules/config/config.go | 239 +++++++------------------------------ modules/pprof/pprof.go | 26 +++- modules/servers/servers.go | 17 ++- server/http.go | 83 ++++++++++++- server/middlewares.go | 11 ++ server/scf.go | 24 +++- server/websocket.go | 106 +++++++++++++++- 7 files changed, 300 insertions(+), 206 deletions(-) diff --git a/modules/config/config.go b/modules/config/config.go index c49a1605f..33930cc69 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -8,11 +8,12 @@ import ( "os" "strconv" "strings" - - "github.com/Mrs4s/go-cqhttp/internal/param" + "sync" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" + + "github.com/Mrs4s/go-cqhttp/internal/param" ) // defaultConfig 默认配置文件 @@ -69,68 +70,11 @@ type Config struct { Database map[string]yaml.Node `yaml:"database"` } -// MiddleWares 通信中间件 -type MiddleWares struct { - AccessToken string `yaml:"access-token"` - Filter string `yaml:"filter"` - RateLimit struct { - Enabled bool `yaml:"enabled"` - Frequency float64 `yaml:"frequency"` - Bucket int `yaml:"bucket"` - } `yaml:"rate-limit"` -} - -// HTTPServer HTTP通信相关配置 -type HTTPServer struct { - Disabled bool `yaml:"disabled"` - Host string `yaml:"host"` - Port int `yaml:"port"` - Timeout int32 `yaml:"timeout"` - LongPolling struct { - Enabled bool `yaml:"enabled"` - MaxQueueSize int `yaml:"max-queue-size"` - } `yaml:"long-polling"` - Post []struct { - URL string `yaml:"url"` - Secret string `yaml:"secret"` - } - - MiddleWares `yaml:"middlewares"` -} - -// PprofServer pprof性能分析服务器相关配置 -type PprofServer struct { - Disabled bool `yaml:"disabled"` - Host string `yaml:"host"` - Port int `yaml:"port"` -} - -// WebsocketServer 正向WS相关配置 -type WebsocketServer struct { - Disabled bool `yaml:"disabled"` - Host string `yaml:"host"` - Port int `yaml:"port"` - - MiddleWares `yaml:"middlewares"` -} - -// WebsocketReverse 反向WS相关配置 -type WebsocketReverse struct { - Disabled bool `yaml:"disabled"` - Universal string `yaml:"universal"` - API string `yaml:"api"` - Event string `yaml:"event"` - ReconnectInterval int `yaml:"reconnect-interval"` - - MiddleWares `yaml:"middlewares"` -} - -// LambdaServer 云函数配置 -type LambdaServer struct { - Disabled bool `yaml:"disabled"` - Type string `yaml:"type"` - - MiddleWares `yaml:"middlewares"` +// Server 的简介和初始配置 +type Server struct { + Brief string + Default string + ParseEnv func() (string, *yaml.Node) } // LevelDBConfig leveldb 相关配置 @@ -183,161 +127,58 @@ func Parse(path string) *Config { _ = n.Encode(dbConf) return *n }() - accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") - if os.Getenv("GCQ_HTTP_PORT") != "" { - node := &yaml.Node{} - httpConf := &HTTPServer{ - Host: "0.0.0.0", - Port: 5700, - MiddleWares: MiddleWares{ - AccessToken: accessTokenEnv, - }, - } - param.SetExcludeDefault(&httpConf.Disabled, param.EnsureBool(os.Getenv("GCQ_HTTP_DISABLE"), false), false) - param.SetExcludeDefault(&httpConf.Host, os.Getenv("GCQ_HTTP_HOST"), "") - param.SetExcludeDefault(&httpConf.Port, int(toInt64(os.Getenv("GCQ_HTTP_PORT"))), 0) - if os.Getenv("GCQ_HTTP_POST_URL") != "" { - httpConf.Post = append(httpConf.Post, struct { - URL string `yaml:"url"` - Secret string `yaml:"secret"` - }{os.Getenv("GCQ_HTTP_POST_URL"), os.Getenv("GCQ_HTTP_POST_SECRET")}) - } - _ = node.Encode(httpConf) - config.Servers = append(config.Servers, map[string]yaml.Node{"http": *node}) - } - if os.Getenv("GCQ_WS_PORT") != "" { - node := &yaml.Node{} - wsServerConf := &WebsocketServer{ - Host: "0.0.0.0", - Port: 6700, - MiddleWares: MiddleWares{ - AccessToken: accessTokenEnv, - }, - } - param.SetExcludeDefault(&wsServerConf.Disabled, param.EnsureBool(os.Getenv("GCQ_WS_DISABLE"), false), false) - param.SetExcludeDefault(&wsServerConf.Host, os.Getenv("GCQ_WS_HOST"), "") - param.SetExcludeDefault(&wsServerConf.Port, int(toInt64(os.Getenv("GCQ_WS_PORT"))), 0) - _ = node.Encode(wsServerConf) - config.Servers = append(config.Servers, map[string]yaml.Node{"ws": *node}) - } - if os.Getenv("GCQ_RWS_API") != "" || os.Getenv("GCQ_RWS_EVENT") != "" || os.Getenv("GCQ_RWS_UNIVERSAL") != "" { - node := &yaml.Node{} - rwsConf := &WebsocketReverse{ - MiddleWares: MiddleWares{ - AccessToken: accessTokenEnv, - }, + + for _, s := range serverconfs { + if s.ParseEnv != nil { + name, node := s.ParseEnv() + if node != nil { + config.Servers = append(config.Servers, map[string]yaml.Node{name: *node}) + } } - param.SetExcludeDefault(&rwsConf.Disabled, param.EnsureBool(os.Getenv("GCQ_RWS_DISABLE"), false), false) - param.SetExcludeDefault(&rwsConf.API, os.Getenv("GCQ_RWS_API"), "") - param.SetExcludeDefault(&rwsConf.Event, os.Getenv("GCQ_RWS_EVENT"), "") - param.SetExcludeDefault(&rwsConf.Universal, os.Getenv("GCQ_RWS_UNIVERSAL"), "") - _ = node.Encode(rwsConf) - config.Servers = append(config.Servers, map[string]yaml.Node{"ws-reverse": *node}) } } return config } +var serverconfs []*Server +var mu sync.Mutex + +// AddServer 添加该服务的简介和默认配置 +func AddServer(s *Server) { + mu.Lock() + serverconfs = append(serverconfs, s) + mu.Unlock() +} + // generateConfig 生成配置文件 func generateConfig() { fmt.Println("未找到配置文件,正在为您生成配置文件中!") sb := strings.Builder{} sb.WriteString(defaultConfig) - fmt.Print(`请选择你需要的通信方式: -> 1: HTTP通信 -> 2: 正向 Websocket 通信 -> 3: 反向 Websocket 通信 -> 4: pprof 性能分析服务器 -> 5: 云函数服务 -请输入你需要的编号,可输入多个,同一编号也可输入多个(如: 233) -您的选择是:`) + hint := "请选择你需要的通信方式:" + for i, s := range serverconfs { + hint += fmt.Sprintf("\n> %d: %s", i, s.Brief) + } + hint += ` +请输入你需要的编号(0-9),可输入多个,同一编号也可输入多个(如: 233) +您的选择是:` + fmt.Print(hint) input := bufio.NewReader(os.Stdin) readString, err := input.ReadString('\n') if err != nil { log.Fatal("输入不合法: ", err) } + rmax := len(serverconfs) + if rmax > 10 { + rmax = 10 + } for _, r := range readString { - switch r { - case '1': - sb.WriteString(httpDefault) - case '2': - sb.WriteString(wsDefault) - case '3': - sb.WriteString(wsReverseDefault) - case '4': - sb.WriteString(pprofDefault) - case '5': - sb.WriteString(lambdaDefault) + r -= '0' + if r >= 0 && r < rune(rmax) { + sb.WriteString(serverconfs[r].Default) } } _ = os.WriteFile("config.yml", []byte(sb.String()), 0o644) fmt.Println("默认配置文件已生成,请修改 config.yml 后重新启动!") _, _ = input.ReadString('\n') } - -const httpDefault = ` # HTTP 通信设置 - - http: - # 服务端监听地址 - host: 127.0.0.1 - # 服务端监听端口 - port: 5700 - # 反向HTTP超时时间, 单位秒 - # 最小值为5,小于5将会忽略本项设置 - timeout: 5 - # 长轮询拓展 - long-polling: - # 是否开启 - enabled: false - # 消息队列大小,0 表示不限制队列大小,谨慎使用 - max-queue-size: 2000 - middlewares: - <<: *default # 引用默认中间件 - # 反向HTTP POST地址列表 - post: - #- url: '' # 地址 - # secret: '' # 密钥 - #- url: http://127.0.0.1:5701/ # 地址 - # secret: '' # 密钥 -` - -const lambdaDefault = ` # LambdaServer 配置 - - lambda: - type: scf # scf: 腾讯云函数 aws: aws Lambda - middlewares: - <<: *default # 引用默认中间件 -` - -const wsDefault = ` # 正向WS设置 - - ws: - # 正向WS服务器监听地址 - host: 127.0.0.1 - # 正向WS服务器监听端口 - port: 6700 - middlewares: - <<: *default # 引用默认中间件 -` - -const wsReverseDefault = ` # 反向WS设置 - - ws-reverse: - # 反向WS Universal 地址 - # 注意 设置了此项地址后下面两项将会被忽略 - universal: ws://your_websocket_universal.server - # 反向WS API 地址 - api: ws://your_websocket_api.server - # 反向WS Event 地址 - event: ws://your_websocket_event.server - # 重连间隔 单位毫秒 - reconnect-interval: 3000 - middlewares: - <<: *default # 引用默认中间件 -` - -const pprofDefault = ` # pprof 性能分析服务器, 一般情况下不需要启用. - # 如果遇到性能问题请上传报告给开发者处理 - # 注意: pprof服务不支持中间件、不支持鉴权. 请不要开放到公网 - - pprof: - # pprof服务器监听地址 - host: 127.0.0.1 - # pprof服务器监听端口 - port: 7700 -` diff --git a/modules/pprof/pprof.go b/modules/pprof/pprof.go index f12dbed49..56e1494d7 100644 --- a/modules/pprof/pprof.go +++ b/modules/pprof/pprof.go @@ -16,9 +16,33 @@ import ( "github.com/Mrs4s/go-cqhttp/modules/servers" ) +const pprofDefault = ` # pprof 性能分析服务器, 一般情况下不需要启用. + # 如果遇到性能问题请上传报告给开发者处理 + # 注意: pprof服务不支持中间件、不支持鉴权. 请不要开放到公网 + - pprof: + # pprof服务器监听地址 + host: 127.0.0.1 + # pprof服务器监听端口 + port: 7700 +` + +// pprofServer pprof性能分析服务器相关配置 +type pprofServer struct { + Disabled bool `yaml:"disabled"` + Host string `yaml:"host"` + Port int `yaml:"port"` +} + +func init() { + config.AddServer(&config.Server{ + Brief: "pprof 性能分析服务器", + Default: pprofDefault, + }) +} + // runPprof 启动 pprof 性能分析服务器 func runPprof(_ *coolq.CQBot, node yaml.Node) { - var conf config.PprofServer + var conf pprofServer switch err := node.Decode(&conf); { case err != nil: log.Warn("读取pprof配置失败 :", err) diff --git a/modules/servers/servers.go b/modules/servers/servers.go index 579f21bc9..7a8ae2890 100644 --- a/modules/servers/servers.go +++ b/modules/servers/servers.go @@ -8,7 +8,10 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" ) -var svr = make(map[string]func(*coolq.CQBot, yaml.Node)) +var ( + svr = make(map[string]func(*coolq.CQBot, yaml.Node)) + nocfgsvr = make(map[string]func(*coolq.CQBot)) +) // Register 注册 Server func Register(name string, proc func(*coolq.CQBot, yaml.Node)) { @@ -19,6 +22,15 @@ func Register(name string, proc func(*coolq.CQBot, yaml.Node)) { svr[name] = proc } +// RegisterCustom 注册无需 config 的自定义 Server +func RegisterCustom(name string, proc func(*coolq.CQBot)) { + _, ok := nocfgsvr[name] + if ok { + panic(name + " server has existed") + } + nocfgsvr[name] = proc +} + // Run 运行所有svr func Run(bot *coolq.CQBot) { for _, l := range base.Servers { @@ -28,4 +40,7 @@ func Run(bot *coolq.CQBot) { } } } + for _, fn := range nocfgsvr { + go fn(bot) + } } diff --git a/server/http.go b/server/http.go index 2c4c81108..fc233c2fc 100644 --- a/server/http.go +++ b/server/http.go @@ -24,11 +24,30 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" ) +// HTTPServer HTTP通信相关配置 +type HTTPServer struct { + Disabled bool `yaml:"disabled"` + Host string `yaml:"host"` + Port int `yaml:"port"` + Timeout int32 `yaml:"timeout"` + LongPolling struct { + Enabled bool `yaml:"enabled"` + MaxQueueSize int `yaml:"max-queue-size"` + } `yaml:"long-polling"` + Post []struct { + URL string `yaml:"url"` + Secret string `yaml:"secret"` + } + + MiddleWares `yaml:"middlewares"` +} + type httpServer struct { HTTP *http.Server api *api.Caller @@ -51,6 +70,68 @@ type httpCtx struct { postForm url.Values } +const httpDefault = ` # HTTP 通信设置 + - http: + # 服务端监听地址 + host: 127.0.0.1 + # 服务端监听端口 + port: 5700 + # 反向HTTP超时时间, 单位秒 + # 最小值为5,小于5将会忽略本项设置 + timeout: 5 + # 长轮询拓展 + long-polling: + # 是否开启 + enabled: false + # 消息队列大小,0 表示不限制队列大小,谨慎使用 + max-queue-size: 2000 + middlewares: + <<: *default # 引用默认中间件 + # 反向HTTP POST地址列表 + post: + #- url: '' # 地址 + # secret: '' # 密钥 + #- url: http://127.0.0.1:5701/ # 地址 + # secret: '' # 密钥 +` + +func init() { + config.AddServer(&config.Server{ + Brief: "HTTP通信", + Default: httpDefault, + ParseEnv: func() (string, *yaml.Node) { + if os.Getenv("GCQ_HTTP_PORT") != "" { + // type convert tools + toInt64 := func(str string) int64 { + i, _ := strconv.ParseInt(str, 10, 64) + return i + } + accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") + node := &yaml.Node{} + httpConf := &HTTPServer{ + Host: "0.0.0.0", + Port: 5700, + MiddleWares: MiddleWares{ + AccessToken: accessTokenEnv, + }, + } + param.SetExcludeDefault(&httpConf.Disabled, param.EnsureBool(os.Getenv("GCQ_HTTP_DISABLE"), false), false) + param.SetExcludeDefault(&httpConf.Host, os.Getenv("GCQ_HTTP_HOST"), "") + param.SetExcludeDefault(&httpConf.Port, int(toInt64(os.Getenv("GCQ_HTTP_PORT"))), 0) + if os.Getenv("GCQ_HTTP_POST_URL") != "" { + httpConf.Post = append(httpConf.Post, struct { + URL string `yaml:"url"` + Secret string `yaml:"secret"` + }{os.Getenv("GCQ_HTTP_POST_URL"), os.Getenv("GCQ_HTTP_POST_SECRET")}) + } + _ = node.Encode(httpConf) + return "http", node + } + return "", nil + }, + }) +} + func (h *httpCtx) Get(s string) gjson.Result { j := h.json.Get(s) if j.Exists() { @@ -163,7 +244,7 @@ func checkAuth(req *http.Request, token string) int { // runHTTP 启动HTTP服务器与HTTP上报客户端 func runHTTP(bot *coolq.CQBot, node yaml.Node) { - var conf config.HTTPServer + var conf HTTPServer switch err := node.Decode(&conf); { case err != nil: log.Warn("读取http配置失败 :", err) diff --git a/server/middlewares.go b/server/middlewares.go index aff1a8de9..d7835a42f 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -13,6 +13,17 @@ import ( "golang.org/x/time/rate" ) +// MiddleWares 通信中间件 +type MiddleWares struct { + AccessToken string `yaml:"access-token"` + Filter string `yaml:"filter"` + RateLimit struct { + Enabled bool `yaml:"enabled"` + Frequency float64 `yaml:"frequency"` + Bucket int `yaml:"bucket"` + } `yaml:"rate-limit"` +} + func rateLimit(frequency float64, bucketSize int) api.Handler { limiter := rate.NewLimiter(rate.Limit(frequency), bucketSize) return func(_ string, _ api.Getter) global.MSG { diff --git a/server/scf.go b/server/scf.go index d863419ff..29546ae41 100644 --- a/server/scf.go +++ b/server/scf.go @@ -81,7 +81,7 @@ var cli *lambdaClient // runLambda type: [scf,aws] func runLambda(bot *coolq.CQBot, node yaml.Node) { - var conf config.LambdaServer + var conf LambdaServer switch err := node.Decode(&conf); { case err != nil: log.Warn("读取lambda配置失败 :", err) @@ -155,6 +155,28 @@ type lambdaInvoke struct { } `json:"requestContext"` } +const lambdaDefault = ` # LambdaServer 配置 + - lambda: + type: scf # scf: 腾讯云函数 aws: aws Lambda + middlewares: + <<: *default # 引用默认中间件 +` + +// LambdaServer 云函数配置 +type LambdaServer struct { + Disabled bool `yaml:"disabled"` + Type string `yaml:"type"` + + MiddleWares `yaml:"middlewares"` +} + +func init() { + config.AddServer(&config.Server{ + Brief: "云函数服务", + Default: lambdaDefault, + }) +} + func (c *lambdaClient) next() *http.Request { r, err := http.NewRequest(http.MethodGet, c.nextURL, nil) if err != nil { diff --git a/server/websocket.go b/server/websocket.go index 38fb96ae3..6cdf658a9 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "os" "runtime/debug" "strconv" "strings" @@ -19,6 +20,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -26,7 +28,7 @@ import ( type webSocketServer struct { bot *coolq.CQBot - conf *config.WebsocketServer + conf *WebsocketServer mu sync.Mutex eventConn []*wsConn @@ -71,9 +73,107 @@ var upgrader = websocket.Upgrader{ }, } +const wsDefault = ` # 正向WS设置 + - ws: + # 正向WS服务器监听地址 + host: 127.0.0.1 + # 正向WS服务器监听端口 + port: 6700 + middlewares: + <<: *default # 引用默认中间件 +` + +const wsReverseDefault = ` # 反向WS设置 + - ws-reverse: + # 反向WS Universal 地址 + # 注意 设置了此项地址后下面两项将会被忽略 + universal: ws://your_websocket_universal.server + # 反向WS API 地址 + api: ws://your_websocket_api.server + # 反向WS Event 地址 + event: ws://your_websocket_event.server + # 重连间隔 单位毫秒 + reconnect-interval: 3000 + middlewares: + <<: *default # 引用默认中间件 +` + +// WebsocketServer 正向WS相关配置 +type WebsocketServer struct { + Disabled bool `yaml:"disabled"` + Host string `yaml:"host"` + Port int `yaml:"port"` + + MiddleWares `yaml:"middlewares"` +} + +// WebsocketReverse 反向WS相关配置 +type WebsocketReverse struct { + Disabled bool `yaml:"disabled"` + Universal string `yaml:"universal"` + API string `yaml:"api"` + Event string `yaml:"event"` + ReconnectInterval int `yaml:"reconnect-interval"` + + MiddleWares `yaml:"middlewares"` +} + +func init() { + config.AddServer(&config.Server{ + Brief: "正向 Websocket 通信", + Default: wsDefault, + ParseEnv: func() (string, *yaml.Node) { + if os.Getenv("GCQ_WS_PORT") != "" { + // type convert tools + toInt64 := func(str string) int64 { + i, _ := strconv.ParseInt(str, 10, 64) + return i + } + accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") + node := &yaml.Node{} + wsServerConf := &WebsocketServer{ + Host: "0.0.0.0", + Port: 6700, + MiddleWares: MiddleWares{ + AccessToken: accessTokenEnv, + }, + } + param.SetExcludeDefault(&wsServerConf.Disabled, param.EnsureBool(os.Getenv("GCQ_WS_DISABLE"), false), false) + param.SetExcludeDefault(&wsServerConf.Host, os.Getenv("GCQ_WS_HOST"), "") + param.SetExcludeDefault(&wsServerConf.Port, int(toInt64(os.Getenv("GCQ_WS_PORT"))), 0) + _ = node.Encode(wsServerConf) + return "ws", node + } + return "", nil + }, + }) + config.AddServer(&config.Server{ + Brief: "反向 Websocket 通信", + Default: wsReverseDefault, + ParseEnv: func() (string, *yaml.Node) { + if os.Getenv("GCQ_RWS_API") != "" || os.Getenv("GCQ_RWS_EVENT") != "" || os.Getenv("GCQ_RWS_UNIVERSAL") != "" { + accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") + node := &yaml.Node{} + rwsConf := &WebsocketReverse{ + MiddleWares: MiddleWares{ + AccessToken: accessTokenEnv, + }, + } + param.SetExcludeDefault(&rwsConf.Disabled, param.EnsureBool(os.Getenv("GCQ_RWS_DISABLE"), false), false) + param.SetExcludeDefault(&rwsConf.API, os.Getenv("GCQ_RWS_API"), "") + param.SetExcludeDefault(&rwsConf.Event, os.Getenv("GCQ_RWS_EVENT"), "") + param.SetExcludeDefault(&rwsConf.Universal, os.Getenv("GCQ_RWS_UNIVERSAL"), "") + _ = node.Encode(rwsConf) + return "ws-reverse", node + } + return "", nil + }, + }) +} + // runWSServer 运行一个正向WS server func runWSServer(b *coolq.CQBot, node yaml.Node) { - var conf config.WebsocketServer + var conf WebsocketServer switch err := node.Decode(&conf); { case err != nil: log.Warn("读取正向Websocket配置失败 :", err) @@ -103,7 +203,7 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) { // runWSClient 运行一个反向向WS client func runWSClient(b *coolq.CQBot, node yaml.Node) { - var conf config.WebsocketReverse + var conf WebsocketReverse switch err := node.Decode(&conf); { case err != nil: log.Warn("读取反向Websocket配置失败 :", err) From 8b8a59f6d172fc0a9b91ee26cba543ecbd1b3b14 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 8 Dec 2021 13:49:29 +0800 Subject: [PATCH 160/612] update dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index be28ff068..901f7ed80 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211207052901-2d17133a7b38 + github.com/Mrs4s/MiraiGo v0.0.0-20211208054742-2caf71a0bb47 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 80f5e891c..d2981fe88 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211207052901-2d17133a7b38 h1:IJg8tgGzHg/95Rav7zf33iB2Wx+pqiAnAanaM3Iw7Ug= -github.com/Mrs4s/MiraiGo v0.0.0-20211207052901-2d17133a7b38/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211208054742-2caf71a0bb47 h1:A3oKsmP6vPaRUK6KKASpm80TrWZHggLQAI1Q41ENwnk= +github.com/Mrs4s/MiraiGo v0.0.0-20211208054742-2caf71a0bb47/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= From 0aff858c0a118a1cdef49c17205c96ace0686ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 8 Dec 2021 16:22:53 +0800 Subject: [PATCH 161/612] =?UTF-8?q?feat:=20=E5=B0=86=E9=AD=94=E6=94=B9?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=94=BE=E5=9B=9E=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gocq/main.go | 12 +++++++++++- go.mod | 15 +++++++++++++++ go.sum | 16 ++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 30494ee0b..366740c64 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -19,6 +19,8 @@ import ( "golang.org/x/crypto/pbkdf2" "golang.org/x/term" + "github.com/Mrs4s/go-cqhttp/qqrobot" + "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" @@ -337,10 +339,18 @@ func Main() { } cli.SetOnlineStatus(allowStatus[base.Account.Status]) - servers.Run(coolq.NewQQBot(cli)) + bot := coolq.NewQQBot(cli) + servers.Run(bot) log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") + log.Info("以下内容基于 go-cqhttp 魔改而成,powered by 风之凌殇") + log.Info("启动本地的机器人处理程序,用于直接收发消息和自动回复") + robot := qqrobot.NewQQRobot(bot, "config.toml") + robot.RegisterHandlers() + robot.Start() + defer robot.Stop() + go selfupdate.CheckUpdate() go func() { time.Sleep(5 * time.Second) diff --git a/go.mod b/go.mod index 901f7ed80..a96e4182a 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,21 @@ module github.com/Mrs4s/go-cqhttp go 1.17 +replace github.com/Mrs4s/go-cqhttp => github.com/fzls/qq_robot_go v1.0.0-beta8 + +// 魔改后需要额外引入的依赖项,单独列出,避免后面又冲突 +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/fzls/logger v1.1.1 + github.com/gookit/color v1.5.0 + github.com/hashicorp/golang-lru v0.5.4 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 +) + +require github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + +// 以下为go-cqhttp原本的依赖 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 diff --git a/go.sum b/go.sum index d2981fe88..26cdfe2cf 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= @@ -17,6 +18,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= +github.com/fzls/logger v1.1.1 h1:9ZteZN4pxh16MVsjlNgCurQb6qBDxKYDKKq7zifWHIU= +github.com/fzls/logger v1.1.1/go.mod h1:FBZoU1PuXNCQBNr19NZjUWCq1vV6/FoGwc3D9q6of/0= github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -63,8 +66,12 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -137,11 +144,16 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 h1:2yISRb1sPRr0lUye5M/Co4uvyj7Y1JfF/7nq2vayF4g= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 h1:m7y3kw3JfAmyIiz/mwWEcuV+WayjrxrfZpiqW94uJU4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4= github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -157,6 +169,8 @@ github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= @@ -203,6 +217,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -245,6 +260,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 44e0ff44fb56053c721d2032794dfc93791dc667 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 8 Dec 2021 17:17:33 +0800 Subject: [PATCH 162/612] update dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 901f7ed80..0d5410f74 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211208054742-2caf71a0bb47 + github.com/Mrs4s/MiraiGo v0.0.0-20211208080234-25c67a3ee1c1 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index d2981fe88..9ea0cba0d 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211208054742-2caf71a0bb47 h1:A3oKsmP6vPaRUK6KKASpm80TrWZHggLQAI1Q41ENwnk= -github.com/Mrs4s/MiraiGo v0.0.0-20211208054742-2caf71a0bb47/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211208080234-25c67a3ee1c1 h1:UipCzEST10GzJnvlhHsY4g39xzwVzSHE+5Go9d0dTPY= +github.com/Mrs4s/MiraiGo v0.0.0-20211208080234-25c67a3ee1c1/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= From 7278f99ed9118fe2e54aca752e62574d5bae3f00 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 8 Dec 2021 17:26:13 +0800 Subject: [PATCH 163/612] api: temporary delete get_guild_members api --- coolq/api.go | 2 ++ modules/api/api.go | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 372d52f4f..f63c4e839 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -128,6 +128,7 @@ func (bot *CQBot) CQGetGuildChannelList(guildID uint64, noCache bool) global.MSG return OK(channels) } +/* // CQGetGuildMembers 获取频道成员列表 // @route(get_guild_members) func (bot *CQBot) CQGetGuildMembers(guildID uint64) global.MSG { @@ -137,6 +138,7 @@ func (bot *CQBot) CQGetGuildMembers(guildID uint64) global.MSG { } return OK(nil) // todo } +*/ // CQGetGuildRoles 获取频道角色列表 // @route(get_guild_roles) diff --git a/modules/api/api.go b/modules/api/api.go index 41b928139..b0a047b72 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -140,9 +140,6 @@ func (c *Caller) call(action string, p Getter) global.MSG { return c.bot.CQGetGuildChannelList(p0, p1) case "get_guild_list": return c.bot.CQGetGuildList() - case "get_guild_members": - p0 := p.Get("guild_id").Uint() - return c.bot.CQGetGuildMembers(p0) case "get_guild_meta_by_guest": p0 := p.Get("guild_id").Uint() return c.bot.CQGetGuildMetaByGuest(p0) From e69051e88bcd4d08937c90262f850eae8eae4df5 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 8 Dec 2021 21:52:00 +0800 Subject: [PATCH 164/612] feat: get_guild_member_list api --- coolq/api.go | 56 +++++++++++++++++++++++++++++++++++++++++----- coolq/bot.go | 4 +++- coolq/converter.go | 16 ++++++++----- go.mod | 2 +- go.sum | 4 ++-- modules/api/api.go | 4 ++++ 6 files changed, 71 insertions(+), 15 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index f63c4e839..ffc94f379 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/segmentio/asm/base64" "math" "os" "path" @@ -29,6 +30,19 @@ import ( "github.com/Mrs4s/go-cqhttp/modules/filter" ) +type guildMemberPageToken struct { + guildID uint64 + nextIndex uint32 + nextRoleID uint64 + nextQueryParam string +} + +var defaultPageToken = &guildMemberPageToken{ + guildID: 0, + nextIndex: 0, + nextRoleID: 2, +} + // CQGetLoginInfo 获取登录号信息 // // https://git.io/Jtz1I @@ -128,17 +142,49 @@ func (bot *CQBot) CQGetGuildChannelList(guildID uint64, noCache bool) global.MSG return OK(channels) } -/* // CQGetGuildMembers 获取频道成员列表 -// @route(get_guild_members) -func (bot *CQBot) CQGetGuildMembers(guildID uint64) global.MSG { +// @route(get_guild_member_list) +func (bot *CQBot) CQGetGuildMembers(guildID uint64, nextToken string) global.MSG { guild := bot.Client.GuildService.FindGuild(guildID) if guild == nil { return Failed(100, "GUILD_NOT_FOUND") } - return OK(nil) // todo + token := defaultPageToken + if nextToken != "" { + i, exists := bot.nextTokenCache.Get(nextToken) + if !exists { + return Failed(100, "NEXT_TOKEN_NOT_EXISTS") + } + token = i.(*guildMemberPageToken) + if token.guildID != guildID { + return Failed(100, "GUILD_NOT_MATCH") + } + } + ret, err := bot.Client.GuildService.FetchGuildMemberListWithRole(guildID, 0, token.nextIndex, token.nextRoleID, token.nextQueryParam) + if err != nil { + return Failed(100, "API_ERROR", err.Error()) + } + res := global.MSG{ + "members": convertGuildMemberInfo(ret.Members), + "finished": ret.Finished, + "next_token": nil, + } + if !ret.Finished { + next := &guildMemberPageToken{ + guildID: guildID, + nextIndex: ret.NextIndex, + nextRoleID: ret.NextRoleId, + nextQueryParam: ret.NextQueryParam, + } + id := base64.StdEncoding.EncodeToString(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt64(uint64(time.Now().UnixNano())) + w.WriteString(utils.RandomString(5)) + })) + bot.nextTokenCache.Add(id, next, time.Minute*10) + res["next_token"] = id + } + return OK(res) } -*/ // CQGetGuildRoles 获取频道角色列表 // @route(get_guild_roles) diff --git a/coolq/bot.go b/coolq/bot.go index 384697c35..97dc448f6 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -34,6 +34,7 @@ type CQBot struct { friendReqCache sync.Map tempSessionCache sync.Map + nextTokenCache *utils.Cache } // Event 事件 @@ -67,7 +68,8 @@ func (e *Event) JSONString() string { // NewQQBot 初始化一个QQBot实例 func NewQQBot(cli *client.QQClient) *CQBot { bot := &CQBot{ - Client: cli, + Client: cli, + nextTokenCache: utils.NewCache(time.Second * 10), } bot.Client.OnPrivateMessage(bot.privateMessageEvent) bot.Client.OnGroupMessage(bot.groupMessageEvent) diff --git a/coolq/converter.go b/coolq/converter.go index bd53eb283..e0253843b 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -52,13 +52,17 @@ func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG } } -func convertGuildMemberInfo(m *client.GuildMemberInfo) global.MSG { - return global.MSG{ - "tiny_id": fU64(m.TinyId), - "title": m.Title, - "nickname": m.Nickname, - "role": m.Role, +func convertGuildMemberInfo(m []*client.GuildMemberInfo) (r []global.MSG) { + for _, mem := range m { + r = append(r, global.MSG{ + "tiny_id": fU64(mem.TinyId), + "title": mem.Title, + "nickname": mem.Nickname, + "role_id": fU64(mem.Role), + "role_name": mem.RoleName, + }) } + return } func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { diff --git a/go.mod b/go.mod index 0d5410f74..ebcd4771b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211208080234-25c67a3ee1c1 + github.com/Mrs4s/MiraiGo v0.0.0-20211208132533-8cd25e02fcf3 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 9ea0cba0d..15b2bbbdf 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211208080234-25c67a3ee1c1 h1:UipCzEST10GzJnvlhHsY4g39xzwVzSHE+5Go9d0dTPY= -github.com/Mrs4s/MiraiGo v0.0.0-20211208080234-25c67a3ee1c1/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211208132533-8cd25e02fcf3 h1:w42Z7gE+HjBcjXDjVZ8o5rzNgAzt7nhuL84HOYpIhRE= +github.com/Mrs4s/MiraiGo v0.0.0-20211208132533-8cd25e02fcf3/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= diff --git a/modules/api/api.go b/modules/api/api.go index b0a047b72..490f2b115 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -140,6 +140,10 @@ func (c *Caller) call(action string, p Getter) global.MSG { return c.bot.CQGetGuildChannelList(p0, p1) case "get_guild_list": return c.bot.CQGetGuildList() + case "get_guild_member_list": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("next_token").String() + return c.bot.CQGetGuildMembers(p0, p1) case "get_guild_meta_by_guest": p0 := p.Get("guild_id").Uint() return c.bot.CQGetGuildMetaByGuest(p0) From c61de3473264f0543ff5c85cdbe6041a2684b774 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Dec 2021 13:52:55 +0000 Subject: [PATCH 165/612] ci(chore): Fix stylings --- coolq/api.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index ffc94f379..7836eca2e 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/segmentio/asm/base64" "math" "os" "path" @@ -15,6 +14,8 @@ import ( "strings" "time" + "github.com/segmentio/asm/base64" + "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" From 8ba93cbf7c9be2ba002149f2af597b51de248dbf Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 8 Dec 2021 22:32:27 +0800 Subject: [PATCH 166/612] ci: use commit sha version --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8ca988c4..c676ae1a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,8 +5,8 @@ on: [push, pull_request,workflow_dispatch] env: BINARY_PREFIX: "go-cqhttp_" BINARY_SUFFIX: "" + COMMIT_ID: "${{ github.sha }}" PR_PROMPT: "::warning:: Build artifact will not be uploaded due to the workflow is trigged by pull request." - LD_FLAGS: "-w -s" jobs: build: @@ -46,7 +46,7 @@ jobs: if $IS_PR ; then echo $PR_PROMPT; fi export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 - go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . + go build -o "output/$BINARY_NAME" -trimpath -ldflags "-w -s -X main.version=${COMMIT_ID::7}" . - name: Upload artifact uses: actions/upload-artifact@v2 if: ${{ !github.head_ref }} From a9c1f2e5f3cbf31fd3d906a54c758bb1461ee18c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 8 Dec 2021 22:52:37 +0800 Subject: [PATCH 167/612] ci: fix version path --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c676ae1a4..801395151 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,8 @@ jobs: if $IS_PR ; then echo $PR_PROMPT; fi export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 - go build -o "output/$BINARY_NAME" -trimpath -ldflags "-w -s -X main.version=${COMMIT_ID::7}" . + export LD_FLAGS="-w -s -X github.com/Mrs4s/go-cqhttp/internal/base.Version=${COMMIT_ID::7}" + go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . - name: Upload artifact uses: actions/upload-artifact@v2 if: ${{ !github.head_ref }} From 9f7f5a6dea0b55f8fe98dbcf21719080b2907f6d Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 9 Dec 2021 00:54:11 +0800 Subject: [PATCH 168/612] doc: update guild doc --- docs/guild.md | 86 +++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/docs/guild.md b/docs/guild.md index f1676729c..1756f106a 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -29,6 +29,8 @@ API以及字段相关命名均为参考QQ官方命名或相似产品命名规则 - `at` 消息的 `target` 依然使用 `qq` 字段, 以保证一致性. 但内容为 `tiny_id` - 所有事件的 `self_id` 均为 BOT 的QQ号. `tiny_id` 将放在 `self_tiny_id` 字段 - 遵循我们一贯的原则, 将不会支持主动加频道/主动拉人/红包相关消息类型 +- 频道相关的API仅能在 `Android Phone` 和 `iPad` 协议上使用. +- 由于频道相关ID的数据类型均为 `uint64` , 为保证不超过某些语言的安全值范围, 所以ID相关数据均转换为 `string` 类型, API调用两种类型均可接受. ## API @@ -41,7 +43,7 @@ API以及字段相关命名均为参考QQ官方命名或相似产品命名规则 | 字段 | 类型 | 说明 | | ------------- | ----- | ---------- | | `nickname` | string | 昵称 | -| `tiny_id` | uint64 | 自身的ID | +| `tiny_id` | string | 自身的ID | | `avatar_url` | string | 头像链接 | ### 获取频道列表 @@ -56,7 +58,7 @@ GuildInfo: | 字段 | 类型 | 说明 | | ------------- | ----- | ---------- | -| `guild_id` | uint64 | 频道ID | +| `guild_id` | string | 频道ID | | `guild_name` | string | 频道名称 | | `guild_display_id` | int64 | 频道显示ID, 公测后可能作为搜索ID使用 | @@ -68,13 +70,13 @@ GuildInfo: | 字段 | 类型 | 说明 | | ---------- | ----- | ---- | -| `guild_id` | uint64 | 频道ID | +| `guild_id` | string | 频道ID | **响应数据** | 字段 | 类型 | 说明 | | ------------- | ----- | ---------- | -| `guild_id` | uint64 | 频道ID | +| `guild_id` | string | 频道ID | | `guild_name` | string | 频道名称 | | `guild_profile` | string | 频道简介 | | `create_time` | int64 | 创建时间 | @@ -82,7 +84,7 @@ GuildInfo: | `max_robot_count` | int64 | 频道BOT数上限 | | `max_admin_count` | int64 | 频道管理员人数上限 | | `member_count` | int64 | 已加入人数 | -| `owner_id` | uint64 | 创建者ID | +| `owner_id` | string | 创建者ID | ### 获取子频道列表 @@ -92,7 +94,7 @@ GuildInfo: | 字段 | 类型 | 说明 | | ---------- | ----- | ---- | -| `guild_id` | uint64 | 频道ID | +| `guild_id` | string | 频道ID | | `no_cache` | bool | 是否无视缓存 | **响应数据** @@ -103,13 +105,12 @@ ChannelInfo: | 字段 | 类型 | 说明 | | ------------- | ----- | ---------- | -| `owner_guild_id` | uint64 | 所属频道ID | -| `channel_id` | uint64 | 子频道ID | +| `owner_guild_id` | string | 所属频道ID | +| `channel_id` | string | 子频道ID | | `channel_type` | int32 | 子频道类型 | | `channel_name` | string | 之频道名称 | | `create_time` | int64 | 创建时间 | -| `creator_id` | int64 | 创建者QQ号 | -| `creator_tiny_id` | uint64 | 创建者ID | +| `creator_tiny_id` | string | 创建者ID | | `talk_permission` | int32 | 发言权限类型 | | `visible_type` | int32 | 可视性类型 | | `current_slow_mode` | int32 | 当前启用的慢速模式Key | @@ -131,35 +132,24 @@ SlowModeInfo: | 1 | 文字频道 | | 2 | 语音频道 | | 5 | 直播频道 | +| 7 | 主题频道 | ### 获取频道成员列表 -终结点: `/get_guild_members` +终结点: `/get_guild_member_list` **参数** | 字段 | 类型 | 说明 | | ---------- | ----- | ---- | -| `guild_id` | uint64 | 频道ID | +| `guild_id` | string | 频道ID | +| `next_token` | string | 翻页Token | -**响应数据** - -> 注意: 类型内无任何成员将返回 `null` +> `next_token` 为空的话即返回第一页数据 +> +> 由于频道人数太多(数万), 请尽量不要全量拉取成员列表, 这可能会导致严重的性能问题 -| 字段 | 类型 | 说明 | -| ------------- | ----- | ---------- | -| `members` | []GuildMemberInfo | 普通成员列表 | -| `bots` | []GuildMemberInfo | 机器人列表 | -| `admins` | []GuildMemberInfo | 管理员列表 | -GuildMemberInfo: - -| 字段 | 类型 | 说明 | -| ------------- | ----- | ---------- | -| `tiny_id` | uint64 | 成员ID | -| `title` | string | 成员头衔 | -| `nickname` | string | 成员昵称 | -| `role` | int32 | 成员权限 | ### 发送信息到子频道 @@ -169,8 +159,8 @@ GuildMemberInfo: | 字段 | 类型 | 说明 | | ---------- | ----- | ---- | -| `guild_id` | uint64 | 频道ID | -| `channel_id` | uint64 | 子频道ID | +| `guild_id` | string | 频道ID | +| `channel_id` | string | 子频道ID | | `message` | Message | 消息, 与原有消息类型相同 | **响应数据** @@ -190,9 +180,9 @@ GuildMemberInfo: | `post_type` | string | `message` | 上报类型 | | `message_type` | string | `guild` | 消息类型 | | `sub_type` | string | `channel` | 消息子类型 | -| `guild_id` | uint64 | | 频道ID | -| `channel_id` | uint64 | | 子频道ID | -| `user_id` | uint64 | | 消息发送者ID | +| `guild_id` | string | | 频道ID | +| `channel_id` | string | | 子频道ID | +| `user_id` | string | | 消息发送者ID | | `message_id` | string | | 消息ID | | `sender` | Sender | | 发送者 | | `message` | Message | | 消息内容 | @@ -205,9 +195,9 @@ GuildMemberInfo: | ------------- | ------ | -------------- | -------------- | | `post_type` | string | `notice` | 上报类型 | | `notice_type` | string | `message_reactions_updated` | 消息类型 | -| `guild_id` | uint64 | | 频道ID | -| `channel_id` | uint64 | | 子频道ID | -| `user_id` | uint64 | | 操作者ID | +| `guild_id` | string | | 频道ID | +| `channel_id` | string | | 子频道ID | +| `user_id` | string | | 操作者ID | | `message_id` | string | | 消息ID | | `current_reactions` | []ReactionInfo | | 当前消息被贴表情列表 | @@ -230,10 +220,10 @@ ReactionInfo: | ------------- | ------ | -------------- | -------------- | | `post_type` | string | `notice` | 上报类型 | | `notice_type` | string | `channel_updated` | 消息类型 | -| `guild_id` | uint64 | | 频道ID | -| `channel_id` | uint64 | | 子频道ID | -| `user_id` | uint64 | | 操作者ID | -| `operator_id` | uint64 | | 操作者ID | +| `guild_id` | string | | 频道ID | +| `channel_id` | string | | 子频道ID | +| `user_id` | string | | 操作者ID | +| `operator_id` | string | | 操作者ID | | `old_info` | ChannelInfo | | 更新前的频道信息 | | `new_info` | ChannelInfo | | 更新后的频道信息 | @@ -245,10 +235,10 @@ ReactionInfo: | ------------- | ------ | -------------- | -------------- | | `post_type` | string | `notice` | 上报类型 | | `notice_type` | string | `channel_created` | 消息类型 | -| `guild_id` | uint64 | | 频道ID | -| `channel_id` | uint64 | | 子频道ID | -| `user_id` | uint64 | | 操作者ID | -| `operator_id` | uint64 | | 操作者ID | +| `guild_id` | string | | 频道ID | +| `channel_id` | string | | 子频道ID | +| `user_id` | string | | 操作者ID | +| `operator_id` | string | | 操作者ID | | `channel_info` | ChannelInfo | | 频道信息 | ### 子频道删除 @@ -259,8 +249,8 @@ ReactionInfo: | ------------- | ------ | -------------- | -------------- | | `post_type` | string | `notice` | 上报类型 | | `notice_type` | string | `channel_destroyed` | 消息类型 | -| `guild_id` | uint64 | | 频道ID | -| `channel_id` | uint64 | | 子频道ID | -| `user_id` | uint64 | | 操作者ID | -| `operator_id` | uint64 | | 操作者ID | +| `guild_id` | string | | 频道ID | +| `channel_id` | string | | 子频道ID | +| `user_id` | string | | 操作者ID | +| `operator_id` | string | | 操作者ID | | `channel_info` | ChannelInfo | | 频道信息 | \ No newline at end of file From 9c3997d11f003ec66e8060209fffc5634fb25a59 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 9 Dec 2021 00:55:50 +0800 Subject: [PATCH 169/612] doc: update guild doc --- docs/guild.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guild.md b/docs/guild.md index 1756f106a..a2392d751 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -30,7 +30,7 @@ API以及字段相关命名均为参考QQ官方命名或相似产品命名规则 - 所有事件的 `self_id` 均为 BOT 的QQ号. `tiny_id` 将放在 `self_tiny_id` 字段 - 遵循我们一贯的原则, 将不会支持主动加频道/主动拉人/红包相关消息类型 - 频道相关的API仅能在 `Android Phone` 和 `iPad` 协议上使用. -- 由于频道相关ID的数据类型均为 `uint64` , 为保证不超过某些语言的安全值范围, 所以ID相关数据均转换为 `string` 类型, API调用两种类型均可接受. +- 由于频道相关ID的数据类型均为 `uint64` , 为保证不超过某些语言的安全值范围, 在 `v1.0.0-beta8-fix3` 以后, 所有ID相关数据均转换为 `string` 类型, API调用两种类型均可接受. ## API From c40f1b8191c9c14569e12a895aa92125a2450481 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 9 Dec 2021 01:00:23 +0800 Subject: [PATCH 170/612] doc: fix typo --- docs/guild.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guild.md b/docs/guild.md index a2392d751..725a9a041 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -108,7 +108,7 @@ ChannelInfo: | `owner_guild_id` | string | 所属频道ID | | `channel_id` | string | 子频道ID | | `channel_type` | int32 | 子频道类型 | -| `channel_name` | string | 之频道名称 | +| `channel_name` | string | 子频道名称 | | `create_time` | int64 | 创建时间 | | `creator_tiny_id` | string | 创建者ID | | `talk_permission` | int32 | 发言权限类型 | From 0211a0ea96bb2deb2d70657871d3eacb2179d478 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 9 Dec 2021 16:28:43 +0800 Subject: [PATCH 171/612] doc: guild api get_guild_member_list --- docs/guild.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/guild.md b/docs/guild.md index 725a9a041..6d7a70dac 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -138,6 +138,8 @@ SlowModeInfo: 终结点: `/get_guild_member_list` +> 由于频道人数较多(数万), 请尽量不要全量拉取成员列表, 这将会导致严重的性能问题 + **参数** | 字段 | 类型 | 说明 | @@ -145,11 +147,27 @@ SlowModeInfo: | `guild_id` | string | 频道ID | | `next_token` | string | 翻页Token | -> `next_token` 为空的话即返回第一页数据 -> -> 由于频道人数太多(数万), 请尽量不要全量拉取成员列表, 这可能会导致严重的性能问题 +> `next_token` 为空的情况下, 将返回第一页的数据, 并在返回值附带下一页的 `token` + +**响应数据** +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `members` | []GuildMemberInfo | 成员列表 | +| `finished` | bool | 是否最终页 | +| `next_token` | string | 翻页Token | + +GuildMemberInfo: + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `tiny_id` | string | 成员ID | +| `title` | string | 成员头衔 | +| `nickname` | string | 成员昵称 | +| `role_id` | string | 所在权限组ID | +| `role_name` | string | 所在权限组名称 | +> 默认情况下频道管理员的权限组ID为 `2`, 部分频道可能会另行创建, 需手动判断 ### 发送信息到子频道 From 49aedc99fee88118b0cac904d7ab30e5aa799b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E8=91=89=E3=81=82=E3=82=93=E3=81=9A?= Date: Thu, 9 Dec 2021 17:53:40 +0800 Subject: [PATCH 172/612] fix(expand env): change os.ExpandEnv to regex (#1231) * fix(expand env): change os.ExpandEnv to regex * fix: MustCompile * fix: regex --- modules/config/config.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/config/config.go b/modules/config/config.go index 33930cc69..4dee91577 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -6,6 +6,7 @@ import ( _ "embed" // embed the default config file "fmt" "os" + "regexp" "strconv" "strings" "sync" @@ -96,7 +97,7 @@ func Parse(path string) *Config { file, err := os.ReadFile(path) config := &Config{} if err == nil { - err = yaml.NewDecoder(strings.NewReader(os.ExpandEnv(string(file)))).Decode(config) + err = yaml.NewDecoder(strings.NewReader(expand(string(file), os.Getenv))).Decode(config) if err != nil && !fromEnv { log.Fatal("配置文件不合法!", err) } @@ -182,3 +183,17 @@ func generateConfig() { fmt.Println("默认配置文件已生成,请修改 config.yml 后重新启动!") _, _ = input.ReadString('\n') } + +// expand 使用正则进行环境变量展开 +// os.ExpandEnv 字符 $ 无法逃逸 +// https://github.com/golang/go/issues/43482 +func expand(s string, mapping func(string) string) string { + r := regexp.MustCompile(`\${([a-zA-Z_]+[a-zA-Z0-9_]*)}`) + re := r.FindAllStringSubmatch(s, -1) + for _, i := range re { + if len(i) == 2 { + s = strings.ReplaceAll(s, i[0], mapping(i[1])) + } + } + return s +} From 5d81267c1280ef98030cde1f556865a12c6bc515 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 11 Dec 2021 02:57:17 +0800 Subject: [PATCH 173/612] feat: get_guild_member_profile api --- coolq/api.go | 29 ++++++++++++++++++++++++++++- coolq/converter.go | 1 - coolq/event.go | 4 ++-- docs/guild.md | 1 + go.mod | 2 +- go.sum | 4 ++-- modules/api/api.go | 4 ++++ 7 files changed, 38 insertions(+), 7 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 7836eca2e..02dca9547 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -187,6 +187,33 @@ func (bot *CQBot) CQGetGuildMembers(guildID uint64, nextToken string) global.MSG return OK(res) } +// CQGetGuildMemberProfile 获取频道成员资料 +// @route(get_guild_member_profile) +func (bot *CQBot) CQGetGuildMemberProfile(guildID, userID uint64) global.MSG { + if bot.Client.GuildService.FindGuild(guildID) == nil { + return Failed(100, "GUILD_NOT_FOUND") + } + profile, err := bot.Client.GuildService.FetchGuildMemberProfileInfo(guildID, userID) + if err != nil { + log.Errorf("获取频道 %v 成员 %v 资料时出现错误: %v", guildID, userID, err) + return Failed(100, "API_ERROR", err.Error()) + } + roles := make([]global.MSG, 0, len(profile.Roles)) + for _, role := range profile.Roles { + roles = append(roles, global.MSG{ + "role_id": fU64(role.RoleId), + "role_name": role.RoleName, + }) + } + return OK(global.MSG{ + "tiny_id": fU64(profile.TinyId), + "nickname": profile.Nickname, + "avatar_url": profile.AvatarUrl, + "join_time": profile.JoinTime, + "roles": roles, + }) +} + // CQGetGuildRoles 获取频道角色列表 // @route(get_guild_roles) func (bot *CQBot) CQGetGuildRoles(guildID uint64) global.MSG { @@ -725,7 +752,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R fixAt := func(elem []message.IMessageElement) { for _, e := range elem { if at, ok := e.(*message.AtElement); ok && at.Target != 0 && at.Display == "" { - mem, _ := bot.Client.GuildService.GetGuildMemberProfileInfo(guildID, uint64(at.Target)) + mem, _ := bot.Client.GuildService.FetchGuildMemberProfileInfo(guildID, uint64(at.Target)) if mem != nil { at.Display = "@" + mem.Nickname } else { diff --git a/coolq/converter.go b/coolq/converter.go index e0253843b..a62bce824 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -154,7 +154,6 @@ func convertChannelInfo(c *client.ChannelInfo) global.MSG { "channel_type": c.ChannelType, "channel_name": c.ChannelName, "owner_guild_id": fU64(c.Meta.GuildId), - "creator_id": c.Meta.CreatorUin, "creator_tiny_id": fU64(c.Meta.CreatorTinyId), "create_time": c.Meta.CreateTime, "current_slow_mode": c.Meta.CurrentSlowMode, diff --git a/coolq/event.go b/coolq/event.go index 82a6fe15f..9c9ec871e 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -239,7 +239,7 @@ func (bot *CQBot) guildChannelCreatedEvent(c *client.QQClient, e *client.GuildCh if guild == nil { return } - member, _ := c.GuildService.GetGuildMemberProfileInfo(e.GuildId, e.OperatorId) + member, _ := c.GuildService.FetchGuildMemberProfileInfo(e.GuildId, e.OperatorId) if member == nil { member = &client.GuildUserProfile{Nickname: "未知"} } @@ -263,7 +263,7 @@ func (bot *CQBot) guildChannelDestroyedEvent(c *client.QQClient, e *client.Guild if guild == nil { return } - member, _ := c.GuildService.GetGuildMemberProfileInfo(e.GuildId, e.OperatorId) + member, _ := c.GuildService.FetchGuildMemberProfileInfo(e.GuildId, e.OperatorId) if member == nil { member = &client.GuildUserProfile{Nickname: "未知"} } diff --git a/docs/guild.md b/docs/guild.md index 6d7a70dac..69c6f0d68 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -31,6 +31,7 @@ API以及字段相关命名均为参考QQ官方命名或相似产品命名规则 - 遵循我们一贯的原则, 将不会支持主动加频道/主动拉人/红包相关消息类型 - 频道相关的API仅能在 `Android Phone` 和 `iPad` 协议上使用. - 由于频道相关ID的数据类型均为 `uint64` , 为保证不超过某些语言的安全值范围, 在 `v1.0.0-beta8-fix3` 以后, 所有ID相关数据均转换为 `string` 类型, API调用两种类型均可接受. +- 为保证一致性, 所有频道接口返回的 `用户ID` 均命名为 `tiny_id`, 所有频道相关接口的 `用户ID` 入参均命名为 `user_id` ## API diff --git a/go.mod b/go.mod index ebcd4771b..d0a41dcbe 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211208132533-8cd25e02fcf3 + github.com/Mrs4s/MiraiGo v0.0.0-20211210183655-416a6c17bee3 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 15b2bbbdf..9b2b00536 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211208132533-8cd25e02fcf3 h1:w42Z7gE+HjBcjXDjVZ8o5rzNgAzt7nhuL84HOYpIhRE= -github.com/Mrs4s/MiraiGo v0.0.0-20211208132533-8cd25e02fcf3/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211210183655-416a6c17bee3 h1:IWZEA6yZOwXCEg5a4kuZkzfy3NghjsRtVIbJ4SBpzU0= +github.com/Mrs4s/MiraiGo v0.0.0-20211210183655-416a6c17bee3/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= diff --git a/modules/api/api.go b/modules/api/api.go index 490f2b115..0d4572942 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -144,6 +144,10 @@ func (c *Caller) call(action string, p Getter) global.MSG { p0 := p.Get("guild_id").Uint() p1 := p.Get("next_token").String() return c.bot.CQGetGuildMembers(p0, p1) + case "get_guild_member_profile": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("user_id").Uint() + return c.bot.CQGetGuildMemberProfile(p0, p1) case "get_guild_meta_by_guest": p0 := p.Get("guild_id").Uint() return c.bot.CQGetGuildMetaByGuest(p0) From 2461ff58a5dc4e1583750726d262a6e4c6b7206a Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 11 Dec 2021 03:10:53 +0800 Subject: [PATCH 174/612] doc: update guild doc --- docs/guild.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/guild.md b/docs/guild.md index 69c6f0d68..b3d2d1a84 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -30,7 +30,7 @@ API以及字段相关命名均为参考QQ官方命名或相似产品命名规则 - 所有事件的 `self_id` 均为 BOT 的QQ号. `tiny_id` 将放在 `self_tiny_id` 字段 - 遵循我们一贯的原则, 将不会支持主动加频道/主动拉人/红包相关消息类型 - 频道相关的API仅能在 `Android Phone` 和 `iPad` 协议上使用. -- 由于频道相关ID的数据类型均为 `uint64` , 为保证不超过某些语言的安全值范围, 在 `v1.0.0-beta8-fix3` 以后, 所有ID相关数据均转换为 `string` 类型, API调用两种类型均可接受. +- 由于频道相关ID的数据类型均为 `uint64` , 为保证不超过某些语言的安全值范围, 在 `v1.0.0-beta8-fix3` 以后, 所有ID相关数据将转换为 `string` 类型, API调用 `uint64` 或 `string` 均可接受. - 为保证一致性, 所有频道接口返回的 `用户ID` 均命名为 `tiny_id`, 所有频道相关接口的 `用户ID` 入参均命名为 `user_id` ## API @@ -140,6 +140,8 @@ SlowModeInfo: 终结点: `/get_guild_member_list` > 由于频道人数较多(数万), 请尽量不要全量拉取成员列表, 这将会导致严重的性能问题 +> +> 尽量使用 `get_guild_member_profile` 接口代替全量拉取 **参数** @@ -169,6 +171,36 @@ GuildMemberInfo: | `role_name` | string | 所在权限组名称 | > 默认情况下频道管理员的权限组ID为 `2`, 部分频道可能会另行创建, 需手动判断 +> +> 此接口仅展现最新的权限组, 获取用户加入的所有权限组请使用 `get_guild_member_profile` 接口 + +### 单独获取频道成员信息 + +终结点: `/get_guild_member_profile` + +**参数** + +| 字段 | 类型 | 说明 | +| ---------- | ----- | ---- | +| `guild_id` | string | 频道ID | +| `user_id` | string | 用户ID | + +**响应数据** + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `tiny_id` | string | 用户ID | +| `nickname` | string | 用户昵称 | +| `avatar_url` | string | 头像地址 | +| `join_time` | int64 | 加入时间 | +| `roles` | []RoleInfo | 加入的所有权限组 | + +RoleInfo: + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `role_id` | string | 权限组ID | +| `role_name` | string | 权限组名称 | ### 发送信息到子频道 From 57b2ce4c049cde092c3247c6ae7644333bb083af Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 11 Dec 2021 23:16:00 +0800 Subject: [PATCH 175/612] feat: database support for guild channel message --- coolq/bot.go | 29 +++++++++++++++++++++++++++++ db/database.go | 23 +++++++++++++++++++++++ db/leveldb/leveldb.go | 39 +++++++++++++++++++++++++++++++++++++-- db/mongodb/mongodb.go | 20 ++++++++++++++++++-- db/multidb.go | 9 +++++++++ 5 files changed, 116 insertions(+), 4 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 97dc448f6..78cfd6858 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/segmentio/asm/base64" "io" "os" "path" @@ -512,6 +513,34 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32 } */ +// InsertGuildChannelMessage 频道消息入数据库 +func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) string { + id := base64.StdEncoding.EncodeToString(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt64(m.GuildId) + w.WriteUInt64(m.ChannelId) + w.WriteUInt64(m.Id) + w.WriteUInt64(m.InternalId) + })) + msg := &db.StoredGuildChannelMessage{ + ID: id, + Attribute: &db.StoredGuildMessageAttribute{ + MessageSeq: m.Id, + InternalID: m.InternalId, + SenderTinyID: m.Sender.TinyId, + SenderName: m.Sender.Nickname, + Timestamp: m.Time, + }, + GuildID: m.GuildId, + ChannelID: m.ChannelId, + Content: ToMessageContent(m.Elements), + } + if err := db.InsertGuildChannelMessage(msg); err != nil { + log.Warnf("记录聊天数据时出现错误: %v", err) + return "" + } + return msg.ID +} + // Release 释放Bot实例 func (bot *CQBot) Release() { diff --git a/db/database.go b/db/database.go index 21445e299..6495f8236 100644 --- a/db/database.go +++ b/db/database.go @@ -19,11 +19,15 @@ type ( GetGroupMessageByGlobalID(int32) (*StoredGroupMessage, error) // GetPrivateMessageByGlobalID 通过 GlobalID 来获取私聊消息 GetPrivateMessageByGlobalID(int32) (*StoredPrivateMessage, error) + // GetGuildChannelMessageByID 通过 ID 来获取频道消息 + GetGuildChannelMessageByID(string) (*StoredGuildChannelMessage, error) // InsertGroupMessage 向数据库写入新的群消息 InsertGroupMessage(*StoredGroupMessage) error // InsertPrivateMessage 向数据库写入新的私聊消息 InsertPrivateMessage(*StoredPrivateMessage) error + // InsertGuildChannelMessage 向数据库写入新的频道消息 + InsertGuildChannelMessage(*StoredGuildChannelMessage) error } StoredMessage interface { @@ -58,6 +62,16 @@ type ( Content []global.MSG `bson:"content"` } + // StoredGuildChannelMessage 持久化频道消息 + StoredGuildChannelMessage struct { + ID string `bson:"_id"` + Attribute *StoredGuildMessageAttribute `bson:"attribute"` + GuildID uint64 `bson:"guildId"` + ChannelID uint64 `bson:"channelId"` + QuotedInfo *QuotedInfo `bson:"quotedInfo"` + Content []global.MSG `bson:"content"` + } + // StoredMessageAttribute 持久化消息属性 StoredMessageAttribute struct { MessageSeq int32 `bson:"messageSeq"` @@ -67,6 +81,15 @@ type ( Timestamp int64 `bson:"timestamp"` } + // StoredGuildMessageAttribute 持久化频道消息属性 + StoredGuildMessageAttribute struct { + MessageSeq uint64 `bson:"messageSeq"` + InternalID uint64 `bson:"internalId"` + SenderTinyID uint64 `bson:"senderTinyId"` + SenderName string `bson:"senderName"` + Timestamp int64 `bson:"timestamp"` + } + // QuotedInfo 引用回复 QuotedInfo struct { PrevID string `bson:"prevId"` diff --git a/db/leveldb/leveldb.go b/db/leveldb/leveldb.go index 3e787e412..9955c1f88 100644 --- a/db/leveldb/leveldb.go +++ b/db/leveldb/leveldb.go @@ -3,6 +3,7 @@ package leveldb import ( "bytes" "encoding/gob" + "github.com/Mrs4s/MiraiGo/utils" "path" "github.com/Mrs4s/MiraiGo/binary" @@ -21,16 +22,19 @@ type LevelDBImpl struct { } const ( - group byte = 0x0 - private byte = 0x1 + group byte = 0x0 + private byte = 0x1 + guildChannel byte = 0x2 ) func init() { gob.Register(db.StoredMessageAttribute{}) + gob.Register(db.StoredGuildMessageAttribute{}) gob.Register(db.QuotedInfo{}) gob.Register(global.MSG{}) gob.Register(db.StoredGroupMessage{}) gob.Register(db.StoredPrivateMessage{}) + gob.Register(db.StoredGuildChannelMessage{}) db.Register("leveldb", func(node yaml.Node) db.Database { conf := new(config.LevelDBConfig) @@ -102,6 +106,24 @@ func (ldb *LevelDBImpl) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivate return p, nil } +func (ldb *LevelDBImpl) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { + v, err := ldb.db.Get([]byte(id), nil) + if err != nil { + return nil, errors.Wrap(err, "get value error") + } + r := binary.NewReader(v) + switch r.ReadByte() { + case guildChannel: + g := &db.StoredGuildChannelMessage{} + if err = gob.NewDecoder(bytes.NewReader(r.ReadAvailable())).Decode(g); err != nil { + return nil, errors.Wrap(err, "decode message error") + } + return g, nil + default: + return nil, errors.New("unknown message flag") + } +} + func (ldb *LevelDBImpl) InsertGroupMessage(msg *db.StoredGroupMessage) error { buf := global.NewBuffer() defer global.PutBuffer(buf) @@ -127,3 +149,16 @@ func (ldb *LevelDBImpl) InsertPrivateMessage(msg *db.StoredPrivateMessage) error }), nil) return errors.Wrap(err, "put data error") } + +func (ldb *LevelDBImpl) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) error { + buf := global.NewBuffer() + defer global.PutBuffer(buf) + if err := gob.NewEncoder(buf).Encode(msg); err != nil { + return errors.Wrap(err, "encode message error") + } + err := ldb.db.Put(utils.S2B(msg.ID), binary.NewWriterF(func(w *binary.Writer) { + w.WriteByte(guildChannel) + w.Write(buf.Bytes()) + }), nil) + return errors.Wrap(err, "put data error") +} diff --git a/db/mongodb/mongodb.go b/db/mongodb/mongodb.go index 6387d5a33..952874346 100644 --- a/db/mongodb/mongodb.go +++ b/db/mongodb/mongodb.go @@ -20,8 +20,9 @@ type MongoDBImpl struct { } const ( - MongoGroupMessageCollection = "group-messages" - MongoPrivateMessageCollection = "private-messages" + MongoGroupMessageCollection = "group-messages" + MongoPrivateMessageCollection = "private-messages" + MongoGuildChannelMessageCollection = "guild-channel-messages" ) func init() { @@ -72,6 +73,15 @@ func (m *MongoDBImpl) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMe return &ret, nil } +func (m *MongoDBImpl) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { + coll := m.mongo.Collection(MongoGuildChannelMessageCollection) + var ret db.StoredGuildChannelMessage + if err := coll.FindOne(context.Background(), bson.D{{"_id", id}}).Decode(&ret); err != nil { + return nil, errors.Wrap(err, "query error") + } + return &ret, nil +} + func (m *MongoDBImpl) InsertGroupMessage(msg *db.StoredGroupMessage) error { coll := m.mongo.Collection(MongoGroupMessageCollection) _, err := coll.InsertOne(context.Background(), msg) @@ -83,3 +93,9 @@ func (m *MongoDBImpl) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { _, err := coll.InsertOne(context.Background(), msg) return errors.Wrap(err, "insert error") } + +func (m *MongoDBImpl) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) error { + coll := m.mongo.Collection(MongoGuildChannelMessageCollection) + _, err := coll.InsertOne(context.Background(), msg) + return errors.Wrap(err, "insert error") +} diff --git a/db/multidb.go b/db/multidb.go index 741ebf7ba..83c086f2d 100644 --- a/db/multidb.go +++ b/db/multidb.go @@ -86,3 +86,12 @@ func InsertPrivateMessage(m *StoredPrivateMessage) error { } return nil } + +func InsertGuildChannelMessage(m *StoredGuildChannelMessage) error { + for _, b := range backends { + if err := b.InsertGuildChannelMessage(m); err != nil { + return errors.Wrap(err, "insert message to backend error") + } + } + return nil +} From 54e69acb8bb192c6f4cfd2671ae514f4d51b1cd6 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 11 Dec 2021 23:35:49 +0800 Subject: [PATCH 176/612] feat: support guild image decode to db --- coolq/cqcode.go | 5 +++++ coolq/event.go | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 7315cde6d..c14203637 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -448,6 +448,11 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { "type": "image", "data": data, } + case *message.GuildImageElement: + m = global.MSG{ + "type": "image", + "data": global.MSG{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url}, + } case *message.FriendImageElement: data := global.MSG{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url} if o.Flash { diff --git a/coolq/event.go b/coolq/event.go index 9c9ec871e..6b1fba114 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -155,14 +155,14 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC SubID: m.ChannelId, } log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, ToStringMessage(m.Elements, source, true)) - // todo: 数据库支持 + id := bot.InsertGuildChannelMessage(m) bot.dispatchEventMessage(global.MSG{ "post_type": "message", "message_type": "guild", "sub_type": "channel", "guild_id": fU64(m.GuildId), "channel_id": fU64(m.ChannelId), - "message_id": fmt.Sprintf("%v-%v", m.Id, m.InternalId), + "message_id": id, "user_id": fU64(m.Sender.TinyId), "message": ToFormattedMessage(m.Elements, source, false), // todo: 增加对频道消息 Reply 的支持 "self_id": bot.Client.Uin, From abf42ce6b13c73abd6d014b0e9e931004e4ddc3e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:36:58 +0000 Subject: [PATCH 177/612] ci(chore): Fix stylings --- coolq/bot.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index 78cfd6858..cb0aa89ea 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/segmentio/asm/base64" "io" "os" "path" @@ -13,6 +12,8 @@ import ( "sync" "time" + "github.com/segmentio/asm/base64" + "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/MiraiGo/binary" From 120b925fd6da14dd1687a29be4b6a21469da9d0c Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 12 Dec 2021 00:24:24 +0800 Subject: [PATCH 178/612] update: remove internal id from guild message id --- coolq/bot.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index 78cfd6858..642172517 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -519,7 +519,6 @@ func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) stri w.WriteUInt64(m.GuildId) w.WriteUInt64(m.ChannelId) w.WriteUInt64(m.Id) - w.WriteUInt64(m.InternalId) })) msg := &db.StoredGuildChannelMessage{ ID: id, From 1bc381851099e7b2c4cbb2592a8c94ee8f94d5f4 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 12 Dec 2021 17:15:42 +0800 Subject: [PATCH 179/612] feat: basic guild music share support --- coolq/bot.go | 12 +++++++----- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 32409c8d4..8c04ee0dd 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -12,17 +12,15 @@ import ( "sync" "time" - "github.com/segmentio/asm/base64" - - "github.com/Mrs4s/go-cqhttp/db" - "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" "github.com/pkg/errors" + "github.com/segmentio/asm/base64" log "github.com/sirupsen/logrus" + "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" ) @@ -377,7 +375,11 @@ func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message. } e = n - case *LocalVoiceElement, *PokeElement, *message.MusicShareElement: + case *message.MusicShareElement: + bot.Client.SendGuildMusicShare(guildID, channelID, i) + return "-1" // todo: fix this + + case *LocalVoiceElement, *PokeElement: log.Warnf("警告: 频道暂不支持发送 %v 消息", i.Type().String()) continue } diff --git a/go.mod b/go.mod index d0a41dcbe..2b592cb89 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211210183655-416a6c17bee3 + github.com/Mrs4s/MiraiGo v0.0.0-20211212091247-abe1fe7770a2 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 9b2b00536..2d132e323 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211210183655-416a6c17bee3 h1:IWZEA6yZOwXCEg5a4kuZkzfy3NghjsRtVIbJ4SBpzU0= -github.com/Mrs4s/MiraiGo v0.0.0-20211210183655-416a6c17bee3/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211212091247-abe1fe7770a2 h1:4qcfWsSbVDRBsNSpnvywWZhIrv5rXNJD9OBksuIi4FQ= +github.com/Mrs4s/MiraiGo v0.0.0-20211212091247-abe1fe7770a2/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= From f5ef0c188b9278850f30954dfe72b6660d97d458 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 13 Dec 2021 02:21:20 +0800 Subject: [PATCH 180/612] feat: guild_channel_recall event --- coolq/bot.go | 14 +++++++++----- coolq/event.go | 35 +++++++++++++++++++++++++++++++++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 8c04ee0dd..cbf9f691c 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -518,11 +518,7 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32 // InsertGuildChannelMessage 频道消息入数据库 func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) string { - id := base64.StdEncoding.EncodeToString(binary.NewWriterF(func(w *binary.Writer) { - w.WriteUInt64(m.GuildId) - w.WriteUInt64(m.ChannelId) - w.WriteUInt64(m.Id) - })) + id := encodeGuildMessageID(m.GuildId, m.ChannelId, m.Id) msg := &db.StoredGuildChannelMessage{ ID: id, Attribute: &db.StoredGuildMessageAttribute{ @@ -612,3 +608,11 @@ func encodeMessageID(target int64, seq int32) string { w.WriteUInt32(uint32(seq)) })) } + +func encodeGuildMessageID(guildID, channelID, seq uint64) string { + return base64.StdEncoding.EncodeToString(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt64(guildID) + w.WriteUInt64(channelID) + w.WriteUInt64(seq) + })) +} diff --git a/coolq/event.go b/coolq/event.go index 6b1fba114..450a5c4db 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -180,7 +180,8 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien if guild == nil { return } - str := fmt.Sprintf("频道 %v(%v) 消息 %v 表情贴片已更新: ", guild.GuildName, guild.GuildId, e.MessageId) + msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId) + str := fmt.Sprintf("频道 %v(%v) 消息 %v 表情贴片已更新: ", guild.GuildName, guild.GuildId, msgID) currentReactions := make([]global.MSG, len(e.CurrentReactions)) for i, r := range e.CurrentReactions { str += fmt.Sprintf("%v*%v ", r.Face.Name, r.Count) @@ -203,7 +204,7 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien "message_sender_uin": e.MessageSenderUin, "guild_id": fU64(e.GuildId), "channel_id": fU64(e.ChannelId), - "message_id": fmt.Sprint(e.MessageId), // todo: 支持数据库后转换为数据库id + "message_id": msgID, "operator_id": fU64(e.OperatorId), "current_reactions": currentReactions, "time": time.Now().Unix(), @@ -213,6 +214,36 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien }) } +func (bot *CQBot) guildChannelMessageRecalledEvent(c *client.QQClient, e *client.GuildMessageRecalledEvent) { + guild := c.GuildService.FindGuild(e.GuildId) + if guild == nil { + return + } + channel := guild.FindChannel(e.ChannelId) + if channel == nil { + return + } + operator, err := c.GuildService.FetchGuildMemberProfileInfo(e.GuildId, e.OperatorId) + if err != nil { + log.Errorf("处理频道撤回事件时出现错误: 获取操作者资料时出现错误 %v", err) + return + } + msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId) + log.Infof("用户 %v(%v) 撤回了频道 %v(%v) 子频道 %v(%v) 的消息 %v", operator.Nickname, operator.TinyId, guild.GuildName, guild.GuildId, channel.ChannelName, channel.ChannelId, msgID) + bot.dispatchEventMessage(global.MSG{ + "post_type": "notice", + "notice_type": "guild_channel_recall", + "guild_id": fU64(e.GuildId), + "channel_id": fU64(e.ChannelId), + "operator_id": fU64(e.OperatorId), + "message_id": msgID, + "time": time.Now().Unix(), + "self_id": bot.Client.Uin, + "self_tiny_id": fU64(bot.Client.GuildService.TinyId), + "user_id": e.OperatorId, + }) +} + func (bot *CQBot) guildChannelUpdatedEvent(c *client.QQClient, e *client.GuildChannelUpdatedEvent) { guild := c.GuildService.FindGuild(e.GuildId) if guild == nil { diff --git a/go.mod b/go.mod index 2b592cb89..9b6cba807 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211212091247-abe1fe7770a2 + github.com/Mrs4s/MiraiGo v0.0.0-20211212174229-5d7cb85b1ed4 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 2d132e323..c31c2892e 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211212091247-abe1fe7770a2 h1:4qcfWsSbVDRBsNSpnvywWZhIrv5rXNJD9OBksuIi4FQ= -github.com/Mrs4s/MiraiGo v0.0.0-20211212091247-abe1fe7770a2/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211212174229-5d7cb85b1ed4 h1:rtUcMuZ8aMspm7Ahgw6wVXHAT+mmHU8Fe0hfmKMxks0= +github.com/Mrs4s/MiraiGo v0.0.0-20211212174229-5d7cb85b1ed4/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= From 32aa82f9147929d0ef879d4c059b9629b69a0b02 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 13 Dec 2021 02:34:05 +0800 Subject: [PATCH 181/612] feat: get_guild_msg api --- coolq/api.go | 24 ++++++++++++++++++++++++ coolq/bot.go | 1 + db/multidb.go | 7 +++++++ modules/api/api.go | 3 +++ 4 files changed, 35 insertions(+) diff --git a/coolq/api.go b/coolq/api.go index 02dca9547..827b90ea4 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1564,6 +1564,30 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { return OK(m) } +// CQGetGuildMessage 获取频道消息 +// @route(get_guild_msg) +func (bot *CQBot) CQGetGuildMessage(messageID string) global.MSG { + msg, err := db.GetGuildChannelMessageByID(messageID) + if err != nil { + log.Warnf("获取消息时出现错误: %v", err) + return Failed(100, "MSG_NOT_FOUND", "消息不存在") + } + m := global.MSG{ + "message_id": msg.ID, + "message_source": "channel", + "guild_id": fU64(msg.GuildID), + "channel_id": fU64(msg.ChannelID), + "message_seq": msg.Attribute.MessageSeq, + "sender": global.MSG{ + "user_id": fU64(msg.Attribute.SenderTinyID), + "nickname": msg.Attribute.SenderName, + }, + "time": msg.Attribute.Timestamp, + "message": ToFormattedMessage(bot.ConvertContentMessage(msg.Content, MessageSourceGuildChannel), MessageSource{SourceType: MessageSourceGuildChannel, PrimaryID: msg.GuildID, SubID: msg.ChannelID}), + } + return OK(m) +} + // CQGetGroupSystemMessages 扩展API-获取群文件系统消息 // // https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E7%B3%BB%E7%BB%9F%E6%B6%88%E6%81%AF diff --git a/coolq/bot.go b/coolq/bot.go index cbf9f691c..ce9d8090c 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -80,6 +80,7 @@ func NewQQBot(cli *client.QQClient) *CQBot { bot.Client.OnTempMessage(bot.tempMessageEvent) bot.Client.GuildService.OnGuildChannelMessage(bot.guildChannelMessageEvent) bot.Client.GuildService.OnGuildMessageReactionsUpdated(bot.guildMessageReactionsUpdatedEvent) + bot.Client.GuildService.OnGuildMessageRecalled(bot.guildChannelMessageRecalledEvent) bot.Client.GuildService.OnGuildChannelUpdated(bot.guildChannelUpdatedEvent) bot.Client.GuildService.OnGuildChannelCreated(bot.guildChannelCreatedEvent) bot.Client.GuildService.OnGuildChannelDestroyed(bot.guildChannelDestroyedEvent) diff --git a/db/multidb.go b/db/multidb.go index 83c086f2d..6d3825ce6 100644 --- a/db/multidb.go +++ b/db/multidb.go @@ -69,6 +69,13 @@ func GetPrivateMessageByGlobalID(id int32) (*StoredPrivateMessage, error) { return backends[0].GetPrivateMessageByGlobalID(id) } +func GetGuildChannelMessageByID(id string) (*StoredGuildChannelMessage, error) { + if len(backends) == 0 { + return nil, DatabaseDisabledError + } + return backends[0].GetGuildChannelMessageByID(id) +} + func InsertGroupMessage(m *StoredGroupMessage) error { for _, b := range backends { if err := b.InsertGroupMessage(m); err != nil { diff --git a/modules/api/api.go b/modules/api/api.go index 0d4572942..8ba0cecf0 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -151,6 +151,9 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "get_guild_meta_by_guest": p0 := p.Get("guild_id").Uint() return c.bot.CQGetGuildMetaByGuest(p0) + case "get_guild_msg": + p0 := p.Get("message_id").String() + return c.bot.CQGetGuildMessage(p0) case "get_guild_roles": p0 := p.Get("guild_id").Uint() return c.bot.CQGetGuildRoles(p0) From 8f37c5f1476074c6e4aa5515193fadf2e0c7edfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 17:35:37 +0800 Subject: [PATCH 182/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E9=9D=92?= =?UTF-8?q?=E9=BE=99=E7=9B=B8=E5=85=B3=E7=9A=84=E4=B8=80=E4=BA=9B=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/cmd/log.go | 9 ++ qqrobot/qinglong/cmd/main.go | 21 +++++ qqrobot/qinglong/env.go | 158 +++++++++++++++++++++++++++++++++++ qqrobot/qinglong/ql.go | 18 ++++ qqrobot/qinglong/query.go | 40 +++++++++ 5 files changed, 246 insertions(+) create mode 100644 qqrobot/qinglong/cmd/log.go create mode 100644 qqrobot/qinglong/cmd/main.go create mode 100644 qqrobot/qinglong/env.go create mode 100644 qqrobot/qinglong/ql.go create mode 100644 qqrobot/qinglong/query.go diff --git a/qqrobot/qinglong/cmd/log.go b/qqrobot/qinglong/cmd/log.go new file mode 100644 index 000000000..96343c76e --- /dev/null +++ b/qqrobot/qinglong/cmd/log.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/fzls/logger" +) + +func initLogger() { + logger.InitLogger("logs", "qinglong", "info") +} diff --git a/qqrobot/qinglong/cmd/main.go b/qqrobot/qinglong/cmd/main.go new file mode 100644 index 000000000..f4c84c848 --- /dev/null +++ b/qqrobot/qinglong/cmd/main.go @@ -0,0 +1,21 @@ +package main + +import ( + logger "github.com/sirupsen/logrus" + + "github.com/Mrs4s/go-cqhttp/qqrobot/qinglong" +) + +func main() { + initLogger() + + envInfos, _ := qinglong.ParseJdCookie() + logger.Infof("my: %v", envInfos["jd_70a2bcede031c"]) + + var info *qinglong.JdCookieInfo + + for _, param := range []string{"", "jd_70a2bcede031c", "1", "风之凌殇"} { + info = qinglong.QueryCookieInfo(param) + logger.Infof("%v: %v", param, info) + } +} diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go new file mode 100644 index 000000000..cf9ce79f2 --- /dev/null +++ b/qqrobot/qinglong/env.go @@ -0,0 +1,158 @@ +package qinglong + +import ( + "bufio" + "encoding/json" + "os" + "strings" +) + +// EnvDBEntry env.db中记录格式 +type EnvDBEntry struct { + Value string `json:"value"` + ID string `json:"_id"` + Created int64 `json:"created"` + Status int `json:"status"` + Timestamp string `json:"timestamp"` + Position float64 `json:"position"` + Name string `json:"name"` + Remarks string `json:"remarks"` +} + +// JdCookieInfo 所需的京东cookie信息 +type JdCookieInfo struct { + Index int // 在env.sh中的JD_COOKIE中的顺序,从1开始计数 + PtPin string // pt_pin + PtKey string // pt_key + Remark string // remark +} + +// ParseJdCookie 解析 db/env.db 和 config/env.sh,获取各个京东cookie的序号和备注信息 +func ParseJdCookie() (map[string]*JdCookieInfo, error) { + ptPinToCookieInfo, err := parseEnvDB() + if err != nil { + return nil, err + } + + ptPinToIndex, err := parseEnvSh() + if err != nil { + return nil, err + } + for ptPin, index := range ptPinToIndex { + cookieInfo := ptPinToCookieInfo[ptPin] + if cookieInfo != nil { + cookieInfo.Index = index + } + } + + return ptPinToCookieInfo, nil +} + +func parseEnvDB() (map[string]*JdCookieInfo, error) { + envDBPath := getPath("db/env.db") + + envDBFile, err := os.Open(envDBPath) + if err != nil { + return nil, err + } + defer envDBFile.Close() + + ptPinToCookieInfo := make(map[string]*JdCookieInfo) + + scanner := bufio.NewScanner(envDBFile) + for scanner.Scan() { + var envEntry EnvDBEntry + err := json.Unmarshal(scanner.Bytes(), &envEntry) + if err != nil { + return nil, err + } + + if envEntry.Name != "JD_COOKIE" { + continue + } + + ptPin := getPtPin(envEntry.Value) + ptKey := getPtKey(envEntry.Value) + remark := getRemark(envEntry.Remarks) + + ptPinToCookieInfo[ptPin] = &JdCookieInfo{ + Index: -1, + PtPin: ptPin, + PtKey: ptKey, + Remark: remark, + } + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return ptPinToCookieInfo, nil +} + +func parseEnvSh() (map[string]int, error) { + envShPath := getPath("config/env.sh") + + envShFile, err := os.Open(envShPath) + if err != nil { + return nil, err + } + defer envShFile.Close() + + ptPinToIndex := make(map[string]int) + + scanner := bufio.NewScanner(envShFile) + for scanner.Scan() { + // export ENV_VAR="XXXX" + envDefineStatement := scanner.Text() + if !strings.HasPrefix(envDefineStatement, `export JD_COOKIE="`) { + continue + } + + cookieStrings := envDefineStatement[len(`export JD_COOKIE="`) : len(envDefineStatement)-1] + for index, cookie := range strings.Split(cookieStrings, "&") { + ptPin := getPtPin(cookie) + ptPinToIndex[ptPin] = index + 1 + } + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return ptPinToIndex, nil +} + +// 以下函数针对的cookie形如 pt_key=XXXX;pt_pin=YYYYYY; + +func getPtPin(cookie string) string { + return getCookie(cookie, "pt_pin") +} + +func getPtKey(cookie string) string { + return getCookie(cookie, "pt_key") +} + +func getCookie(cookie string, cookieKey string) string { + for _, kv := range strings.Split(cookie, ";") { + kvArr := strings.SplitN(kv, "=", 2) + if len(kvArr) != 2 { + continue + } + if kvArr[0] == cookieKey { + return kvArr[1] + } + } + + return "" +} + +func getRemark(remarks string) string { + if strings.HasPrefix(remarks, "remark=") { + // remark=备注; + remarks = strings.TrimPrefix(remarks, "remark=") + remarks = strings.TrimSuffix(remarks, ";") + } + + return remarks +} diff --git a/qqrobot/qinglong/ql.go b/qqrobot/qinglong/ql.go new file mode 100644 index 000000000..fc017939d --- /dev/null +++ b/qqrobot/qinglong/ql.go @@ -0,0 +1,18 @@ +// Package qinglong 封装青龙相关逻辑 +package qinglong + +import ( + "os" +) + +func getQlDir() string { + qlEnv := os.Getenv("QL_DIR") + if qlEnv != "" { + return qlEnv + } + return "/qinglong/data" +} + +func getPath(path string) string { + return getQlDir() + "/" + path +} diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go new file mode 100644 index 000000000..7c6654c1e --- /dev/null +++ b/qqrobot/qinglong/query.go @@ -0,0 +1,40 @@ +package qinglong + +import ( + "strconv" + "strings" +) + +// QueryCookieInfo 尝试通过pt_pin/序号/昵称等来查询cookie信息 +func QueryCookieInfo(param string) *JdCookieInfo { + if param == "" { + return nil + } + + ptPinToCookieInfo, err := ParseJdCookie() + if err != nil { + return nil + } + // 尝试pt_pin + if info, ok := ptPinToCookieInfo[param]; ok { + return info + } + + // 尝试序号 + index, _ := strconv.ParseInt(param, 10, 64) + for _, info := range ptPinToCookieInfo { + if info.Index == int(index) { + return info + } + } + + // 尝试昵称 + for _, info := range ptPinToCookieInfo { + remark := getRemark(info.Remark) + if strings.Contains(remark, param) { + return info + } + } + + return nil +} From 519908dd68ca90523c3971e6e0a82483b1b61371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 17:55:18 +0800 Subject: [PATCH 183/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E8=B4=A6=E5=8F=B7=E5=AF=B9=E5=BA=94=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E5=9B=BE=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/cmd/main.go | 5 ++++- qqrobot/qinglong/query.go | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/qqrobot/qinglong/cmd/main.go b/qqrobot/qinglong/cmd/main.go index f4c84c848..88a873d9b 100644 --- a/qqrobot/qinglong/cmd/main.go +++ b/qqrobot/qinglong/cmd/main.go @@ -16,6 +16,9 @@ func main() { for _, param := range []string{"", "jd_70a2bcede031c", "1", "风之凌殇"} { info = qinglong.QueryCookieInfo(param) - logger.Infof("%v: %v", param, info) + chart := qinglong.QueryChartPath(info) + + // logger.Infof("%v: %v", param, info) + logger.Infof("%v: %v", param, chart) } } diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 7c6654c1e..28c965323 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -1,6 +1,8 @@ package qinglong import ( + "fmt" + "path/filepath" "strconv" "strings" ) @@ -38,3 +40,18 @@ func QueryCookieInfo(param string) *JdCookieInfo { return nil } + +// QueryChartPath 查询账号对应的统计图的路径 +func QueryChartPath(info *JdCookieInfo) string { + if info == nil { + return "" + } + + imageDir := getPath("log/.bean_chart") + path, err := filepath.Abs(fmt.Sprintf("%s/chart_%v.jpeg", imageDir, info.Index)) + if err != nil { + return "" + } + + return path +} From c8dd418cbd22e0ba0520f5edce9f258f4b9f5289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 19:51:07 +0800 Subject: [PATCH 184/612] =?UTF-8?q?feat:=20=E4=B8=8D=E9=9C=80=E8=A6=81?= =?UTF-8?q?=E8=A7=A3=E6=9E=90pt=5Fkey=EF=BC=8C=E5=90=8C=E6=97=B6=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=A4=84=E7=90=86=E4=BB=85env.sh=E4=B8=AD=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E7=9A=84=E8=B4=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index cf9ce79f2..0d83375f3 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -23,7 +23,6 @@ type EnvDBEntry struct { type JdCookieInfo struct { Index int // 在env.sh中的JD_COOKIE中的顺序,从1开始计数 PtPin string // pt_pin - PtKey string // pt_key Remark string // remark } @@ -42,6 +41,13 @@ func ParseJdCookie() (map[string]*JdCookieInfo, error) { cookieInfo := ptPinToCookieInfo[ptPin] if cookieInfo != nil { cookieInfo.Index = index + } else { + // 部分账号可能在env.db中不存在,但是env.sh中有 + ptPinToCookieInfo[ptPin] = &JdCookieInfo{ + Index: index, + PtPin: ptPin, + Remark: ptPin, + } } } @@ -72,13 +78,11 @@ func parseEnvDB() (map[string]*JdCookieInfo, error) { } ptPin := getPtPin(envEntry.Value) - ptKey := getPtKey(envEntry.Value) remark := getRemark(envEntry.Remarks) ptPinToCookieInfo[ptPin] = &JdCookieInfo{ Index: -1, PtPin: ptPin, - PtKey: ptKey, Remark: remark, } } @@ -129,10 +133,6 @@ func getPtPin(cookie string) string { return getCookie(cookie, "pt_pin") } -func getPtKey(cookie string) string { - return getCookie(cookie, "pt_key") -} - func getCookie(cookie string, cookieKey string) string { for _, kv := range strings.Split(cookie, ";") { kvArr := strings.SplitN(kv, "=", 2) From e92d27c7244a76d3862b2e901465a564e6be1670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 19:52:16 +0800 Subject: [PATCH 185/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=9C=80=E6=96=B0=E7=BB=9F=E8=AE=A1=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/cmd/main.go | 14 ++++++++------ qqrobot/qinglong/query.go | 33 +++++++++++++++++++++++++++++++++ qqrobot/qinglong/summary.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 qqrobot/qinglong/summary.go diff --git a/qqrobot/qinglong/cmd/main.go b/qqrobot/qinglong/cmd/main.go index 88a873d9b..f4426acd7 100644 --- a/qqrobot/qinglong/cmd/main.go +++ b/qqrobot/qinglong/cmd/main.go @@ -9,16 +9,18 @@ import ( func main() { initLogger() - envInfos, _ := qinglong.ParseJdCookie() - logger.Infof("my: %v", envInfos["jd_70a2bcede031c"]) + envInfos, err := qinglong.ParseJdCookie() + logger.Infof("my: %v %v", envInfos["jd_70a2bcede031c"], err) var info *qinglong.JdCookieInfo for _, param := range []string{"", "jd_70a2bcede031c", "1", "风之凌殇"} { info = qinglong.QueryCookieInfo(param) - chart := qinglong.QueryChartPath(info) - - // logger.Infof("%v: %v", param, info) - logger.Infof("%v: %v", param, chart) + logger.Infof("%v: %v", param, info) } + + chart := qinglong.QueryChartPath(info) + summary := qinglong.QuerySummary(info) + logger.Info(chart) + logger.Info(summary) } diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 28c965323..dbb078aad 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -2,9 +2,13 @@ package qinglong import ( "fmt" + "io/ioutil" "path/filepath" + "sort" "strconv" "strings" + + logger "github.com/sirupsen/logrus" ) // QueryCookieInfo 尝试通过pt_pin/序号/昵称等来查询cookie信息 @@ -55,3 +59,32 @@ func QueryChartPath(info *JdCookieInfo) string { return path } + +// QuerySummary 查询账号对应的最新统计信息 +func QuerySummary(info *JdCookieInfo) string { + if info == nil { + return "" + } + + summaryDir := getPath("log/shufflewzc_faker2_jd_bean_change") + logFiles, err := ioutil.ReadDir(summaryDir) + if err != nil { + logger.Errorf("read log dir failed, err=%v", err) + return "" + } + + // 按时间逆序排列 + sort.Slice(logFiles, func(i, j int) bool { + return logFiles[i].Name() > logFiles[j].Name() + }) + + // 因为有可能最新的日志还在处理中,因此逆序搜索每一个日志,直到搜索到为止 + for _, logFile := range logFiles { + summary := parseSummary(info, filepath.Join(summaryDir, logFile.Name())) + if summary != "" { + return summary + } + } + + return "" +} diff --git a/qqrobot/qinglong/summary.go b/qqrobot/qinglong/summary.go new file mode 100644 index 000000000..7a05e73bc --- /dev/null +++ b/qqrobot/qinglong/summary.go @@ -0,0 +1,34 @@ +package qinglong + +import ( + "fmt" + "io/ioutil" + "strings" + + "github.com/Mrs4s/go-cqhttp/global" +) + +func parseSummary(info *JdCookieInfo, logFilePath string) string { + if !global.PathExists(logFilePath) { + return "" + } + + contentBytes, err := ioutil.ReadFile(logFilePath) + if err != nil { + return "" + } + content := string(contentBytes) + + prefix := fmt.Sprintf("【账号%v🆔】", info.Index) + suffix := "\n\n" + + prefixIndex := strings.Index(content, prefix) + if prefixIndex == -1 { + return "" + } + suffixIndex := prefixIndex + strings.Index(content[prefixIndex:], suffix) + + summary := content[prefixIndex:suffixIndex] + + return summary +} From fd462efaf0b4bbc6104f4137b2eda50017ad6a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 20:00:49 +0800 Subject: [PATCH 186/612] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/cmd/main.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qqrobot/qinglong/cmd/main.go b/qqrobot/qinglong/cmd/main.go index f4426acd7..eb5499a6a 100644 --- a/qqrobot/qinglong/cmd/main.go +++ b/qqrobot/qinglong/cmd/main.go @@ -9,11 +9,14 @@ import ( func main() { initLogger() + logger.Infof("解析cookie") envInfos, err := qinglong.ParseJdCookie() logger.Infof("my: %v %v", envInfos["jd_70a2bcede031c"], err) var info *qinglong.JdCookieInfo + logger.Infof("") + logger.Infof("使用不同方式查询cookie信息") for _, param := range []string{"", "jd_70a2bcede031c", "1", "风之凌殇"} { info = qinglong.QueryCookieInfo(param) logger.Infof("%v: %v", param, info) @@ -21,6 +24,8 @@ func main() { chart := qinglong.QueryChartPath(info) summary := qinglong.QuerySummary(info) - logger.Info(chart) - logger.Info(summary) + logger.Infof("") + logger.Infof("解析的统计信息如下") + logger.Infof("chart: %v", chart) + logger.Infof("summary\n%v", summary) } From 6c3c882873a514d6e275ca6ea78814c57c942aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 20:51:39 +0800 Subject: [PATCH 187/612] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=85=E9=9D=92?= =?UTF-8?q?=E9=BE=99=E6=A8=A1=E5=9D=97=E7=9B=B8=E5=85=B3=E7=9A=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 2 +- qqrobot/qinglong/env_test.go | 74 +++++++++++++++++++ qqrobot/qinglong/ql.go | 4 +- qqrobot/qinglong/ql_test.go | 22 ++++++ qqrobot/qinglong/query_test.go | 53 +++++++++++++ qqrobot/qinglong/summary_test.go | 19 +++++ qqrobot/qinglong/test_data/config/env.sh | 1 + qqrobot/qinglong/test_data/db/env.db | 2 + .../2021-12-13-09-30-00.log | 52 +++++++++++++ qqrobot/qinglong/test_init.go | 9 +++ 10 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 qqrobot/qinglong/env_test.go create mode 100644 qqrobot/qinglong/ql_test.go create mode 100644 qqrobot/qinglong/query_test.go create mode 100644 qqrobot/qinglong/summary_test.go create mode 100644 qqrobot/qinglong/test_data/config/env.sh create mode 100644 qqrobot/qinglong/test_data/db/env.db create mode 100644 qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log create mode 100644 qqrobot/qinglong/test_init.go diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index 0d83375f3..d243d4a0f 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -127,7 +127,7 @@ func parseEnvSh() (map[string]int, error) { return ptPinToIndex, nil } -// 以下函数针对的cookie形如 pt_key=XXXX;pt_pin=YYYYYY; +// 以下函数针对的cookie形如 pt_key=XXXX;pt_pin=YYYY; func getPtPin(cookie string) string { return getCookie(cookie, "pt_pin") diff --git a/qqrobot/qinglong/env_test.go b/qqrobot/qinglong/env_test.go new file mode 100644 index 000000000..6d8b21b78 --- /dev/null +++ b/qqrobot/qinglong/env_test.go @@ -0,0 +1,74 @@ +package qinglong + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseJdCookie(t *testing.T) { + ptPinToCookieInfo, err := ParseJdCookie() + assert.NoError(t, err) + assert.Equal(t, 3, len(ptPinToCookieInfo)) + + // 普通账号 + ck1, ok := ptPinToCookieInfo["pin_1"] + assert.True(t, ok) + assert.Equal(t, 1, ck1.Index) + assert.Equal(t, "测试账号-1", ck1.Remark) + + // remarks中直接写备注,不带 remark=前缀 和 ;后缀 + ck2, ok := ptPinToCookieInfo["pin_2"] + assert.True(t, ok) + assert.Equal(t, 2, ck2.Index) + assert.Equal(t, "测试账号-2", ck2.Remark) + + // 仅在env.sh中存在的账号 + ck3, ok := ptPinToCookieInfo["pin_3"] + assert.True(t, ok) + assert.Equal(t, 3, ck3.Index) + assert.Equal(t, "pin_3", ck3.Remark) + + // 不存在的账号 + _, ok = ptPinToCookieInfo["pin_4"] + assert.False(t, ok) +} + +func Test_parseEnvDB(t *testing.T) { + ptPinToCookieInfo, err := parseEnvDB() + assert.NoError(t, err) + assert.Equal(t, 2, len(ptPinToCookieInfo)) + + assert.NotNil(t, ptPinToCookieInfo["pin_1"]) + assert.Nil(t, ptPinToCookieInfo["pin_3"]) +} + +func Test_parseEnvSh(t *testing.T) { + ptPinToIndex, err := parseEnvSh() + assert.NoError(t, err) + assert.Equal(t, 3, len(ptPinToIndex)) + + assert.Equal(t, 1, ptPinToIndex["pin_1"]) + assert.Equal(t, 3, ptPinToIndex["pin_3"]) + + _, ok := ptPinToIndex["pin_4"] + assert.False(t, ok) +} + +func Test_getPtPin(t *testing.T) { + assert.Equal(t, "YYYY", getPtPin("pt_key=XXXX;pt_pin=YYYY;")) + assert.Equal(t, "", getPtPin("")) + assert.Equal(t, "YYYY=1", getPtPin("pt_key=XXXX;pt_pin=YYYY=1;")) + assert.Equal(t, "", getPtPin("pt_key=XXXX;pt_pinYYYY;")) +} + +func Test_getCookie(t *testing.T) { + assert.Equal(t, "YYYY", getCookie("pt_key=XXXX;pt_pin=YYYY;", "pt_pin")) +} + +func Test_getRemark(t *testing.T) { + assert.Equal(t, "test", getRemark("remark=test;")) + assert.Equal(t, "remark=test", getRemark("remark=remark=test;")) + assert.Equal(t, "test", getRemark("test")) + assert.Equal(t, "", getRemark("")) +} diff --git a/qqrobot/qinglong/ql.go b/qqrobot/qinglong/ql.go index fc017939d..0d759fcfc 100644 --- a/qqrobot/qinglong/ql.go +++ b/qqrobot/qinglong/ql.go @@ -5,12 +5,14 @@ import ( "os" ) +var default_ql_dir = "/qinglong/data" + func getQlDir() string { qlEnv := os.Getenv("QL_DIR") if qlEnv != "" { return qlEnv } - return "/qinglong/data" + return default_ql_dir } func getPath(path string) string { diff --git a/qqrobot/qinglong/ql_test.go b/qqrobot/qinglong/ql_test.go new file mode 100644 index 000000000..b1d63b373 --- /dev/null +++ b/qqrobot/qinglong/ql_test.go @@ -0,0 +1,22 @@ +package qinglong + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_getQlDir(t *testing.T) { + assert.Equal(t, test_ql_dir, getQlDir()) + + _ = os.Unsetenv("QL_DIR") + assert.Equal(t, default_ql_dir, getQlDir()) + + _ = os.Setenv("QL_DIR", test_ql_dir) + assert.Equal(t, test_ql_dir, getQlDir()) +} + +func Test_getPath(t *testing.T) { + assert.Equal(t, test_ql_dir+"/test.txt", getPath("test.txt")) +} diff --git a/qqrobot/qinglong/query_test.go b/qqrobot/qinglong/query_test.go new file mode 100644 index 000000000..e85b70c12 --- /dev/null +++ b/qqrobot/qinglong/query_test.go @@ -0,0 +1,53 @@ +package qinglong + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +// 2021/12/13 20:33 by fzls + +func TestQueryCookieInfo(t *testing.T) { + var info *JdCookieInfo + + // 无参数 + info = QueryCookieInfo("") + assert.Nil(t, info) + + // pin + info = QueryCookieInfo("pin_1") + assert.NotNil(t, info) + + // 序号 + info = QueryCookieInfo("1") + assert.NotNil(t, info) + + // 备注 + info = QueryCookieInfo("测试账号-1") + assert.NotNil(t, info) + + // 仅env中存在的账号,使用pin + info = QueryCookieInfo("pin_3") + assert.NotNil(t, info) + + // 不存在的账号 + info = QueryCookieInfo("not exists") + assert.Nil(t, info) +} + +func TestQueryChartPath(t *testing.T) { + info := QueryCookieInfo("1") + chartPath := QueryChartPath(info) + expected, _ := filepath.Abs(getPath("log/.bean_chart/chart_1.jpeg")) + assert.Equal(t, expected, chartPath) +} + +func TestQuerySummary(t *testing.T) { + info := QueryCookieInfo("1") + assert.NotEmpty(t, QuerySummary(info)) + + info = QueryCookieInfo("3") + assert.Empty(t, QuerySummary(info)) +} diff --git a/qqrobot/qinglong/summary_test.go b/qqrobot/qinglong/summary_test.go new file mode 100644 index 000000000..22b951fbd --- /dev/null +++ b/qqrobot/qinglong/summary_test.go @@ -0,0 +1,19 @@ +package qinglong + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// 2021/12/13 20:48 by fzls + +func Test_parseSummary(t *testing.T) { + logPath := getPath("log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log") + + info := QueryCookieInfo("1") + assert.NotEmpty(t, parseSummary(info, logPath)) + + info = QueryCookieInfo("3") + assert.Empty(t, parseSummary(info, logPath)) +} diff --git a/qqrobot/qinglong/test_data/config/env.sh b/qqrobot/qinglong/test_data/config/env.sh new file mode 100644 index 000000000..3cf4c139a --- /dev/null +++ b/qqrobot/qinglong/test_data/config/env.sh @@ -0,0 +1 @@ +export JD_COOKIE="pt_key=key_1;pt_pin=pin_1;&pt_key=key_2;pt_pin=pin_2;&pt_key=key_3;pt_pin=pin_3;" diff --git a/qqrobot/qinglong/test_data/db/env.db b/qqrobot/qinglong/test_data/db/env.db new file mode 100644 index 000000000..ee1d2abd7 --- /dev/null +++ b/qqrobot/qinglong/test_data/db/env.db @@ -0,0 +1,2 @@ +{"value":"pt_key=key_1;pt_pin=pin_1;","_id":"1","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"remark=测试账号-1;"} +{"value":"pt_key=key_2;pt_pin=pin_2;","_id":"2","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"测试账号-2"} diff --git a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log new file mode 100644 index 000000000..1397eb843 --- /dev/null +++ b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log @@ -0,0 +1,52 @@ +******开始查询【京东账号1】pin_1 ********* + +京东服务器返回空数据,将无法获取等级及VIP信息 +正在获取领现金任务签名... +领现金任务签名获取成功... +【账号1🆔】测试账号-1 +【账号信息】京享值7871 +【今日京豆】收203豆 +【昨日京豆】收771豆 +【当前京豆】18527豆(≈185.27元) +【京喜牧场】283枚鸡蛋 +【极速金币】255254币(≈25.53元) +【京东赚赚】65300币(≈6.53元) +【京东秒杀】7598币(≈7.60元) +【其他信息】领现金:5.78元 +【东东农场】4斤冰糖橙(97%,3天) +【京喜工厂】兑换超时,请重选商品! +【东东工厂】小米11青春版8GB+256GB(15%,剩29) +【东东萌宠】憨憨宠 猫砂除臭珠 (32%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】49.83(总过期26.42)元 +【京喜红包】24.15(将过期19.52)元 +【京东红包】25.68(将过期6.90)元 + +******开始查询【京东账号2】pin_2********* +京东服务器返回空数据,将无法获取等级及VIP信息 +正在获取领现金任务签名... +领现金任务签名获取成功... +【账号2🆔】测试账号-2 +【账号信息】京享值6529 +【今日京豆】收133豆 +【昨日京豆】收310豆,支2豆 +【当前京豆】4184豆(≈41.84元) +【京喜牧场】160枚鸡蛋 +【极速金币】110142币(≈11.01元) +【京东赚赚】60200币(≈6.02元) +【京东秒杀】6854币(≈6.85元) +【其他信息】汪汪:19级,领现金:3.4元 +【东东农场】16枚鸡蛋(61%,9天) +【京喜工厂】鸡蛋挂面1kg(84%,1天) +【东东工厂】小米11青春版8GB+256GB(10%,剩29) +【东东萌宠】3层抽纸巾30包(14%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】9.68(总过期4.03)元 +【京喜红包】8.88(将过期4.00)元 +【京东红包】0.80(将过期0.03)元 + +开始发送通知... +通知标题: 京东资产变动 +正在处理账号Remark..... +处理完成,开始发送通知... +发送go-cqhttp通知调用API失败!! diff --git a/qqrobot/qinglong/test_init.go b/qqrobot/qinglong/test_init.go new file mode 100644 index 000000000..4c77c9e61 --- /dev/null +++ b/qqrobot/qinglong/test_init.go @@ -0,0 +1,9 @@ +package qinglong + +import "os" + +var test_ql_dir = "test_data" + +func init() { + _ = os.Setenv("QL_DIR", test_ql_dir) +} From be50d624a9473a2d6253b33bb9a49c3cdd3c95f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 21:33:06 +0800 Subject: [PATCH 188/612] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=E5=86=99=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/{test_init.go => init_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename qqrobot/qinglong/{test_init.go => init_test.go} (100%) diff --git a/qqrobot/qinglong/test_init.go b/qqrobot/qinglong/init_test.go similarity index 100% rename from qqrobot/qinglong/test_init.go rename to qqrobot/qinglong/init_test.go From 562d0fd04386e9b341ecc85ae1f5236ab7b8e2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 21:40:13 +0800 Subject: [PATCH 189/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E9=9D=92?= =?UTF-8?q?=E9=BE=99=E7=9B=B8=E5=85=B3=E7=9A=84=E6=8C=87=E4=BB=A4=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/command.go | 26 +++++++++++++++++++++++++- qqrobot/config.go | 12 +++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/qqrobot/command.go b/qqrobot/command.go index 87c8041ee..5aed45a80 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -8,6 +8,8 @@ import ( "strings" "time" + "github.com/Mrs4s/go-cqhttp/coolq" + "github.com/Mrs4s/go-cqhttp/qqrobot/qinglong" "github.com/pkg/errors" "github.com/Mrs4s/MiraiGo/message" @@ -101,11 +103,33 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms musicElem, err := r.makeMusicShareElement(musicName, message.QQMusic) if err != nil { - return "", nil, errors.Errorf("没有找到歌曲:%v", musicName) + return fmt.Sprintf("没有找到歌曲:%v", musicName), nil, nil } msg = fmt.Sprintf("请欣赏歌曲:%v", musicName) extraReplies = append(extraReplies, musicElem) + } else if match = commandRegexQinglongChart.FindStringSubmatch(commandStr); len(match) == len(commandRegexQinglongChart.SubexpNames()) { + // full_match|参数 + queryParam := match[1] + + cookieInfo := qinglong.QueryCookieInfo(queryParam) + if cookieInfo == nil { + return fmt.Sprintf("未找到相关cookie:%v", queryParam), nil, nil + } + + chartPath := qinglong.QueryChartPath(cookieInfo) + extraReplies = append(extraReplies, &coolq.LocalImageElement{File: chartPath}) + } else if match = commandRegexQinglongSummary.FindStringSubmatch(commandStr); len(match) == len(commandRegexQinglongSummary.SubexpNames()) { + // full_match|参数 + queryParam := match[1] + + cookieInfo := qinglong.QueryCookieInfo(queryParam) + if cookieInfo == nil { + return fmt.Sprintf("未找到相关cookie:%v", queryParam), nil, nil + } + + summary := qinglong.QuerySummary(cookieInfo) + extraReplies = append(extraReplies, message.NewText(summary)) } else { return "", nil, errors.Errorf("没有找到该指令哦") } diff --git a/qqrobot/config.go b/qqrobot/config.go index 4e24222bb..df3b616d0 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -91,11 +91,13 @@ const ( ) var ( - commandregexAddwhitelist = regexp.MustCompile(`\s*AddWhiteList\s+(?P.+?)\s+(?P\d+)`) - commandregexRulenamelist = regexp.MustCompile(`RuleNameList`) - commandregexBuycard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) - commandregexQuerycard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) - commandRegexMusic = regexp.MustCompile(`.*(点歌|听歌|点首|来首|想听|要听)\s*(?P.+)`) + commandregexAddwhitelist = regexp.MustCompile(`\s*AddWhiteList\s+(?P.+?)\s+(?P\d+)`) + commandregexRulenamelist = regexp.MustCompile(`RuleNameList`) + commandregexBuycard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) + commandregexQuerycard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) + commandRegexMusic = regexp.MustCompile(`.*(点歌|听歌|点首|来首|想听|要听)\s*(?P.+)`) + commandRegexQinglongChart = regexp.MustCompile(`统计图\s*(?P.+)`) + commandRegexQinglongSummary = regexp.MustCompile(`概览\s*(?P.+)`) ) // RuleConfig 规则配置 From 85a40d06a400742e0f697b80126c204e6521df02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 21:40:44 +0800 Subject: [PATCH 190/612] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E5=8F=8C=E5=87=BB=E6=97=B6=EF=BC=8C=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E5=AE=9E=E9=99=85=E7=94=A8=E5=88=B0FastStart=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gocq/main.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 366740c64..e9e4466bf 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -13,6 +13,7 @@ import ( "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" + "github.com/Mrs4s/go-cqhttp/global/terminal" para "github.com/fumiama/go-hide-param" rotatelogs "github.com/lestrrat-go/file-rotatelogs" log "github.com/sirupsen/logrus" @@ -24,7 +25,6 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/global/terminal" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/selfdiagnosis" @@ -45,14 +45,6 @@ var allowStatus = [...]client.UserOnlineStatus{ // Main 启动主程序 func Main() { base.Parse() - if !base.FastStart && terminal.RunningByDoubleClick() { - err := terminal.NoMoreDoubleClick() - if err != nil { - log.Errorf("遇到错误: %v", err) - time.Sleep(time.Second * 5) - } - return - } switch { case base.LittleH: base.Help() @@ -121,6 +113,14 @@ func Main() { } } } + if !base.FastStart && terminal.RunningByDoubleClick() { + err := terminal.NoMoreDoubleClick() + if err != nil { + log.Errorf("遇到错误: %v", err) + time.Sleep(time.Second * 5) + } + return + } if (base.Account.Uin == 0 || (base.Account.Password == "" && !base.Account.Encrypt)) && !global.PathExists("session.token") { log.Warn("账号密码未配置, 将使用二维码登录.") From 23a2bc885b883e59d298601fd8d52cc019e1b81f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 21:54:48 +0800 Subject: [PATCH 191/612] =?UTF-8?q?feat:=20=E4=BF=AE=E6=AD=A3=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E7=9A=84=E9=9D=92=E9=BE=99=E6=95=B0=E6=8D=AE=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=EF=BC=8C=E5=B9=B6=E5=9C=A8=E5=90=AF=E5=8A=A8=E6=97=B6?= =?UTF-8?q?=E6=89=93=E5=8D=B0=E8=AF=A5=E4=BF=A1=E6=81=AF=EF=BC=8C=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/ql.go | 6 +++--- qqrobot/qinglong/ql_test.go | 6 +++--- qqrobot/qq_robot.go | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/qqrobot/qinglong/ql.go b/qqrobot/qinglong/ql.go index 0d759fcfc..8c7a74efd 100644 --- a/qqrobot/qinglong/ql.go +++ b/qqrobot/qinglong/ql.go @@ -5,9 +5,9 @@ import ( "os" ) -var default_ql_dir = "/qinglong/data" +var default_ql_dir = "/root/qinglong/data" -func getQlDir() string { +func GetQlDir() string { qlEnv := os.Getenv("QL_DIR") if qlEnv != "" { return qlEnv @@ -16,5 +16,5 @@ func getQlDir() string { } func getPath(path string) string { - return getQlDir() + "/" + path + return GetQlDir() + "/" + path } diff --git a/qqrobot/qinglong/ql_test.go b/qqrobot/qinglong/ql_test.go index b1d63b373..88065c98c 100644 --- a/qqrobot/qinglong/ql_test.go +++ b/qqrobot/qinglong/ql_test.go @@ -8,13 +8,13 @@ import ( ) func Test_getQlDir(t *testing.T) { - assert.Equal(t, test_ql_dir, getQlDir()) + assert.Equal(t, test_ql_dir, GetQlDir()) _ = os.Unsetenv("QL_DIR") - assert.Equal(t, default_ql_dir, getQlDir()) + assert.Equal(t, default_ql_dir, GetQlDir()) _ = os.Setenv("QL_DIR", test_ql_dir) - assert.Equal(t, test_ql_dir, getQlDir()) + assert.Equal(t, test_ql_dir, GetQlDir()) } func Test_getPath(t *testing.T) { diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 2e756d592..a432f041a 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -15,6 +15,7 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/qqrobot/qinglong" "github.com/gookit/color" lru "github.com/hashicorp/golang-lru" "github.com/pkg/errors" @@ -100,6 +101,7 @@ func (r *QQRobot) Start() { r.quitCtx, r.quitFunc = context.WithCancel(context.Background()) logger.Infof("当前机器人账号:%v", base.Account.Uin) + logger.Infof("当前青龙数据目录: %v", qinglong.GetQlDir()) r.notify(r.Config.Robot.OnStart) go r.updateTicker() From af8831ec9c30db3c6bf0c2dd012fbd8c8d265bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 22:56:53 +0800 Subject: [PATCH 192/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E7=BB=9F=E8=AE=A1=E5=9B=BE=E6=96=87=E4=BB=B6=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E5=AD=98=E5=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index dbb078aad..5c377bf34 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" + "github.com/Mrs4s/go-cqhttp/global" logger "github.com/sirupsen/logrus" ) @@ -57,6 +58,10 @@ func QueryChartPath(info *JdCookieInfo) string { return "" } + if !global.PathExists(path) { + return "" + } + return path } From 38d0e1d05da1b53eda4840b26545519a562a0dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 13 Dec 2021 23:13:11 +0800 Subject: [PATCH 193/612] =?UTF-8?q?feat:=20chart=E7=9A=84=E6=94=B9?= =?UTF-8?q?=E7=94=A8pt=5Fpin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 2 +- qqrobot/qinglong/query_test.go | 2 +- qqrobot/qinglong/test_data/log/.bean_chart/chart_pin_1.jpeg | 0 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 qqrobot/qinglong/test_data/log/.bean_chart/chart_pin_1.jpeg diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 5c377bf34..cda9fb414 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -53,7 +53,7 @@ func QueryChartPath(info *JdCookieInfo) string { } imageDir := getPath("log/.bean_chart") - path, err := filepath.Abs(fmt.Sprintf("%s/chart_%v.jpeg", imageDir, info.Index)) + path, err := filepath.Abs(fmt.Sprintf("%s/chart_%v.jpeg", imageDir, info.PtPin)) if err != nil { return "" } diff --git a/qqrobot/qinglong/query_test.go b/qqrobot/qinglong/query_test.go index e85b70c12..53280a109 100644 --- a/qqrobot/qinglong/query_test.go +++ b/qqrobot/qinglong/query_test.go @@ -40,7 +40,7 @@ func TestQueryCookieInfo(t *testing.T) { func TestQueryChartPath(t *testing.T) { info := QueryCookieInfo("1") chartPath := QueryChartPath(info) - expected, _ := filepath.Abs(getPath("log/.bean_chart/chart_1.jpeg")) + expected, _ := filepath.Abs(getPath("log/.bean_chart/chart_pin_1.jpeg")) assert.Equal(t, expected, chartPath) } diff --git a/qqrobot/qinglong/test_data/log/.bean_chart/chart_pin_1.jpeg b/qqrobot/qinglong/test_data/log/.bean_chart/chart_pin_1.jpeg new file mode 100644 index 000000000..e69de29bb From 8f4286074b8b6834794844c4e4a2bdc912d4afc9 Mon Sep 17 00:00:00 2001 From: sam01101 Date: Tue, 14 Dec 2021 11:08:46 +0800 Subject: [PATCH 194/612] [fix] Checkbox cause github 500 error --- .github/ISSUE_TEMPLATE/bug-report.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 9e2f15613..f799746ca 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -121,14 +121,6 @@ body: validations: required: true - # Optional | Reproduce possible - - type: checkboxes - id: reproduce-possible - attributes: - label: 在最新的 Release 版本中能否复现 - options: - - label: 能 - # Input | Reproduce - type: textarea id: reproduce-steps From eab9e271ecd5b1fbc3625af5e1ef35a9595f0164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 14 Dec 2021 15:51:12 +0800 Subject: [PATCH 195/612] =?UTF-8?q?feat:=20faststart=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=94=BE=E5=88=B0=E6=A3=80=E6=9F=A5=E5=8F=8C=E5=87=BB=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E5=8D=95=E7=8B=AC=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gocq/main.go | 20 +++++++++----------- internal/base/flag.go | 11 +++++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index e9e4466bf..bcb1152ef 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -13,7 +13,6 @@ import ( "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" - "github.com/Mrs4s/go-cqhttp/global/terminal" para "github.com/fumiama/go-hide-param" rotatelogs "github.com/lestrrat-go/file-rotatelogs" log "github.com/sirupsen/logrus" @@ -25,6 +24,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/global/terminal" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/selfdiagnosis" @@ -45,6 +45,14 @@ var allowStatus = [...]client.UserOnlineStatus{ // Main 启动主程序 func Main() { base.Parse() + if !base.FastStart && terminal.RunningByDoubleClick() { + err := terminal.NoMoreDoubleClick() + if err != nil { + log.Errorf("遇到错误: %v", err) + time.Sleep(time.Second * 5) + } + return + } switch { case base.LittleH: base.Help() @@ -108,19 +116,9 @@ func Main() { byteKey = []byte(arg[p]) para.Hide(p) } - case "faststart": - base.FastStart = true } } } - if !base.FastStart && terminal.RunningByDoubleClick() { - err := terminal.NoMoreDoubleClick() - if err != nil { - log.Errorf("遇到错误: %v", err) - time.Sleep(time.Second * 5) - } - return - } if (base.Account.Uin == 0 || (base.Account.Password == "" && !base.Account.Encrypt)) && !global.PathExists("session.token") { log.Warn("账号密码未配置, 将使用二维码登录.") diff --git a/internal/base/flag.go b/internal/base/flag.go index 8cc0ab6cb..782a56295 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -68,6 +68,17 @@ func Parse() { if *d { Debug = true } + + // 在检查windows双击之前解析 faststart 标记 + arg := os.Args + if len(arg) > 1 { + for i := range arg { + switch arg[i] { + case "faststart": + FastStart = true + } + } + } } // Init read config from yml file From a218afeca49cdc69f42f55f16b6a0879c76190f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 14 Dec 2021 15:55:20 +0800 Subject: [PATCH 196/612] fix: lint --- internal/base/flag.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/base/flag.go b/internal/base/flag.go index 782a56295..9f77a1a97 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -73,8 +73,7 @@ func Parse() { arg := os.Args if len(arg) > 1 { for i := range arg { - switch arg[i] { - case "faststart": + if arg[i] == "faststart" { FastStart = true } } From 240bcc12eb916c2a0cafba9730b36604d7f9c9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 14 Dec 2021 16:00:51 +0800 Subject: [PATCH 197/612] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 8 ++++---- .gitignore | 2 +- .goreleaser.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5fc583a0..e8855b5d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ name: CI -on: [push, pull_request,workflow_dispatch] +on: [ push, pull_request,workflow_dispatch ] env: BINARY_PREFIX: "qq_robot_" @@ -15,8 +15,8 @@ jobs: strategy: matrix: # build and publish in parallel: linux/386, linux/amd64, windows/386, windows/amd64, darwin/amd64, darwin/arm64 - goos: [linux, windows, darwin] - goarch: ["386", amd64, arm, arm64] + goos: [ linux, windows, darwin ] + goarch: [ "386", amd64, arm, arm64 ] exclude: - goos: darwin goarch: arm @@ -57,7 +57,7 @@ jobs: go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . - cp global/config/config.yml output/config.yml + cp modules/config/default_config.yml output/config.yml cp qqrobot/config.toml output/config.toml - name: Upload artifact diff --git a/.gitignore b/.gitignore index c193f3538..9b87e7a87 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,5 @@ logs/ *.exe resource.syso -!global/config/config.yml +!modules/config/default_config.yml !qqrobot/config.toml diff --git a/.goreleaser.yml b/.goreleaser.yml index c900dbd8f..52e610e18 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -102,7 +102,7 @@ archives: - goos: windows format: zip files: - - src: global/config/config.yml + - src: modules/config/default_config.yml dst: . strip_parent: true - src: qqrobot/config.toml From 7e2e20560c5c707335dd9b9b14109d514140e35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 14 Dec 2021 16:05:22 +0800 Subject: [PATCH 198/612] =?UTF-8?q?ci:=20=E4=B8=8Dlint=20cqhttp=E8=87=AA?= =?UTF-8?q?=E5=B7=B1=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .golangci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.golangci.yml b/.golangci.yml index ca674a0e7..df39a08c5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -66,6 +66,7 @@ run: skip-dirs: - db - cmd/api-generator + - coolq tests: true # output configuration options From 4a1ed9ef63dcd37e2018e7b7b2276504019ca186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 14 Dec 2021 16:11:07 +0800 Subject: [PATCH 199/612] fix: lint --- qqrobot/command.go | 3 ++- qqrobot/qinglong/init_test.go | 4 ++-- qqrobot/qinglong/ql.go | 5 +++-- qqrobot/qinglong/ql_test.go | 10 +++++----- qqrobot/qinglong/query.go | 3 ++- qqrobot/qq_robot.go | 5 +++-- qqrobot/qq_robot_test.go | 26 +++++++++++++------------- 7 files changed, 30 insertions(+), 26 deletions(-) diff --git a/qqrobot/command.go b/qqrobot/command.go index 5aed45a80..828b37cbb 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -8,9 +8,10 @@ import ( "strings" "time" + "github.com/pkg/errors" + "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/qqrobot/qinglong" - "github.com/pkg/errors" "github.com/Mrs4s/MiraiGo/message" logger "github.com/sirupsen/logrus" diff --git a/qqrobot/qinglong/init_test.go b/qqrobot/qinglong/init_test.go index 4c77c9e61..6af4b3c8a 100644 --- a/qqrobot/qinglong/init_test.go +++ b/qqrobot/qinglong/init_test.go @@ -2,8 +2,8 @@ package qinglong import "os" -var test_ql_dir = "test_data" +var testQlDir = "test_data" func init() { - _ = os.Setenv("QL_DIR", test_ql_dir) + _ = os.Setenv("QL_DIR", testQlDir) } diff --git a/qqrobot/qinglong/ql.go b/qqrobot/qinglong/ql.go index 8c7a74efd..007c439d4 100644 --- a/qqrobot/qinglong/ql.go +++ b/qqrobot/qinglong/ql.go @@ -5,14 +5,15 @@ import ( "os" ) -var default_ql_dir = "/root/qinglong/data" +var defaultQlDir = "/root/qinglong/data" +// GetQlDir 获取青龙数据目录路径 func GetQlDir() string { qlEnv := os.Getenv("QL_DIR") if qlEnv != "" { return qlEnv } - return default_ql_dir + return defaultQlDir } func getPath(path string) string { diff --git a/qqrobot/qinglong/ql_test.go b/qqrobot/qinglong/ql_test.go index 88065c98c..787f0ef91 100644 --- a/qqrobot/qinglong/ql_test.go +++ b/qqrobot/qinglong/ql_test.go @@ -8,15 +8,15 @@ import ( ) func Test_getQlDir(t *testing.T) { - assert.Equal(t, test_ql_dir, GetQlDir()) + assert.Equal(t, testQlDir, GetQlDir()) _ = os.Unsetenv("QL_DIR") - assert.Equal(t, default_ql_dir, GetQlDir()) + assert.Equal(t, defaultQlDir, GetQlDir()) - _ = os.Setenv("QL_DIR", test_ql_dir) - assert.Equal(t, test_ql_dir, GetQlDir()) + _ = os.Setenv("QL_DIR", testQlDir) + assert.Equal(t, testQlDir, GetQlDir()) } func Test_getPath(t *testing.T) { - assert.Equal(t, test_ql_dir+"/test.txt", getPath("test.txt")) + assert.Equal(t, testQlDir+"/test.txt", getPath("test.txt")) } diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index cda9fb414..849004f46 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -8,8 +8,9 @@ import ( "strconv" "strings" - "github.com/Mrs4s/go-cqhttp/global" logger "github.com/sirupsen/logrus" + + "github.com/Mrs4s/go-cqhttp/global" ) // QueryCookieInfo 尝试通过pt_pin/序号/昵称等来查询cookie信息 diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index a432f041a..5767e7e36 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -14,14 +14,15 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" - "github.com/Mrs4s/go-cqhttp/internal/base" - "github.com/Mrs4s/go-cqhttp/qqrobot/qinglong" "github.com/gookit/color" lru "github.com/hashicorp/golang-lru" "github.com/pkg/errors" logger "github.com/sirupsen/logrus" tbp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp/v20190627" + "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/qqrobot/qinglong" + "github.com/Mrs4s/go-cqhttp/coolq" ) diff --git a/qqrobot/qq_robot_test.go b/qqrobot/qq_robot_test.go index 093de5b8b..3db7f8857 100644 --- a/qqrobot/qq_robot_test.go +++ b/qqrobot/qq_robot_test.go @@ -65,22 +65,22 @@ func Test_version_to_version_int_list(t *testing.T) { func Test_version_less(t *testing.T) { tests := []struct { - version_left string - version_right string - want bool + versionLeft string + versionRight string + want bool }{ - {version_left: "v1.0.0", version_right: "v1.0.0", want: false}, - {version_left: "v1.0.0", version_right: "v1.0.1", want: true}, - {version_left: "v1.0.0", version_right: "v1.1.0", want: true}, - {version_left: "v1.0.0", version_right: "v2.0.0", want: true}, - {version_left: "v2.0.0", version_right: "v1.0.0", want: false}, - {version_left: "v2.0.0", version_right: "v10.0.0", want: true}, - {version_left: "v3.2.9.1", version_right: "v4.0.0", want: true}, + {versionLeft: "v1.0.0", versionRight: "v1.0.0", want: false}, + {versionLeft: "v1.0.0", versionRight: "v1.0.1", want: true}, + {versionLeft: "v1.0.0", versionRight: "v1.1.0", want: true}, + {versionLeft: "v1.0.0", versionRight: "v2.0.0", want: true}, + {versionLeft: "v2.0.0", versionRight: "v1.0.0", want: false}, + {versionLeft: "v2.0.0", versionRight: "v10.0.0", want: true}, + {versionLeft: "v3.2.9.1", versionRight: "v4.0.0", want: true}, } for _, tt := range tests { - t.Run(fmt.Sprintf("%v-%v", tt.version_left, tt.version_right), func(t *testing.T) { - if got := versionLess(tt.version_left, tt.version_right); got != tt.want { - t.Errorf("version_less(%v, %v) = %v, want %v", tt.version_left, tt.version_right, got, tt.want) + t.Run(fmt.Sprintf("%v-%v", tt.versionLeft, tt.versionRight), func(t *testing.T) { + if got := versionLess(tt.versionLeft, tt.versionRight); got != tt.want { + t.Errorf("version_less(%v, %v) = %v, want %v", tt.versionLeft, tt.versionRight, got, tt.want) } }) } From 8da29c292e1c580d2ea99be55a48fd7be2527299 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 14 Dec 2021 17:50:36 +0800 Subject: [PATCH 200/612] feat: get_guild_msg api no_cache --- coolq/api.go | 132 +++++++++++++++++++++++++++--------------- coolq/bot.go | 27 +++++++-- coolq/converter.go | 15 +++++ coolq/cqcode.go | 3 +- coolq/event.go | 30 +++++----- db/mongodb/mongodb.go | 6 +- docs/guild.md | 2 + go.mod | 2 +- go.sum | 4 +- modules/api/api.go | 3 +- 10 files changed, 151 insertions(+), 73 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 827b90ea4..2c6c78642 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -131,7 +131,7 @@ func (bot *CQBot) CQGetGuildChannelList(guildID uint64, noCache bool) global.MSG if noCache { channels, err := bot.Client.GuildService.FetchChannelList(guildID) if err != nil { - log.Errorf("获取频道 %v 子频道列表时出现错误: %v", guildID, err) + log.Warnf("获取频道 %v 子频道列表时出现错误: %v", guildID, err) return Failed(100, "API_ERROR", err.Error()) } guild.Channels = channels @@ -195,7 +195,7 @@ func (bot *CQBot) CQGetGuildMemberProfile(guildID, userID uint64) global.MSG { } profile, err := bot.Client.GuildService.FetchGuildMemberProfileInfo(guildID, userID) if err != nil { - log.Errorf("获取频道 %v 成员 %v 资料时出现错误: %v", guildID, userID, err) + log.Warnf("获取频道 %v 成员 %v 资料时出现错误: %v", guildID, userID, err) return Failed(100, "API_ERROR", err.Error()) } roles := make([]global.MSG, 0, len(profile.Roles)) @@ -219,7 +219,7 @@ func (bot *CQBot) CQGetGuildMemberProfile(guildID, userID uint64) global.MSG { func (bot *CQBot) CQGetGuildRoles(guildID uint64) global.MSG { r, err := bot.Client.GuildService.GetGuildRoles(guildID) if err != nil { - log.Errorf("获取频道 %v 角色列表时出现错误: %v", guildID, err) + log.Warnf("获取频道 %v 角色列表时出现错误: %v", guildID, err) return Failed(100, "API_ERROR", err.Error()) } roles := make([]global.MSG, len(r)) @@ -249,7 +249,7 @@ func (bot *CQBot) CQCreateGuildRole(guildID uint64, name string, color uint32, i } role, err := bot.Client.GuildService.CreateGuildRole(guildID, name, color, independent, userSlice) if err != nil { - log.Errorf("创建频道 %v 角色时出现错误: %v", guildID, err) + log.Warnf("创建频道 %v 角色时出现错误: %v", guildID, err) return Failed(100, "API_ERROR", err.Error()) } return OK(global.MSG{ @@ -262,7 +262,7 @@ func (bot *CQBot) CQCreateGuildRole(guildID uint64, name string, color uint32, i func (bot *CQBot) CQDeleteGuildRole(guildID uint64, roleID uint64) global.MSG { err := bot.Client.GuildService.DeleteGuildRole(guildID, roleID) if err != nil { - log.Errorf("删除频道 %v 角色时出现错误: %v", guildID, err) + log.Warnf("删除频道 %v 角色时出现错误: %v", guildID, err) return Failed(100, "API_ERROR", err.Error()) } return OK(nil) @@ -279,7 +279,7 @@ func (bot *CQBot) CQSetGuildMemberRole(guildID uint64, set bool, roleID uint64, } err := bot.Client.GuildService.SetUserRoleInGuild(guildID, set, roleID, userSlice) if err != nil { - log.Errorf("设置用户在频道 %v 中的角色时出现错误: %v", guildID, err) + log.Warnf("设置用户在频道 %v 中的角色时出现错误: %v", guildID, err) return Failed(100, "API_ERROR", err.Error()) } return OK(nil) @@ -290,7 +290,7 @@ func (bot *CQBot) CQSetGuildMemberRole(guildID uint64, set bool, roleID uint64, func (bot *CQBot) CQModifyRoleInGuild(guildID uint64, roleID uint64, name string, color uint32, indepedent bool) global.MSG { err := bot.Client.GuildService.ModifyRoleInGuild(guildID, roleID, name, color, indepedent) if err != nil { - log.Errorf("修改频道 %v 角色时出现错误: %v", guildID, err) + log.Warnf("修改频道 %v 角色时出现错误: %v", guildID, err) return Failed(100, "API_ERROR", err.Error()) } return OK(nil) @@ -312,7 +312,7 @@ func (bot *CQBot) CQGetTopicChannelFeeds(guildID, channelID uint64) global.MSG { } feeds, err := bot.Client.GuildService.GetTopicChannelFeeds(guildID, channelID) if err != nil { - log.Errorf("获取频道 %v 帖子时出现错误: %v", channelID, err) + log.Warnf("获取频道 %v 帖子时出现错误: %v", channelID, err) return Failed(100, "API_ERROR", err.Error()) } c := make([]global.MSG, 0, len(feeds)) @@ -344,7 +344,7 @@ func (bot *CQBot) CQGetFriendList() global.MSG { func (bot *CQBot) CQGetUnidirectionalFriendList() global.MSG { list, err := bot.Client.GetUnidirectionalFriendList() if err != nil { - log.Errorf("获取单向好友列表时出现错误: %v", err) + log.Warnf("获取单向好友列表时出现错误: %v", err) return Failed(100, "API_ERROR", err.Error()) } fs := make([]global.MSG, 0, len(list)) @@ -365,13 +365,13 @@ func (bot *CQBot) CQGetUnidirectionalFriendList() global.MSG { func (bot *CQBot) CQDeleteUnidirectionalFriend(uin int64) global.MSG { list, err := bot.Client.GetUnidirectionalFriendList() if err != nil { - log.Errorf("获取单向好友列表时出现错误: %v", err) + log.Warnf("获取单向好友列表时出现错误: %v", err) return Failed(100, "API_ERROR", err.Error()) } for _, f := range list { if f.Uin == uin { if err = bot.Client.DeleteUnidirectionalFriend(uin); err != nil { - log.Errorf("删除单向好友时出现错误: %v", err) + log.Warnf("删除单向好友时出现错误: %v", err) return Failed(100, "API_ERROR", err.Error()) } return OK(nil) @@ -388,7 +388,7 @@ func (bot *CQBot) CQDeleteFriend(uin int64) global.MSG { return Failed(100, "FRIEND_NOT_FOUND", "好友不存在") } if err := bot.Client.DeleteFriend(uin); err != nil { - log.Errorf("删除好友时出现错误: %v", err) + log.Warnf("删除好友时出现错误: %v", err) return Failed(100, "DELETE_API_ERROR", err.Error()) } return OK(nil) @@ -516,7 +516,7 @@ func (bot *CQBot) CQGetGroupMemberInfo(groupID, userID int64, noCache bool) glob func (bot *CQBot) CQGetGroupFileSystemInfo(groupID int64) global.MSG { fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { - log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err) + log.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } return OK(fs) @@ -529,12 +529,12 @@ func (bot *CQBot) CQGetGroupFileSystemInfo(groupID int64) global.MSG { func (bot *CQBot) CQGetGroupRootFiles(groupID int64) global.MSG { fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { - log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err) + log.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } files, folders, err := fs.Root() if err != nil { - log.Errorf("获取群 %v 根目录文件失败: %v", groupID, err) + log.Warnf("获取群 %v 根目录文件失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } return OK(global.MSG{ @@ -550,12 +550,12 @@ func (bot *CQBot) CQGetGroupRootFiles(groupID int64) global.MSG { func (bot *CQBot) CQGetGroupFilesByFolderID(groupID int64, folderID string) global.MSG { fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { - log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err) + log.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } files, folders, err := fs.GetFilesByFolder(folderID) if err != nil { - log.Errorf("获取群 %v 根目录 %v 子文件失败: %v", groupID, folderID, err) + log.Warnf("获取群 %v 根目录 %v 子文件失败: %v", groupID, folderID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } return OK(global.MSG{ @@ -584,19 +584,19 @@ func (bot *CQBot) CQGetGroupFileURL(groupID int64, fileID string, busID int32) g // @route(upload_group_file) func (bot *CQBot) CQUploadGroupFile(groupID int64, file, name, folder string) global.MSG { if !global.PathExists(file) { - log.Errorf("上传群文件 %v 失败: 文件不存在", file) + log.Warnf("上传群文件 %v 失败: 文件不存在", file) return Failed(100, "FILE_NOT_FOUND", "文件不存在") } fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { - log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err) + log.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } if folder == "" { folder = "/" } if err = fs.UploadFile(file, name, folder); err != nil { - log.Errorf("上传群 %v 文件 %v 失败: %v", groupID, file, err) + log.Warnf("上传群 %v 文件 %v 失败: %v", groupID, file, err) return Failed(100, "FILE_SYSTEM_UPLOAD_API_ERROR", err.Error()) } return OK(nil) @@ -608,11 +608,11 @@ func (bot *CQBot) CQUploadGroupFile(groupID int64, file, name, folder string) gl func (bot *CQBot) CQGroupFileCreateFolder(groupID int64, parentID, name string) global.MSG { fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { - log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err) + log.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } if err = fs.CreateFolder(parentID, name); err != nil { - log.Errorf("创建群 %v 文件夹失败: %v", groupID, err) + log.Warnf("创建群 %v 文件夹失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } return OK(nil) @@ -625,11 +625,11 @@ func (bot *CQBot) CQGroupFileCreateFolder(groupID int64, parentID, name string) func (bot *CQBot) CQGroupFileDeleteFolder(groupID int64, id string) global.MSG { fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { - log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err) + log.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } if err = fs.DeleteFolder(id); err != nil { - log.Errorf("删除群 %v 文件夹 %v 时出现文件: %v", groupID, id, err) + log.Warnf("删除群 %v 文件夹 %v 时出现文件: %v", groupID, id, err) return Failed(200, "FILE_SYSTEM_API_ERROR", err.Error()) } return OK(nil) @@ -642,11 +642,11 @@ func (bot *CQBot) CQGroupFileDeleteFolder(groupID int64, id string) global.MSG { func (bot *CQBot) CQGroupFileDeleteFile(groupID int64, id string, busID int32) global.MSG { fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { - log.Errorf("获取群 %v 文件系统信息失败: %v", groupID, err) + log.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) return Failed(100, "FILE_SYSTEM_API_ERROR", err.Error()) } if res := fs.DeleteFile("", id, busID); res != "" { - log.Errorf("删除群 %v 文件 %v 时出现文件: %v", groupID, id, res) + log.Warnf("删除群 %v 文件 %v 时出现文件: %v", groupID, id, res) return Failed(200, "FILE_SYSTEM_API_ERROR", res) } return OK(nil) @@ -1125,14 +1125,14 @@ func (bot *CQBot) CQProcessFriendRequest(flag string, approve bool) global.MSG { func (bot *CQBot) CQProcessGroupRequest(flag, subType, reason string, approve bool) global.MSG { msgs, err := bot.Client.GetGroupSystemMessages() if err != nil { - log.Errorf("获取群系统消息失败: %v", err) + log.Warnf("获取群系统消息失败: %v", err) return Failed(100, "SYSTEM_MSG_API_ERROR", err.Error()) } if subType == "add" { for _, req := range msgs.JoinRequests { if strconv.FormatInt(req.RequestId, 10) == flag { if req.Checked { - log.Errorf("处理群系统消息失败: 无法操作已处理的消息.") + log.Warnf("处理群系统消息失败: 无法操作已处理的消息.") return Failed(100, "FLAG_HAS_BEEN_CHECKED", "消息已被处理") } if approve { @@ -1147,7 +1147,7 @@ func (bot *CQBot) CQProcessGroupRequest(flag, subType, reason string, approve bo for _, req := range msgs.InvitedRequests { if strconv.FormatInt(req.RequestId, 10) == flag { if req.Checked { - log.Errorf("处理群系统消息失败: 无法操作已处理的消息.") + log.Warnf("处理群系统消息失败: 无法操作已处理的消息.") return Failed(100, "FLAG_HAS_BEEN_CHECKED", "消息已被处理") } if approve { @@ -1159,7 +1159,7 @@ func (bot *CQBot) CQProcessGroupRequest(flag, subType, reason string, approve bo } } } - log.Errorf("处理群系统消息失败: 消息 %v 不存在.", flag) + log.Warnf("处理群系统消息失败: 消息 %v 不存在.", flag) return Failed(100, "FLAG_NOT_FOUND", "FLAG不存在") } @@ -1566,24 +1566,64 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { // CQGetGuildMessage 获取频道消息 // @route(get_guild_msg) -func (bot *CQBot) CQGetGuildMessage(messageID string) global.MSG { - msg, err := db.GetGuildChannelMessageByID(messageID) - if err != nil { - log.Warnf("获取消息时出现错误: %v", err) - return Failed(100, "MSG_NOT_FOUND", "消息不存在") +func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { + source, seq := decodeGuildMessageID(messageID) + if source == nil { + log.Warnf("获取消息时出现错误: 无效消息ID") + return Failed(100, "INVALID_MESSAGE_ID", "无效消息ID") } m := global.MSG{ - "message_id": msg.ID, - "message_source": "channel", - "guild_id": fU64(msg.GuildID), - "channel_id": fU64(msg.ChannelID), - "message_seq": msg.Attribute.MessageSeq, - "sender": global.MSG{ - "user_id": fU64(msg.Attribute.SenderTinyID), - "nickname": msg.Attribute.SenderName, - }, - "time": msg.Attribute.Timestamp, - "message": ToFormattedMessage(bot.ConvertContentMessage(msg.Content, MessageSourceGuildChannel), MessageSource{SourceType: MessageSourceGuildChannel, PrimaryID: msg.GuildID, SubID: msg.ChannelID}), + "message_id": messageID, + "message_source": func() string { + if source.SourceType == MessageSourceGuildDirect { + return "direct" + } + return "channel" + }(), + "message_seq": seq, + "guild_id": fU64(source.PrimaryID), + "reactions": []int{}, + } + switch source.SourceType { + case MessageSourceGuildChannel: + m["channel_id"] = fU64(source.SubID) + if noCache { + pull, err := bot.Client.GuildService.PullGuildChannelMessage(source.PrimaryID, source.SubID, seq, seq) + if err != nil { + log.Warnf("获取消息时出现错误: %v", err) + return Failed(100, "API_ERROR", err.Error()) + } + if len(m) == 0 { + log.Warnf("获取消息时出现错误: 消息不存在") + return Failed(100, "MSG_NOT_FOUND", "消息不存在") + } + m["time"] = pull[0].Time + m["sender"] = global.MSG{ + "user_id": pull[0].Sender.TinyId, + "tiny_id": fU64(pull[0].Sender.TinyId), + "nickname": pull[0].Sender.Nickname, + } + m["message"] = ToFormattedMessage(pull[0].Elements, *source, false) + m["reactions"] = convertReactions(pull[0].Reactions) + bot.InsertGuildChannelMessage(pull[0]) + return OK(m) + } else { + channelMsgByDB, err := db.GetGuildChannelMessageByID(messageID) + if err != nil { + log.Warnf("获取消息时出现错误: %v", err) + return Failed(100, "MSG_NOT_FOUND", "消息不存在") + } + m["time"] = channelMsgByDB.Attribute.Timestamp + m["sender"] = global.MSG{ + "user_id": channelMsgByDB.Attribute.SenderTinyID, + "tiny_id": fU64(channelMsgByDB.Attribute.SenderTinyID), + "nickname": channelMsgByDB.Attribute.SenderName, + } + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, MessageSourceGuildChannel), *source) + } + case MessageSourceGuildDirect: + // todo(mrs4s): 支持 direct 消息 + m["tiny_id"] = fU64(source.SubID) } return OK(m) } diff --git a/coolq/bot.go b/coolq/bot.go index ce9d8090c..b19004973 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -519,7 +519,7 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32 // InsertGuildChannelMessage 频道消息入数据库 func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) string { - id := encodeGuildMessageID(m.GuildId, m.ChannelId, m.Id) + id := encodeGuildMessageID(m.GuildId, m.ChannelId, m.Id, MessageSourceGuildChannel) msg := &db.StoredGuildChannelMessage{ ID: id, Attribute: &db.StoredGuildMessageAttribute{ @@ -610,10 +610,29 @@ func encodeMessageID(target int64, seq int32) string { })) } -func encodeGuildMessageID(guildID, channelID, seq uint64) string { +// encodeGuildMessageID 将频道信息编码为字符串 +// 当信息来源为 Channel 时 primaryID 为 guildID , subID 为 channelID +// 当信息来源为 Direct 时 primaryID 为 guildID , subID 为 tinyID +func encodeGuildMessageID(primaryID, subID, seq uint64, source MessageSourceType) string { return base64.StdEncoding.EncodeToString(binary.NewWriterF(func(w *binary.Writer) { - w.WriteUInt64(guildID) - w.WriteUInt64(channelID) + w.WriteByte(byte(source)) + w.WriteUInt64(primaryID) + w.WriteUInt64(subID) w.WriteUInt64(seq) })) } + +func decodeGuildMessageID(id string) (source *MessageSource, seq uint64) { + b, _ := base64.StdEncoding.DecodeString(id) + if len(b) < 25 { + return + } + r := binary.NewReader(b) + source = &MessageSource{ + SourceType: MessageSourceType(r.ReadByte()), + PrimaryID: uint64(r.ReadInt64()), + SubID: uint64(r.ReadInt64()), + } + seq = uint64(r.ReadInt64()) + return +} diff --git a/coolq/converter.go b/coolq/converter.go index a62bce824..44bc17cc5 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -205,6 +205,21 @@ func convertChannelFeedInfo(f *topic.Feed) global.MSG { return m } +func convertReactions(reactions []*message.GuildMessageEmojiReaction) (r []global.MSG) { + r = make([]global.MSG, len(reactions)) + for i, re := range reactions { + r[i] = global.MSG{ + "emoji_id": re.EmojiId, + "emoji_index": re.Face.Index, + "emoji_type": re.EmojiType, + "emoji_name": re.Face.Name, + "count": re.Count, + "clicked": re.Clicked, + } + } + return +} + func fU64(v uint64) string { return strconv.FormatUint(v, 10) } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index c14203637..84b2a1372 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -72,13 +72,14 @@ type MessageSource struct { } // MessageSourceType 消息来源类型 -type MessageSourceType int32 +type MessageSourceType byte // MessageSourceType 常量 const ( MessageSourcePrivate MessageSourceType = 0 MessageSourceGroup MessageSourceType = 1 MessageSourceGuildChannel MessageSourceType = 2 + MessageSourceGuildDirect MessageSourceType = 3 ) const ( diff --git a/coolq/event.go b/coolq/event.go index 450a5c4db..0199bf660 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -169,7 +169,8 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "time": m.Time, "sender": global.MSG{ - "user_id": fU64(m.Sender.TinyId), + "user_id": m.Sender.TinyId, + "tiny_id": fU64(m.Sender.TinyId), "nickname": m.Sender.Nickname, }, }) @@ -180,7 +181,7 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien if guild == nil { return } - msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId) + msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId, MessageSourceGuildChannel) str := fmt.Sprintf("频道 %v(%v) 消息 %v 表情贴片已更新: ", guild.GuildName, guild.GuildId, msgID) currentReactions := make([]global.MSG, len(e.CurrentReactions)) for i, r := range e.CurrentReactions { @@ -199,18 +200,17 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien } log.Infof(str) bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "message_reactions_updated", - "message_sender_uin": e.MessageSenderUin, - "guild_id": fU64(e.GuildId), - "channel_id": fU64(e.ChannelId), - "message_id": msgID, - "operator_id": fU64(e.OperatorId), - "current_reactions": currentReactions, - "time": time.Now().Unix(), - "self_id": bot.Client.Uin, - "self_tiny_id": fU64(bot.Client.GuildService.TinyId), - "user_id": e.OperatorId, + "post_type": "notice", + "notice_type": "message_reactions_updated", + "guild_id": fU64(e.GuildId), + "channel_id": fU64(e.ChannelId), + "message_id": msgID, + "operator_id": fU64(e.OperatorId), + "current_reactions": currentReactions, + "time": time.Now().Unix(), + "self_id": bot.Client.Uin, + "self_tiny_id": fU64(bot.Client.GuildService.TinyId), + "user_id": e.OperatorId, }) } @@ -228,7 +228,7 @@ func (bot *CQBot) guildChannelMessageRecalledEvent(c *client.QQClient, e *client log.Errorf("处理频道撤回事件时出现错误: 获取操作者资料时出现错误 %v", err) return } - msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId) + msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId, MessageSourceGuildChannel) log.Infof("用户 %v(%v) 撤回了频道 %v(%v) 子频道 %v(%v) 的消息 %v", operator.Nickname, operator.TinyId, guild.GuildName, guild.GuildId, channel.ChannelName, channel.ChannelId, msgID) bot.dispatchEventMessage(global.MSG{ "post_type": "notice", diff --git a/db/mongodb/mongodb.go b/db/mongodb/mongodb.go index 952874346..9c8449d7d 100644 --- a/db/mongodb/mongodb.go +++ b/db/mongodb/mongodb.go @@ -84,18 +84,18 @@ func (m *MongoDBImpl) GetGuildChannelMessageByID(id string) (*db.StoredGuildChan func (m *MongoDBImpl) InsertGroupMessage(msg *db.StoredGroupMessage) error { coll := m.mongo.Collection(MongoGroupMessageCollection) - _, err := coll.InsertOne(context.Background(), msg) + _, err := coll.UpdateOne(context.Background(), bson.D{{"_id", msg.ID}}, bson.D{{"$set", msg}}, options.Update().SetUpsert(true)) return errors.Wrap(err, "insert error") } func (m *MongoDBImpl) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { coll := m.mongo.Collection(MongoPrivateMessageCollection) - _, err := coll.InsertOne(context.Background(), msg) + _, err := coll.UpdateOne(context.Background(), bson.D{{"_id", msg.ID}}, bson.D{{"$set", msg}}, options.Update().SetUpsert(true)) return errors.Wrap(err, "insert error") } func (m *MongoDBImpl) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) error { coll := m.mongo.Collection(MongoGuildChannelMessageCollection) - _, err := coll.InsertOne(context.Background(), msg) + _, err := coll.UpdateOne(context.Background(), bson.D{{"_id", msg.ID}}, bson.D{{"$set", msg}}, options.Update().SetUpsert(true)) return errors.Wrap(err, "insert error") } diff --git a/docs/guild.md b/docs/guild.md index b3d2d1a84..e6a2ed20f 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -238,6 +238,8 @@ RoleInfo: | `sender` | Sender | | 发送者 | | `message` | Message | | 消息内容 | +> 注: 此处的 `Sender` 对象为保证一致性, `user_id` 为 `uint64` 类型, 并添加了 `string` 类型的 `tiny_id` 字段 + ### 频道消息表情贴更新 **上报数据** diff --git a/go.mod b/go.mod index 9b6cba807..c159c7af7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211212174229-5d7cb85b1ed4 + github.com/Mrs4s/MiraiGo v0.0.0-20211214084325-ed979508cf8a github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index c31c2892e..06f5a9ef6 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211212174229-5d7cb85b1ed4 h1:rtUcMuZ8aMspm7Ahgw6wVXHAT+mmHU8Fe0hfmKMxks0= -github.com/Mrs4s/MiraiGo v0.0.0-20211212174229-5d7cb85b1ed4/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211214084325-ed979508cf8a h1:hADFsj9XtB+A2AZAf99vMW870YpzjPDA30+mqCXJDCo= +github.com/Mrs4s/MiraiGo v0.0.0-20211214084325-ed979508cf8a/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= diff --git a/modules/api/api.go b/modules/api/api.go index 8ba0cecf0..1eaee6216 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -153,7 +153,8 @@ func (c *Caller) call(action string, p Getter) global.MSG { return c.bot.CQGetGuildMetaByGuest(p0) case "get_guild_msg": p0 := p.Get("message_id").String() - return c.bot.CQGetGuildMessage(p0) + p1 := p.Get("no_cache").Bool() + return c.bot.CQGetGuildMessage(p0, p1) case "get_guild_roles": p0 := p.Get("guild_id").Uint() return c.bot.CQGetGuildRoles(p0) From 9506dc21abe07b48c52c07b7f239c6d4a3f9d3b1 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 14 Dec 2021 17:53:51 +0800 Subject: [PATCH 201/612] style: delete useless return --- coolq/api.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 2c6c78642..9dab51c49 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1606,7 +1606,6 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { m["message"] = ToFormattedMessage(pull[0].Elements, *source, false) m["reactions"] = convertReactions(pull[0].Reactions) bot.InsertGuildChannelMessage(pull[0]) - return OK(m) } else { channelMsgByDB, err := db.GetGuildChannelMessageByID(messageID) if err != nil { From 0accc89693930da3cca6de404dcd7b3490b9363d Mon Sep 17 00:00:00 2001 From: Ink33 Date: Tue, 14 Dec 2021 22:27:36 +0800 Subject: [PATCH 202/612] ci(package): remove useless nightly package (#1260) --- .github/workflows/build_docker_image.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/build_docker_image.yml b/.github/workflows/build_docker_image.yml index 0b592bc8d..8f09138a5 100644 --- a/.github/workflows/build_docker_image.yml +++ b/.github/workflows/build_docker_image.yml @@ -7,9 +7,7 @@ on: # Sequence of patterns matched against refs/tags tags: - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 - schedule: - # 每晚十点生成 nightly 镜像 - - cron: '00 14 * * *' # GMT 14:00 => 北京时间 22:00 + workflow_dispatch: jobs: From 304667a822f3e83a29db82597b53c99bc62c3af3 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 16 Dec 2021 17:48:39 +0800 Subject: [PATCH 203/612] feat: error dump --- cmd/gocq/main.go | 8 ++++++++ global/fs.go | 2 ++ go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 30494ee0b..b4220749a 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -6,6 +6,7 @@ import ( "crypto/md5" "crypto/sha1" "encoding/hex" + "fmt" "os" "path" "sync" @@ -407,6 +408,13 @@ func newClient() *client.QQClient { log.Error("Protocol -> " + e.Message) case "DEBUG": log.Debug("Protocol -> " + e.Message) + case "DUMP": + if !global.PathExists(global.DumpsPath) { + _ = os.MkdirAll(global.DumpsPath, 0o755) + } + dumpFile := path.Join(global.DumpsPath, fmt.Sprintf("%v.dump", time.Now().Unix())) + log.Errorf("出现错误 %v. 详细信息已转储至文件 %v 请连同日志提交给开发者处理", e.Message, dumpFile) + _ = os.WriteFile(dumpFile, e.Dump, 0o644) } }) return c diff --git a/global/fs.go b/global/fs.go index 91d346b1f..f46934442 100644 --- a/global/fs.go +++ b/global/fs.go @@ -32,6 +32,8 @@ const ( VideoPath = "data/videos" // CachePath go-cqhttp使用的缓存目录 CachePath = "data/cache" + // DumpsPath go-cqhttp使用错误转储目录 + DumpsPath = "dumps" ) var ( diff --git a/go.mod b/go.mod index c159c7af7..45ea6c515 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211214084325-ed979508cf8a + github.com/Mrs4s/MiraiGo v0.0.0-20211216072607-5a59cc656c48 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 06f5a9ef6..5da840093 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211214084325-ed979508cf8a h1:hADFsj9XtB+A2AZAf99vMW870YpzjPDA30+mqCXJDCo= -github.com/Mrs4s/MiraiGo v0.0.0-20211214084325-ed979508cf8a/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211216072607-5a59cc656c48 h1:ZXofuoHtO8bWDSEAFJyho5qJyBjOdbLv/vDSn0d/rOI= +github.com/Mrs4s/MiraiGo v0.0.0-20211216072607-5a59cc656c48/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= From cb16c08ac8acc30699ae044e756fd13beea9ab33 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Fri, 17 Dec 2021 02:50:37 +0800 Subject: [PATCH 204/612] fix: download & delete group file api error --- coolq/api.go | 3 ++- go.mod | 2 +- go.sum | 4 ++-- modules/api/api.go | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 9dab51c49..cb381d25e 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -568,6 +568,7 @@ func (bot *CQBot) CQGetGroupFilesByFolderID(groupID int64, folderID string) glob // // https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%96%87%E4%BB%B6%E8%B5%84%E6%BA%90%E9%93%BE%E6%8E%A5 // @route(get_group_file_url) +// @rename(bus_id->busid) func (bot *CQBot) CQGetGroupFileURL(groupID int64, fileID string, busID int32) global.MSG { url := bot.Client.GetGroupFileUrl(groupID, fileID, busID) if url == "" { @@ -638,7 +639,7 @@ func (bot *CQBot) CQGroupFileDeleteFolder(groupID int64, id string) global.MSG { // CQGroupFileDeleteFile 拓展API-删除群文件 // // @route(delete_group_file) -// @rename(id->file_id) +// @rename(id->file_id, bus_id->busid) func (bot *CQBot) CQGroupFileDeleteFile(groupID int64, id string, busID int32) global.MSG { fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { diff --git a/go.mod b/go.mod index 45ea6c515..7f5e5aea2 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211216072607-5a59cc656c48 + github.com/Mrs4s/MiraiGo v0.0.0-20211216171228-a5410846cb5f github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 5da840093..db4186094 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211216072607-5a59cc656c48 h1:ZXofuoHtO8bWDSEAFJyho5qJyBjOdbLv/vDSn0d/rOI= -github.com/Mrs4s/MiraiGo v0.0.0-20211216072607-5a59cc656c48/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211216171228-a5410846cb5f h1:xM45KYUWkSGmImTpalgQJYWHVz7/eSSOoOnrRuUeb/4= +github.com/Mrs4s/MiraiGo v0.0.0-20211216171228-a5410846cb5f/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= diff --git a/modules/api/api.go b/modules/api/api.go index 1eaee6216..b22d12528 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -61,7 +61,7 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "delete_group_file": p0 := p.Get("group_id").Int() p1 := p.Get("file_id").String() - p2 := int32(p.Get("bus_id").Int()) + p2 := int32(p.Get("busid").Int()) return c.bot.CQGroupFileDeleteFile(p0, p1, p2) case "delete_group_folder": p0 := p.Get("group_id").Int() @@ -99,7 +99,7 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "get_group_file_url": p0 := p.Get("group_id").Int() p1 := p.Get("file_id").String() - p2 := int32(p.Get("bus_id").Int()) + p2 := int32(p.Get("busid").Int()) return c.bot.CQGetGroupFileURL(p0, p1, p2) case "get_group_files_by_folder": p0 := p.Get("group_id").Int() From 2f1077e7953ed337b6c94d83ce3aa8eb2b21cefb Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 17 Dec 2021 12:36:00 +0800 Subject: [PATCH 205/612] feat: expand forward message if received a nested forward, the content would be an array rather than a `[CQ:forward]` --- coolq/api.go | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index cb381d25e..74d511b0c 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1515,20 +1515,31 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG { if m == nil { return Failed(100, "MSG_NOT_FOUND", "消息不存在") } - r := make([]global.MSG, 0, len(m.Nodes)) - for _, n := range m.Nodes { - bot.checkMedia(n.Message, 0) - r = append(r, global.MSG{ - "sender": global.MSG{ - "user_id": n.SenderId, - "nickname": n.SenderName, - }, - "time": n.Time, - "content": ToFormattedMessage(n.Message, MessageSource{SourceType: MessageSourceGroup}, false), - }) + + var transformNodes func(nodes []*message.ForwardNode) []global.MSG + transformNodes = func(nodes []*message.ForwardNode) []global.MSG { + r := make([]global.MSG, len(nodes)) + for i, n := range nodes { + bot.checkMedia(n.Message, 0) + content := ToFormattedMessage(n.Message, MessageSource{SourceType: MessageSourceGroup}, false) + if len(n.Message) == 1 { + if forward, ok := n.Message[0].(*message.ForwardMessage); ok { + content = transformNodes(forward.Nodes) + } + } + r[i] = global.MSG{ + "sender": global.MSG{ + "user_id": n.SenderId, + "nickname": n.SenderName, + }, + "time": n.Time, + "content": content, + } + } + return r } return OK(global.MSG{ - "messages": r, + "messages": transformNodes(m.Nodes), }) } From 5a180cb8c2af08d73a23cf6463e843cba150c866 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 17 Dec 2021 12:38:52 +0800 Subject: [PATCH 206/612] dep: update MiraiGo --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7f5e5aea2..fe62b0a51 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211216171228-a5410846cb5f + github.com/Mrs4s/MiraiGo v0.0.0-20211217042714-dc66c61d57d8 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index db4186094..2307ec6ad 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211216171228-a5410846cb5f h1:xM45KYUWkSGmImTpalgQJYWHVz7/eSSOoOnrRuUeb/4= -github.com/Mrs4s/MiraiGo v0.0.0-20211216171228-a5410846cb5f/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211217042714-dc66c61d57d8 h1:cQ2gmXWcG7SBOTeBSD2RVyohSjCL1FmsLDVbeBX2a7E= +github.com/Mrs4s/MiraiGo v0.0.0-20211217042714-dc66c61d57d8/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= From 20ce02f0c98e044182b3366aa0b16557313e54e9 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 17 Dec 2021 12:44:53 +0800 Subject: [PATCH 207/612] ci: fix lint by adding `// nolint` --- coolq/api.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coolq/api.go b/coolq/api.go index 74d511b0c..55c1929c1 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1596,6 +1596,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "guild_id": fU64(source.PrimaryID), "reactions": []int{}, } + // nolint: exhaustive switch source.SourceType { case MessageSourceGuildChannel: m["channel_id"] = fU64(source.SubID) From a42e33f46997c47203bd35e7eb221388004b74d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 15:56:49 +0800 Subject: [PATCH 208/612] =?UTF-8?q?feat:=20=E5=A4=84=E7=90=86=E6=9C=AA?= =?UTF-8?q?=E6=89=BE=E5=88=B0=E5=90=8E=E7=BC=80=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/summary.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qqrobot/qinglong/summary.go b/qqrobot/qinglong/summary.go index 7a05e73bc..a6d46c151 100644 --- a/qqrobot/qinglong/summary.go +++ b/qqrobot/qinglong/summary.go @@ -26,7 +26,11 @@ func parseSummary(info *JdCookieInfo, logFilePath string) string { if prefixIndex == -1 { return "" } - suffixIndex := prefixIndex + strings.Index(content[prefixIndex:], suffix) + relativeSuffixIndex := strings.Index(content[prefixIndex:], suffix) + if relativeSuffixIndex == -1 { + return "" + } + suffixIndex := prefixIndex + relativeSuffixIndex summary := content[prefixIndex:suffixIndex] From db87abebfce0468f24cf276514aafe0b096b2679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 15:57:11 +0800 Subject: [PATCH 209/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E4=BA=AC=E4=B8=9C=E8=B4=A6=E5=8F=B7=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/expired.go | 38 ++++++++++++++ qqrobot/qinglong/expired_test.go | 20 +++++++ qqrobot/qinglong/query.go | 32 ++++++++++++ qqrobot/qinglong/query_test.go | 14 +++++ .../2021-12-17-12-00-01.log | 52 +++++++++++++++++++ 5 files changed, 156 insertions(+) create mode 100644 qqrobot/qinglong/expired.go create mode 100644 qqrobot/qinglong/expired_test.go create mode 100644 qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log diff --git a/qqrobot/qinglong/expired.go b/qqrobot/qinglong/expired.go new file mode 100644 index 000000000..063534bb0 --- /dev/null +++ b/qqrobot/qinglong/expired.go @@ -0,0 +1,38 @@ +package qinglong + +import ( + "fmt" + "io/ioutil" + "strings" + + "github.com/Mrs4s/go-cqhttp/global" +) + +func parseCookieExpired(info *JdCookieInfo, logFilePath string) string { + if !global.PathExists(logFilePath) { + return "" + } + + contentBytes, err := ioutil.ReadFile(logFilePath) + if err != nil { + return "" + } + content := string(contentBytes) + + prefix := fmt.Sprintf("京东账号%v : ", info.Index) + suffix := "\n\n" + + prefixIndex := strings.Index(content, prefix) + if prefixIndex == -1 { + return "" + } + relativeSuffixIndex := strings.Index(content[prefixIndex:], suffix) + if relativeSuffixIndex == -1 { + return "" + } + suffixIndex := prefixIndex + relativeSuffixIndex + + result := content[prefixIndex:suffixIndex] + + return result +} diff --git a/qqrobot/qinglong/expired_test.go b/qqrobot/qinglong/expired_test.go new file mode 100644 index 000000000..1bf86ef73 --- /dev/null +++ b/qqrobot/qinglong/expired_test.go @@ -0,0 +1,20 @@ +package qinglong + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_parseCookieExpired(t *testing.T) { + logPath := getPath("log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log") + + info := QueryCookieInfo("1") + assert.Equal(t, "京东账号1 : pin_1 状态正常!", parseCookieExpired(info, logPath)) + + info = QueryCookieInfo("2") + assert.Equal(t, "京东账号2 : pin_2 已失效,自动禁用成功!", parseCookieExpired(info, logPath)) + + info = QueryCookieInfo("3") + assert.Equal(t, "京东账号3 : pin_3 已失效,已禁用!", parseCookieExpired(info, logPath)) +} diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 849004f46..ab835cae6 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -94,3 +94,35 @@ func QuerySummary(info *JdCookieInfo) string { return "" } + +// QueryCookieExpired 查询账号是否已过期 +func QueryCookieExpired(info *JdCookieInfo) string { + if info == nil { + return "" + } + + checkCookieDir := getPath("log/shufflewzc_faker2_jd_CheckCK") + logFiles, err := ioutil.ReadDir(checkCookieDir) + if err != nil { + logger.Errorf("read log dir failed, err=%v", err) + return "" + } + + if len(logFiles) == 0 { + return "" + } + + // 按时间逆序排列 + sort.Slice(logFiles, func(i, j int) bool { + return logFiles[i].Name() > logFiles[j].Name() + }) + + // 只取最新的一个日志 + latestLogFile := logFiles[0] + result := parseCookieExpired(info, filepath.Join(checkCookieDir, latestLogFile.Name())) + if result != "" { + return result + } + + return "" +} diff --git a/qqrobot/qinglong/query_test.go b/qqrobot/qinglong/query_test.go index 53280a109..bc2354bc9 100644 --- a/qqrobot/qinglong/query_test.go +++ b/qqrobot/qinglong/query_test.go @@ -51,3 +51,17 @@ func TestQuerySummary(t *testing.T) { info = QueryCookieInfo("3") assert.Empty(t, QuerySummary(info)) } + +func TestQueryCookieExpired(t *testing.T) { + info := QueryCookieInfo("1") + assert.NotEmpty(t, QueryCookieExpired(info)) + + info = QueryCookieInfo("2") + assert.NotEmpty(t, QueryCookieExpired(info)) + + info = QueryCookieInfo("3") + assert.NotEmpty(t, QueryCookieExpired(info)) + + info = QueryCookieInfo("4") + assert.Empty(t, QueryCookieExpired(info)) +} diff --git a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log new file mode 100644 index 000000000..5742f8153 --- /dev/null +++ b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log @@ -0,0 +1,52 @@ +开始检测【京东账号1】pin_1 .... + +账号没有分组 +京东服务器返回空数据 +接口1检测失败,尝试使用接口2.... + +使用X1a0He写的接口加强检测: Cookie有效 + +京东账号1 : pin_1 状态正常! + +等待2秒....... + +开始检测【京东账号2】pin_2 .... + +账号没有分组 +京东服务器返回空数据 +接口1检测失败,尝试使用接口2.... + +使用X1a0He写的接口加强检测: Cookie无效 + +京东账号2 : pin_2 已失效,自动禁用成功! + +等待2秒....... + +开始检测【京东账号3】pin_3 .... + +账号没有分组 +京东服务器返回空数据 +接口1检测失败,尝试使用接口2.... + +使用X1a0He写的接口加强检测: Cookie无效 + +京东账号3 : pin_3 已失效,已禁用! + +等待2秒....... + +京东CK检测: +👇👇👇👇👇自动禁用账号👇👇👇👇👇 +【账号2🆔】pin_2 (自动禁用成功!) + + +👇👇👇👇👇失效账号👇👇👇👇👇 +【账号2🆔】pin_2 已失效,自动禁用成功! +【账号3🆔】pin_3 已失效,已禁用. + + + +开始发送通知... +通知标题: 京东CK检测 +正在处理账号Remark..... +处理完成,开始发送通知... +发送go-cqhttp通知调用API失败!! From ee4e212a8d1e9a90f30aa019d9651272a574cdad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 16:05:30 +0800 Subject: [PATCH 210/612] =?UTF-8?q?feat:=20=E6=9C=BA=E5=99=A8=E4=BA=BA?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E6=9F=A5=E8=AF=A2=E8=BF=87=E6=9C=9F=E7=9A=84?= =?UTF-8?q?=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/command.go | 11 +++++++++++ qqrobot/config.go | 15 ++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/qqrobot/command.go b/qqrobot/command.go index 828b37cbb..67354d641 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -131,6 +131,17 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms summary := qinglong.QuerySummary(cookieInfo) extraReplies = append(extraReplies, message.NewText(summary)) + } else if match = commandRegexQinglongCookieExpired.FindStringSubmatch(commandStr); len(match) == len(commandRegexQinglongCookieExpired.SubexpNames()) { + // full_match|参数 + queryParam := match[1] + + cookieInfo := qinglong.QueryCookieInfo(queryParam) + if cookieInfo == nil { + return fmt.Sprintf("未找到相关cookie:%v", queryParam), nil, nil + } + + result := qinglong.QueryCookieExpired(cookieInfo) + extraReplies = append(extraReplies, message.NewText(result)) } else { return "", nil, errors.Errorf("没有找到该指令哦") } diff --git a/qqrobot/config.go b/qqrobot/config.go index df3b616d0..0ae22411b 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -91,13 +91,14 @@ const ( ) var ( - commandregexAddwhitelist = regexp.MustCompile(`\s*AddWhiteList\s+(?P.+?)\s+(?P\d+)`) - commandregexRulenamelist = regexp.MustCompile(`RuleNameList`) - commandregexBuycard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) - commandregexQuerycard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) - commandRegexMusic = regexp.MustCompile(`.*(点歌|听歌|点首|来首|想听|要听)\s*(?P.+)`) - commandRegexQinglongChart = regexp.MustCompile(`统计图\s*(?P.+)`) - commandRegexQinglongSummary = regexp.MustCompile(`概览\s*(?P.+)`) + commandregexAddwhitelist = regexp.MustCompile(`\s*AddWhiteList\s+(?P.+?)\s+(?P\d+)`) + commandregexRulenamelist = regexp.MustCompile(`RuleNameList`) + commandregexBuycard = regexp.MustCompile(`\s*我想要给(?P\d+)买一张(?P[1-3]-[1-4])`) + commandregexQuerycard = regexp.MustCompile(`\s*给我康康现在还有哪些卡`) + commandRegexMusic = regexp.MustCompile(`.*(点歌|听歌|点首|来首|想听|要听)\s*(?P.+)`) + commandRegexQinglongChart = regexp.MustCompile(`统计图\s*(?P.+)`) + commandRegexQinglongSummary = regexp.MustCompile(`概览\s*(?P.+)`) + commandRegexQinglongCookieExpired = regexp.MustCompile(`过期\s*(?P.+)`) ) // RuleConfig 规则配置 From faf62a5acf5c824593141dd31edaf33fd78b1803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 16:21:43 +0800 Subject: [PATCH 211/612] =?UTF-8?q?feat:=20=E6=9F=A5=E8=AF=A2=E6=A6=82?= =?UTF-8?q?=E8=A7=88=E5=A2=9E=E5=8A=A0=E6=A3=80=E6=B5=8B=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=B7=B2=E8=BF=87=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index ab835cae6..344544849 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -72,6 +72,11 @@ func QuerySummary(info *JdCookieInfo) string { return "" } + // 检查是否过期 + if isCookieExpired(info) { + return "该账号已过期,请参照群文件教程更新cookie" + } + summaryDir := getPath("log/shufflewzc_faker2_jd_bean_change") logFiles, err := ioutil.ReadDir(summaryDir) if err != nil { @@ -126,3 +131,8 @@ func QueryCookieExpired(info *JdCookieInfo) string { return "" } + +func isCookieExpired(info *JdCookieInfo) bool { + result := QueryCookieExpired(info) + return strings.Contains(result, "已失效") +} From e9783df3e621fc012652bf8c889b980c0da76e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 16:29:35 +0800 Subject: [PATCH 212/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E8=A7=A3=E6=9E=90=E7=9A=84=E8=B4=A6=E5=8F=B7=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=EF=BC=8C=E6=96=B9=E4=BE=BF=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/command.go | 3 +++ qqrobot/qinglong/env.go | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/qqrobot/command.go b/qqrobot/command.go index 67354d641..3b80751be 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -120,6 +120,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms chartPath := qinglong.QueryChartPath(cookieInfo) extraReplies = append(extraReplies, &coolq.LocalImageElement{File: chartPath}) + extraReplies = append(extraReplies, message.NewText(cookieInfo.ToChatMessage())) } else if match = commandRegexQinglongSummary.FindStringSubmatch(commandStr); len(match) == len(commandRegexQinglongSummary.SubexpNames()) { // full_match|参数 queryParam := match[1] @@ -131,6 +132,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms summary := qinglong.QuerySummary(cookieInfo) extraReplies = append(extraReplies, message.NewText(summary)) + extraReplies = append(extraReplies, message.NewText(cookieInfo.ToChatMessage())) } else if match = commandRegexQinglongCookieExpired.FindStringSubmatch(commandStr); len(match) == len(commandRegexQinglongCookieExpired.SubexpNames()) { // full_match|参数 queryParam := match[1] @@ -142,6 +144,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms result := qinglong.QueryCookieExpired(cookieInfo) extraReplies = append(extraReplies, message.NewText(result)) + extraReplies = append(extraReplies, message.NewText(cookieInfo.ToChatMessage())) } else { return "", nil, errors.Errorf("没有找到该指令哦") } diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index d243d4a0f..ee8e3abd6 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -3,6 +3,7 @@ package qinglong import ( "bufio" "encoding/json" + "fmt" "os" "strings" ) @@ -26,6 +27,18 @@ type JdCookieInfo struct { Remark string // remark } +// ToChatMessage 转换为聊天消息 +func (cookieInfo *JdCookieInfo) ToChatMessage() string { + return fmt.Sprintf("\n"+ + "\n序号: %v"+ + "\npt_pin: %v"+ + "\n备注: %v", + cookieInfo.Index, + cookieInfo.PtPin, + cookieInfo.Remark, + ) +} + // ParseJdCookie 解析 db/env.db 和 config/env.sh,获取各个京东cookie的序号和备注信息 func ParseJdCookie() (map[string]*JdCookieInfo, error) { ptPinToCookieInfo, err := parseEnvDB() From 4a8c8d1cc3785acc6adba529259e9cad98c1f943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 16:47:58 +0800 Subject: [PATCH 213/612] =?UTF-8?q?feat:=20=E6=98=AF=E5=90=A6=E5=B7=B2?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=E6=94=B9=E4=B8=BA=E6=94=BE=E5=88=B0=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E4=BF=A1=E6=81=AF=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 18 +++++++++++++----- qqrobot/qinglong/query.go | 5 ----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index ee8e3abd6..5d44f27b7 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -28,14 +28,22 @@ type JdCookieInfo struct { } // ToChatMessage 转换为聊天消息 -func (cookieInfo *JdCookieInfo) ToChatMessage() string { +func (info *JdCookieInfo) ToChatMessage() string { + expiredInfo := "未过期" + // 检查是否过期 + if isCookieExpired(info) { + expiredInfo = "已过期,请更新cookie" + } return fmt.Sprintf("\n"+ "\n序号: %v"+ "\npt_pin: %v"+ - "\n备注: %v", - cookieInfo.Index, - cookieInfo.PtPin, - cookieInfo.Remark, + "\n备注: %v"+ + "\n状态: %v"+ + "", + info.Index, + info.PtPin, + info.Remark, + expiredInfo, ) } diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 344544849..148b4275d 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -72,11 +72,6 @@ func QuerySummary(info *JdCookieInfo) string { return "" } - // 检查是否过期 - if isCookieExpired(info) { - return "该账号已过期,请参照群文件教程更新cookie" - } - summaryDir := getPath("log/shufflewzc_faker2_jd_bean_change") logFiles, err := ioutil.ReadDir(summaryDir) if err != nil { From 9bcf9189ca1d8009eb52c9641a894dbbd877f20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 16:54:52 +0800 Subject: [PATCH 214/612] =?UTF-8?q?feat:=20=E8=BF=87=E6=9C=9F=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=94=B9=E4=B8=BA=E4=BD=BF=E7=94=A8ptpin=E5=AE=9A?= =?UTF-8?q?=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/expired.go | 6 +++--- qqrobot/qinglong/expired_test.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qqrobot/qinglong/expired.go b/qqrobot/qinglong/expired.go index 063534bb0..2a053d192 100644 --- a/qqrobot/qinglong/expired.go +++ b/qqrobot/qinglong/expired.go @@ -1,7 +1,6 @@ package qinglong import ( - "fmt" "io/ioutil" "strings" @@ -19,7 +18,8 @@ func parseCookieExpired(info *JdCookieInfo, logFilePath string) string { } content := string(contentBytes) - prefix := fmt.Sprintf("京东账号%v : ", info.Index) + prefixToRemove := " : " + prefix := prefixToRemove + info.PtPin suffix := "\n\n" prefixIndex := strings.Index(content, prefix) @@ -32,7 +32,7 @@ func parseCookieExpired(info *JdCookieInfo, logFilePath string) string { } suffixIndex := prefixIndex + relativeSuffixIndex - result := content[prefixIndex:suffixIndex] + result := content[prefixIndex+len(prefixToRemove) : suffixIndex] return result } diff --git a/qqrobot/qinglong/expired_test.go b/qqrobot/qinglong/expired_test.go index 1bf86ef73..03ab171e7 100644 --- a/qqrobot/qinglong/expired_test.go +++ b/qqrobot/qinglong/expired_test.go @@ -10,11 +10,11 @@ func Test_parseCookieExpired(t *testing.T) { logPath := getPath("log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log") info := QueryCookieInfo("1") - assert.Equal(t, "京东账号1 : pin_1 状态正常!", parseCookieExpired(info, logPath)) + assert.Equal(t, "pin_1 状态正常!", parseCookieExpired(info, logPath)) info = QueryCookieInfo("2") - assert.Equal(t, "京东账号2 : pin_2 已失效,自动禁用成功!", parseCookieExpired(info, logPath)) + assert.Equal(t, "pin_2 已失效,自动禁用成功!", parseCookieExpired(info, logPath)) info = QueryCookieInfo("3") - assert.Equal(t, "京东账号3 : pin_3 已失效,已禁用!", parseCookieExpired(info, logPath)) + assert.Equal(t, "pin_3 已失效,已禁用!", parseCookieExpired(info, logPath)) } From 71c154e7f2dfed573f7d0976084c80713ea1f62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 17:12:03 +0800 Subject: [PATCH 215/612] =?UTF-8?q?feat:=20=E6=80=BB=E7=BB=93=E4=B9=9F?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E7=94=A8pt=5Fpin=E5=AE=9A=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 2 +- qqrobot/qinglong/summary.go | 21 ++++++++++++++----- qqrobot/qinglong/summary_test.go | 2 +- .../2021-12-13-09-30-00.log | 2 +- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index 5d44f27b7..6d784df91 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -23,7 +23,7 @@ type EnvDBEntry struct { // JdCookieInfo 所需的京东cookie信息 type JdCookieInfo struct { Index int // 在env.sh中的JD_COOKIE中的顺序,从1开始计数 - PtPin string // pt_pin + PtPin string // pt_pin, note: 定位只能使用这个字段,而不能使用index,因青龙不是依据env.sh来生成index的 Remark string // remark } diff --git a/qqrobot/qinglong/summary.go b/qqrobot/qinglong/summary.go index a6d46c151..27e07bfa0 100644 --- a/qqrobot/qinglong/summary.go +++ b/qqrobot/qinglong/summary.go @@ -19,18 +19,29 @@ func parseSummary(info *JdCookieInfo, logFilePath string) string { } content := string(contentBytes) - prefix := fmt.Sprintf("【账号%v🆔】", info.Index) + blockPrefix := fmt.Sprintf("】%v*********", info.PtPin) + prefix := "【账号" suffix := "\n\n" - prefixIndex := strings.Index(content, prefix) + // 定位唯一区分账号的日志前缀 + blockPrefixIndex := strings.Index(content, blockPrefix) + if blockPrefixIndex == -1 { + return "" + } + + // 定位实际前缀 + prefixIndex := strings.Index(content[blockPrefixIndex:], prefix) if prefixIndex == -1 { return "" } - relativeSuffixIndex := strings.Index(content[prefixIndex:], suffix) - if relativeSuffixIndex == -1 { + prefixIndex += blockPrefixIndex + + // 定位后缀 + suffixIndex := strings.Index(content[prefixIndex:], suffix) + if suffixIndex == -1 { return "" } - suffixIndex := prefixIndex + relativeSuffixIndex + suffixIndex += prefixIndex summary := content[prefixIndex:suffixIndex] diff --git a/qqrobot/qinglong/summary_test.go b/qqrobot/qinglong/summary_test.go index 22b951fbd..084578d55 100644 --- a/qqrobot/qinglong/summary_test.go +++ b/qqrobot/qinglong/summary_test.go @@ -12,7 +12,7 @@ func Test_parseSummary(t *testing.T) { logPath := getPath("log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log") info := QueryCookieInfo("1") - assert.NotEmpty(t, parseSummary(info, logPath)) + assert.Contains(t, parseSummary(info, logPath), "测试账号-1") info = QueryCookieInfo("3") assert.Empty(t, parseSummary(info, logPath)) diff --git a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log index 1397eb843..62196b26f 100644 --- a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log +++ b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log @@ -1,4 +1,4 @@ -******开始查询【京东账号1】pin_1 ********* +******开始查询【京东账号1】pin_1********* 京东服务器返回空数据,将无法获取等级及VIP信息 正在获取领现金任务签名... From c0fd5090e796b717d90dbc64676608f6d3f310e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 17:14:31 +0800 Subject: [PATCH 216/612] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E8=BF=87?= =?UTF-8?q?=E6=9C=9F=E5=AE=9A=E4=BD=8D=E7=9A=84=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/expired.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/qqrobot/qinglong/expired.go b/qqrobot/qinglong/expired.go index 2a053d192..f8a1e55a7 100644 --- a/qqrobot/qinglong/expired.go +++ b/qqrobot/qinglong/expired.go @@ -22,17 +22,21 @@ func parseCookieExpired(info *JdCookieInfo, logFilePath string) string { prefix := prefixToRemove + info.PtPin suffix := "\n\n" + // 定位前缀 prefixIndex := strings.Index(content, prefix) if prefixIndex == -1 { return "" } - relativeSuffixIndex := strings.Index(content[prefixIndex:], suffix) - if relativeSuffixIndex == -1 { + prefixIndex += len(prefixToRemove) + + // 定位后缀 + suffixIndex := strings.Index(content[prefixIndex:], suffix) + if suffixIndex == -1 { return "" } - suffixIndex := prefixIndex + relativeSuffixIndex + suffixIndex += prefixIndex - result := content[prefixIndex+len(prefixToRemove) : suffixIndex] + result := content[prefixIndex:suffixIndex] return result } From 5c8254287c9dae609f9e15ad946db08955eca0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 17:53:08 +0800 Subject: [PATCH 217/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=8C=85=E5=90=AB=E4=B8=AD=E6=96=87url=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E7=9A=84pt=5Fpin=E5=9C=A8=E6=97=A5=E5=BF=97=E4=B8=AD?= =?UTF-8?q?=E4=BC=9A=E6=89=93=E5=8D=B0=E8=A7=A3=E7=A0=81=E5=90=8E=E7=9A=84?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 12 ++++++++++ qqrobot/qinglong/expired.go | 2 +- qqrobot/qinglong/expired_test.go | 4 ++++ qqrobot/qinglong/summary.go | 2 +- qqrobot/qinglong/summary_test.go | 4 ++++ qqrobot/qinglong/test_data/config/env.sh | 2 +- qqrobot/qinglong/test_data/db/env.db | 1 + .../2021-12-17-12-00-01.log | 12 ++++++++++ .../2021-12-13-09-30-00.log | 23 +++++++++++++++++++ 9 files changed, 59 insertions(+), 3 deletions(-) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index 6d784df91..74b97dd65 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -4,6 +4,7 @@ import ( "bufio" "encoding/json" "fmt" + "net/url" "os" "strings" ) @@ -27,6 +28,17 @@ type JdCookieInfo struct { Remark string // remark } +// QueryUnescapedPtPin 返回url解码后的pt_pin +func (info *JdCookieInfo) QueryUnescapedPtPin() string { + // 由于可能部分pt_pin包含中文的url编码,而日志中均会打印为解码后的,需要解码一次 + pin, err := url.QueryUnescape(info.PtPin) + if err != nil { + return info.PtPin + } + + return pin +} + // ToChatMessage 转换为聊天消息 func (info *JdCookieInfo) ToChatMessage() string { expiredInfo := "未过期" diff --git a/qqrobot/qinglong/expired.go b/qqrobot/qinglong/expired.go index f8a1e55a7..99718aaf3 100644 --- a/qqrobot/qinglong/expired.go +++ b/qqrobot/qinglong/expired.go @@ -19,7 +19,7 @@ func parseCookieExpired(info *JdCookieInfo, logFilePath string) string { content := string(contentBytes) prefixToRemove := " : " - prefix := prefixToRemove + info.PtPin + prefix := prefixToRemove + info.QueryUnescapedPtPin() suffix := "\n\n" // 定位前缀 diff --git a/qqrobot/qinglong/expired_test.go b/qqrobot/qinglong/expired_test.go index 03ab171e7..8c22a0bbe 100644 --- a/qqrobot/qinglong/expired_test.go +++ b/qqrobot/qinglong/expired_test.go @@ -1,6 +1,7 @@ package qinglong import ( + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -17,4 +18,7 @@ func Test_parseCookieExpired(t *testing.T) { info = QueryCookieInfo("3") assert.Equal(t, "pin_3 已失效,已禁用!", parseCookieExpired(info, logPath)) + + info = QueryCookieInfo(url.QueryEscape("中文pin")) + assert.Equal(t, "中文pin 状态正常!", parseCookieExpired(info, logPath)) } diff --git a/qqrobot/qinglong/summary.go b/qqrobot/qinglong/summary.go index 27e07bfa0..79e5edf5c 100644 --- a/qqrobot/qinglong/summary.go +++ b/qqrobot/qinglong/summary.go @@ -19,7 +19,7 @@ func parseSummary(info *JdCookieInfo, logFilePath string) string { } content := string(contentBytes) - blockPrefix := fmt.Sprintf("】%v*********", info.PtPin) + blockPrefix := fmt.Sprintf("】%v*********", info.QueryUnescapedPtPin()) prefix := "【账号" suffix := "\n\n" diff --git a/qqrobot/qinglong/summary_test.go b/qqrobot/qinglong/summary_test.go index 084578d55..a8be42d77 100644 --- a/qqrobot/qinglong/summary_test.go +++ b/qqrobot/qinglong/summary_test.go @@ -1,6 +1,7 @@ package qinglong import ( + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -14,6 +15,9 @@ func Test_parseSummary(t *testing.T) { info := QueryCookieInfo("1") assert.Contains(t, parseSummary(info, logPath), "测试账号-1") + info = QueryCookieInfo(url.QueryEscape("中文pin")) + assert.Contains(t, parseSummary(info, logPath), "中文名字") + info = QueryCookieInfo("3") assert.Empty(t, parseSummary(info, logPath)) } diff --git a/qqrobot/qinglong/test_data/config/env.sh b/qqrobot/qinglong/test_data/config/env.sh index 3cf4c139a..f274c197f 100644 --- a/qqrobot/qinglong/test_data/config/env.sh +++ b/qqrobot/qinglong/test_data/config/env.sh @@ -1 +1 @@ -export JD_COOKIE="pt_key=key_1;pt_pin=pin_1;&pt_key=key_2;pt_pin=pin_2;&pt_key=key_3;pt_pin=pin_3;" +export JD_COOKIE="pt_key=key_1;pt_pin=pin_1;&pt_key=key_2;pt_pin=pin_2;&pt_key=key_3;pt_pin=pin_3;&pt_key=key_4;pt_pin=%E4%B8%AD%E6%96%87pin;" diff --git a/qqrobot/qinglong/test_data/db/env.db b/qqrobot/qinglong/test_data/db/env.db index ee1d2abd7..a7788bfe9 100644 --- a/qqrobot/qinglong/test_data/db/env.db +++ b/qqrobot/qinglong/test_data/db/env.db @@ -1,2 +1,3 @@ {"value":"pt_key=key_1;pt_pin=pin_1;","_id":"1","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"remark=测试账号-1;"} {"value":"pt_key=key_2;pt_pin=pin_2;","_id":"2","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"测试账号-2"} +{"value":"pt_key=key_4;pt_pin=%E4%B8%AD%E6%96%87pin;","_id":"2","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"中文名字"} diff --git a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log index 5742f8153..e6b4ff973 100644 --- a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log +++ b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log @@ -34,6 +34,18 @@ 等待2秒....... +开始检测【京东账号4】中文pin .... + +账号没有分组 +京东服务器返回空数据 +接口1检测失败,尝试使用接口2.... + +使用X1a0He写的接口加强检测: Cookie无效 + +京东账号4 : 中文pin 状态正常! + +等待2秒....... + 京东CK检测: 👇👇👇👇👇自动禁用账号👇👇👇👇👇 【账号2🆔】pin_2 (自动禁用成功!) diff --git a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log index 62196b26f..e70d84272 100644 --- a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log +++ b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log @@ -45,6 +45,29 @@ 【京喜红包】8.88(将过期4.00)元 【京东红包】0.80(将过期0.03)元 +******开始查询【京东账号4】中文pin********* +京东服务器返回空数据,将无法获取等级及VIP信息 +正在获取领现金任务签名... +领现金任务签名获取成功... +【账号2🆔】中文名字 +【账号信息】京享值6529 +【今日京豆】收133豆 +【昨日京豆】收310豆,支2豆 +【当前京豆】4184豆(≈41.84元) +【京喜牧场】160枚鸡蛋 +【极速金币】110142币(≈11.01元) +【京东赚赚】60200币(≈6.02元) +【京东秒杀】6854币(≈6.85元) +【其他信息】汪汪:19级,领现金:3.4元 +【东东农场】16枚鸡蛋(61%,9天) +【京喜工厂】鸡蛋挂面1kg(84%,1天) +【东东工厂】小米11青春版8GB+256GB(10%,剩29) +【东东萌宠】3层抽纸巾30包(14%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】9.68(总过期4.03)元 +【京喜红包】8.88(将过期4.00)元 +【京东红包】0.80(将过期0.03)元 + 开始发送通知... 通知标题: 京东资产变动 正在处理账号Remark..... From 45f501468bf3e6b4b08823d36c7f7bdb6ad5913f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 18:02:07 +0800 Subject: [PATCH 218/612] =?UTF-8?q?test:=20=E4=BF=AE=E6=AD=A3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env_test.go | 6 +++--- qqrobot/qinglong/query_test.go | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/qqrobot/qinglong/env_test.go b/qqrobot/qinglong/env_test.go index 6d8b21b78..75c53baf0 100644 --- a/qqrobot/qinglong/env_test.go +++ b/qqrobot/qinglong/env_test.go @@ -9,7 +9,7 @@ import ( func TestParseJdCookie(t *testing.T) { ptPinToCookieInfo, err := ParseJdCookie() assert.NoError(t, err) - assert.Equal(t, 3, len(ptPinToCookieInfo)) + assert.Equal(t, 4, len(ptPinToCookieInfo)) // 普通账号 ck1, ok := ptPinToCookieInfo["pin_1"] @@ -37,7 +37,7 @@ func TestParseJdCookie(t *testing.T) { func Test_parseEnvDB(t *testing.T) { ptPinToCookieInfo, err := parseEnvDB() assert.NoError(t, err) - assert.Equal(t, 2, len(ptPinToCookieInfo)) + assert.Equal(t, 3, len(ptPinToCookieInfo)) assert.NotNil(t, ptPinToCookieInfo["pin_1"]) assert.Nil(t, ptPinToCookieInfo["pin_3"]) @@ -46,7 +46,7 @@ func Test_parseEnvDB(t *testing.T) { func Test_parseEnvSh(t *testing.T) { ptPinToIndex, err := parseEnvSh() assert.NoError(t, err) - assert.Equal(t, 3, len(ptPinToIndex)) + assert.Equal(t, 4, len(ptPinToIndex)) assert.Equal(t, 1, ptPinToIndex["pin_1"]) assert.Equal(t, 3, ptPinToIndex["pin_3"]) diff --git a/qqrobot/qinglong/query_test.go b/qqrobot/qinglong/query_test.go index bc2354bc9..b3e3c1211 100644 --- a/qqrobot/qinglong/query_test.go +++ b/qqrobot/qinglong/query_test.go @@ -63,5 +63,8 @@ func TestQueryCookieExpired(t *testing.T) { assert.NotEmpty(t, QueryCookieExpired(info)) info = QueryCookieInfo("4") + assert.NotEmpty(t, QueryCookieExpired(info)) + + info = QueryCookieInfo("99999") assert.Empty(t, QueryCookieExpired(info)) } From ad47712bc3f6224ea882d5111e79179f10cb47b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 18:08:53 +0800 Subject: [PATCH 219/612] =?UTF-8?q?feat:=20=E5=BA=9F=E5=BC=83=E5=AE=B9?= =?UTF-8?q?=E6=98=93=E5=87=BA=E9=97=AE=E9=A2=98=E7=9A=84=E5=BA=8F=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/command.go | 6 +++--- qqrobot/qinglong/env.go | 11 ++--------- qqrobot/qinglong/query.go | 9 --------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/qqrobot/command.go b/qqrobot/command.go index 3b80751be..84d2a006e 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -115,7 +115,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms cookieInfo := qinglong.QueryCookieInfo(queryParam) if cookieInfo == nil { - return fmt.Sprintf("未找到相关cookie:%v", queryParam), nil, nil + return fmt.Sprintf("未找到相关cookie:%v,请使用 pt_pin 或者 备注 来进行查询", queryParam), nil, nil } chartPath := qinglong.QueryChartPath(cookieInfo) @@ -127,7 +127,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms cookieInfo := qinglong.QueryCookieInfo(queryParam) if cookieInfo == nil { - return fmt.Sprintf("未找到相关cookie:%v", queryParam), nil, nil + return fmt.Sprintf("未找到相关cookie:%v,请使用 pt_pin 或者 备注 来进行查询", queryParam), nil, nil } summary := qinglong.QuerySummary(cookieInfo) @@ -139,7 +139,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms cookieInfo := qinglong.QueryCookieInfo(queryParam) if cookieInfo == nil { - return fmt.Sprintf("未找到相关cookie:%v", queryParam), nil, nil + return fmt.Sprintf("未找到相关cookie:%v,请使用 pt_pin 或者 备注 来进行查询", queryParam), nil, nil } result := qinglong.QueryCookieExpired(cookieInfo) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index 74b97dd65..c7ace6c91 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -23,7 +23,6 @@ type EnvDBEntry struct { // JdCookieInfo 所需的京东cookie信息 type JdCookieInfo struct { - Index int // 在env.sh中的JD_COOKIE中的顺序,从1开始计数 PtPin string // pt_pin, note: 定位只能使用这个字段,而不能使用index,因青龙不是依据env.sh来生成index的 Remark string // remark } @@ -47,12 +46,10 @@ func (info *JdCookieInfo) ToChatMessage() string { expiredInfo = "已过期,请更新cookie" } return fmt.Sprintf("\n"+ - "\n序号: %v"+ "\npt_pin: %v"+ "\n备注: %v"+ "\n状态: %v"+ "", - info.Index, info.PtPin, info.Remark, expiredInfo, @@ -70,14 +67,11 @@ func ParseJdCookie() (map[string]*JdCookieInfo, error) { if err != nil { return nil, err } - for ptPin, index := range ptPinToIndex { + for ptPin, _ := range ptPinToIndex { cookieInfo := ptPinToCookieInfo[ptPin] - if cookieInfo != nil { - cookieInfo.Index = index - } else { + if cookieInfo == nil { // 部分账号可能在env.db中不存在,但是env.sh中有 ptPinToCookieInfo[ptPin] = &JdCookieInfo{ - Index: index, PtPin: ptPin, Remark: ptPin, } @@ -114,7 +108,6 @@ func parseEnvDB() (map[string]*JdCookieInfo, error) { remark := getRemark(envEntry.Remarks) ptPinToCookieInfo[ptPin] = &JdCookieInfo{ - Index: -1, PtPin: ptPin, Remark: remark, } diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 148b4275d..73784a4ac 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -5,7 +5,6 @@ import ( "io/ioutil" "path/filepath" "sort" - "strconv" "strings" logger "github.com/sirupsen/logrus" @@ -28,14 +27,6 @@ func QueryCookieInfo(param string) *JdCookieInfo { return info } - // 尝试序号 - index, _ := strconv.ParseInt(param, 10, 64) - for _, info := range ptPinToCookieInfo { - if info.Index == int(index) { - return info - } - } - // 尝试昵称 for _, info := range ptPinToCookieInfo { remark := getRemark(info.Remark) From 5161b620e58d432a2303b147ac32aec7e0edc0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 18:15:25 +0800 Subject: [PATCH 220/612] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=B8=AD=E5=BA=8F=E5=8F=B7=E7=9B=B8=E5=85=B3=E5=86=85?= =?UTF-8?q?=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/cmd/main.go | 2 +- qqrobot/qinglong/env.go | 2 +- qqrobot/qinglong/env_test.go | 3 --- qqrobot/qinglong/expired_test.go | 6 +++--- qqrobot/qinglong/query_test.go | 21 +++++++++------------ qqrobot/qinglong/summary_test.go | 4 ++-- 6 files changed, 16 insertions(+), 22 deletions(-) diff --git a/qqrobot/qinglong/cmd/main.go b/qqrobot/qinglong/cmd/main.go index eb5499a6a..1a368d04f 100644 --- a/qqrobot/qinglong/cmd/main.go +++ b/qqrobot/qinglong/cmd/main.go @@ -17,7 +17,7 @@ func main() { logger.Infof("") logger.Infof("使用不同方式查询cookie信息") - for _, param := range []string{"", "jd_70a2bcede031c", "1", "风之凌殇"} { + for _, param := range []string{"", "jd_70a2bcede031c", "风之凌殇"} { info = qinglong.QueryCookieInfo(param) logger.Infof("%v: %v", param, info) } diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index c7ace6c91..d57daacd5 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -67,7 +67,7 @@ func ParseJdCookie() (map[string]*JdCookieInfo, error) { if err != nil { return nil, err } - for ptPin, _ := range ptPinToIndex { + for ptPin := range ptPinToIndex { cookieInfo := ptPinToCookieInfo[ptPin] if cookieInfo == nil { // 部分账号可能在env.db中不存在,但是env.sh中有 diff --git a/qqrobot/qinglong/env_test.go b/qqrobot/qinglong/env_test.go index 75c53baf0..64834d382 100644 --- a/qqrobot/qinglong/env_test.go +++ b/qqrobot/qinglong/env_test.go @@ -14,19 +14,16 @@ func TestParseJdCookie(t *testing.T) { // 普通账号 ck1, ok := ptPinToCookieInfo["pin_1"] assert.True(t, ok) - assert.Equal(t, 1, ck1.Index) assert.Equal(t, "测试账号-1", ck1.Remark) // remarks中直接写备注,不带 remark=前缀 和 ;后缀 ck2, ok := ptPinToCookieInfo["pin_2"] assert.True(t, ok) - assert.Equal(t, 2, ck2.Index) assert.Equal(t, "测试账号-2", ck2.Remark) // 仅在env.sh中存在的账号 ck3, ok := ptPinToCookieInfo["pin_3"] assert.True(t, ok) - assert.Equal(t, 3, ck3.Index) assert.Equal(t, "pin_3", ck3.Remark) // 不存在的账号 diff --git a/qqrobot/qinglong/expired_test.go b/qqrobot/qinglong/expired_test.go index 8c22a0bbe..0a0e4dbc5 100644 --- a/qqrobot/qinglong/expired_test.go +++ b/qqrobot/qinglong/expired_test.go @@ -10,13 +10,13 @@ import ( func Test_parseCookieExpired(t *testing.T) { logPath := getPath("log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log") - info := QueryCookieInfo("1") + info := QueryCookieInfo("pin_1") assert.Equal(t, "pin_1 状态正常!", parseCookieExpired(info, logPath)) - info = QueryCookieInfo("2") + info = QueryCookieInfo("pin_2") assert.Equal(t, "pin_2 已失效,自动禁用成功!", parseCookieExpired(info, logPath)) - info = QueryCookieInfo("3") + info = QueryCookieInfo("pin_3") assert.Equal(t, "pin_3 已失效,已禁用!", parseCookieExpired(info, logPath)) info = QueryCookieInfo(url.QueryEscape("中文pin")) diff --git a/qqrobot/qinglong/query_test.go b/qqrobot/qinglong/query_test.go index b3e3c1211..1719e3611 100644 --- a/qqrobot/qinglong/query_test.go +++ b/qqrobot/qinglong/query_test.go @@ -1,6 +1,7 @@ package qinglong import ( + "net/url" "path/filepath" "testing" @@ -20,10 +21,6 @@ func TestQueryCookieInfo(t *testing.T) { info = QueryCookieInfo("pin_1") assert.NotNil(t, info) - // 序号 - info = QueryCookieInfo("1") - assert.NotNil(t, info) - // 备注 info = QueryCookieInfo("测试账号-1") assert.NotNil(t, info) @@ -38,33 +35,33 @@ func TestQueryCookieInfo(t *testing.T) { } func TestQueryChartPath(t *testing.T) { - info := QueryCookieInfo("1") + info := QueryCookieInfo("pin_1") chartPath := QueryChartPath(info) expected, _ := filepath.Abs(getPath("log/.bean_chart/chart_pin_1.jpeg")) assert.Equal(t, expected, chartPath) } func TestQuerySummary(t *testing.T) { - info := QueryCookieInfo("1") + info := QueryCookieInfo("pin_1") assert.NotEmpty(t, QuerySummary(info)) - info = QueryCookieInfo("3") + info = QueryCookieInfo("pin_3") assert.Empty(t, QuerySummary(info)) } func TestQueryCookieExpired(t *testing.T) { - info := QueryCookieInfo("1") + info := QueryCookieInfo("pin_1") assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("2") + info = QueryCookieInfo("pin_2") assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("3") + info = QueryCookieInfo("pin_3") assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("4") + info = QueryCookieInfo(url.QueryEscape("中文pin")) assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("99999") + info = QueryCookieInfo("pin_99999") assert.Empty(t, QueryCookieExpired(info)) } diff --git a/qqrobot/qinglong/summary_test.go b/qqrobot/qinglong/summary_test.go index a8be42d77..eabdb0f80 100644 --- a/qqrobot/qinglong/summary_test.go +++ b/qqrobot/qinglong/summary_test.go @@ -12,12 +12,12 @@ import ( func Test_parseSummary(t *testing.T) { logPath := getPath("log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log") - info := QueryCookieInfo("1") + info := QueryCookieInfo("pin_1") assert.Contains(t, parseSummary(info, logPath), "测试账号-1") info = QueryCookieInfo(url.QueryEscape("中文pin")) assert.Contains(t, parseSummary(info, logPath), "中文名字") - info = QueryCookieInfo("3") + info = QueryCookieInfo("pin_3") assert.Empty(t, parseSummary(info, logPath)) } From d7d4add8f28978a0130683262c6e514a9ae277ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 17 Dec 2021 19:27:25 +0800 Subject: [PATCH 221/612] =?UTF-8?q?feat:=20=E9=99=90=E5=88=B6=E6=A6=82?= =?UTF-8?q?=E8=A7=88=E6=90=9C=E7=B4=A2=E7=9A=84=E6=9C=80=E5=A4=A7=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=95=B0=EF=BC=8C=E9=81=BF=E5=85=8D=E6=BD=9C=E5=9C=A8?= =?UTF-8?q?=E6=80=A7=E8=83=BD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 73784a4ac..cf34d2e2f 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -75,8 +75,13 @@ func QuerySummary(info *JdCookieInfo) string { return logFiles[i].Name() > logFiles[j].Name() }) - // 因为有可能最新的日志还在处理中,因此逆序搜索每一个日志,直到搜索到为止 - for _, logFile := range logFiles { + // 因为有可能最新的日志还在处理中,因此逆序搜索一定数目的日志,直到搜索到为止 + const MaxCheckCount = 6 + for idx, logFile := range logFiles { + if idx >= MaxCheckCount { + break + } + summary := parseSummary(info, filepath.Join(summaryDir, logFile.Name())) if summary != "" { return summary From 38000611c38fc93f696acd02018c029230ef9efb Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 17 Dec 2021 21:05:27 +0800 Subject: [PATCH 222/612] dep: update MiraiGo --- cmd/gocq/main.go | 4 ++-- coolq/api.go | 4 ++-- go.mod | 3 ++- go.sum | 6 ++++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index b4220749a..6097b3403 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -280,7 +280,7 @@ func Main() { reLoginLock.Lock() defer reLoginLock.Unlock() times = 1 - if cli.Online { + if cli.Online.Load() { return } log.Warnf("Bot已离线: %v", e.Message) @@ -300,7 +300,7 @@ func Main() { } else { time.Sleep(time.Second) } - if cli.Online { + if cli.Online.Load() { log.Infof("登录已完成") break } diff --git a/coolq/api.go b/coolq/api.go index 55c1929c1..7057ed14a 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1799,8 +1799,8 @@ func (bot *CQBot) CQGetStatus() global.MSG { "app_enabled": true, "plugins_good": nil, "app_good": true, - "online": bot.Client.Online, - "good": bot.Client.Online, + "online": bot.Client.Online.Load(), + "good": bot.Client.Online.Load(), "stat": bot.Client.GetStatistics(), }) } diff --git a/go.mod b/go.mod index fe62b0a51..49c9bd6da 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211217042714-dc66c61d57d8 + github.com/Mrs4s/MiraiGo v0.0.0-20211217130110-cf49727531c5 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 @@ -51,6 +51,7 @@ require ( github.com/xdg-go/scram v1.0.2 // indirect github.com/xdg-go/stringprep v1.0.2 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 // indirect diff --git a/go.sum b/go.sum index 2307ec6ad..087852bd8 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211217042714-dc66c61d57d8 h1:cQ2gmXWcG7SBOTeBSD2RVyohSjCL1FmsLDVbeBX2a7E= -github.com/Mrs4s/MiraiGo v0.0.0-20211217042714-dc66c61d57d8/go.mod h1:YD9gBKkxC9lPPtx3doYXRG26VBkK6YXjrS76cv01C5w= +github.com/Mrs4s/MiraiGo v0.0.0-20211217130110-cf49727531c5 h1:p3343Qyi/sDYRyAKiWDuvGlAx+PuG4k8iuT6Olq9T70= +github.com/Mrs4s/MiraiGo v0.0.0-20211217130110-cf49727531c5/go.mod h1:HI05ardsa5HE1Z2xTw69dytmaqqRWv4ajD5Jpnycqqw= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= @@ -163,6 +163,8 @@ github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.mongodb.org/mongo-driver v1.7.4 h1:sllcioag8Mec0LYkftYWq+cKNPIR4Kqq3iv9ZXY0g/E= go.mongodb.org/mongo-driver v1.7.4/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= From 7785c819fb066765bee27461561a8814707621a8 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Fri, 17 Dec 2021 23:29:42 +0800 Subject: [PATCH 223/612] doc: guild api `get_topic_channel_feeds` --- coolq/feed.go | 2 +- docs/guild.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/coolq/feed.go b/coolq/feed.go index 0c362df7b..b8b09bc9a 100644 --- a/coolq/feed.go +++ b/coolq/feed.go @@ -20,7 +20,7 @@ func FeedContentsToArrayMessage(contents []topic.IFeedRichContentElement) []glob case *topic.AtElement: m = global.MSG{ "type": "at", - "data": global.MSG{"qq": elem.Id}, + "data": global.MSG{"id": elem.Id, "qq": elem.Id}, } case *topic.EmojiElement: m = global.MSG{ diff --git a/docs/guild.md b/docs/guild.md index e6a2ed20f..93582cfd7 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -220,6 +220,108 @@ RoleInfo: | ------------- | ----- | ---------- | | `message_id` | string | 消息ID | +### 获取话题频道帖子 + +终结点: `/get_topic_channel_feeds` + +**参数** + +| 字段 | 类型 | 说明 | +| ---------- | ----- | ---- | +| `guild_id` | string | 频道ID | +| `channel_id` | string | 子频道ID | + +**响应数据** + +返回 `FeedInfo` 数组 + +FeedInfo: + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `id` | string | 帖子ID | +| `channel_id` | string | 子频道ID | +| `guild_id` | string | 频道ID | +| `create_time` | int64 | 发帖时间 | +| `title` | string | 帖子标题 | +| `sub_title` | string | 帖子副标题 | +| `poster_info` | PosterInfo | 发帖人信息 | +| `resource` | ResourceInfo | 媒体资源信息 | +| `resource.images` | []FeedMedia | 帖子附带的图片列表 | +| `resource.videos` | []FeedMedia | 帖子附带的视频列表 | +| `contents` | []FeedContent | 帖子内容 | + +PosterInfo: + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `tiny_id` | string | 发帖人ID | +| `nickname` | string | 发帖人昵称 | +| `icon_url` | string | 发帖人头像链接 | + +FeedMedia: + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `file_id` | string | 媒体ID | +| `pattern_id` | string | 控件ID?(不确定) | +| `url` | string | 媒体链接 | +| `height` | int32 | 媒体高度 | +| `width` | int32 | 媒体宽度 | + +FeedContent: + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `type` | string | 内容类型 | +| `data` | Data | 内容数据 | + +#### 内容类型列表: + +| 类型 | 说明 | +| ----- | ---------- | +| `text` | 文本 | +| `face` | 表情 | +| `at` | At | +| `url_quote` | 链接引用 | +| `channel_quote` | 子频道引用 | + +#### 内容类型对应数据列表: + +- `text` + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `text` | string | 文本内容 | + +- `face` + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `id` | string | 表情ID | + +- `at` + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `id` | string | 目标ID | +| `qq` | string | 目标ID, 为确保和 `array message` 的一致性保留 | + +- `url_quote` + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `display_text` | string | 显示文本 | +| `url` | string | 链接 | + +- `channel_quote` + +| 字段 | 类型 | 说明 | +| ------------- | ----- | ---------- | +| `display_text` | string | 显示文本 | +| `guild_id` | string | 频道ID | +| `channel_id` | string | 子频道ID | + ## 事件 ### 收到频道消息 From c1212fa955f7effd851b1afd479dca9eeea5e336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 18 Dec 2021 20:40:40 +0800 Subject: [PATCH 224/612] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E8=BF=87?= =?UTF-8?q?=E6=9C=9F=E7=8A=B6=E6=80=81=E7=9A=84=E6=8F=90=E7=A4=BA=E8=AF=AD?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E8=AF=B4=E6=98=8E=E5=90=88=E9=80=82?= =?UTF-8?q?=E4=BC=9A=E6=9B=B4=E6=96=B0=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index d57daacd5..28a614431 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -43,7 +43,7 @@ func (info *JdCookieInfo) ToChatMessage() string { expiredInfo := "未过期" // 检查是否过期 if isCookieExpired(info) { - expiredInfo = "已过期,请更新cookie" + expiredInfo = "已过期,请更新cookie(每六个小时重新检测并自动启用)" } return fmt.Sprintf("\n"+ "\npt_pin: %v"+ From 3f67bee51ed3ca151b4caebe26e88bf1407d118b Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 19 Dec 2021 15:43:48 +0800 Subject: [PATCH 225/612] dep: update MiraiGo may Fixes #1276 --- cmd/gocq/main.go | 16 +--------------- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 6097b3403..01bc77bdc 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -206,21 +206,7 @@ func Main() { time.Sleep(time.Second * 5) } log.Info("开始尝试登录并同步消息...") - log.Infof("使用协议: %v", func() string { - switch client.SystemDeviceInfo.Protocol { - case client.IPad: - return "iPad" - case client.AndroidPhone: - return "Android Phone" - case client.AndroidWatch: - return "Android Watch" - case client.MacOS: - return "MacOS" - case client.QiDian: - return "企点" - } - return "未知" - }()) + log.Infof("使用协议: %s", client.SystemDeviceInfo.Protocol) cli = newClient() isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt isTokenLogin := false diff --git a/go.mod b/go.mod index 49c9bd6da..3da5c9a58 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211217130110-cf49727531c5 + github.com/Mrs4s/MiraiGo v0.0.0-20211219072930-afb81a32aa75 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 087852bd8..33b9dc8df 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211217130110-cf49727531c5 h1:p3343Qyi/sDYRyAKiWDuvGlAx+PuG4k8iuT6Olq9T70= -github.com/Mrs4s/MiraiGo v0.0.0-20211217130110-cf49727531c5/go.mod h1:HI05ardsa5HE1Z2xTw69dytmaqqRWv4ajD5Jpnycqqw= +github.com/Mrs4s/MiraiGo v0.0.0-20211219072930-afb81a32aa75 h1:4xgd1/lhAKHv/2sAbxbR38hDwt/HfOiWlVOqSJc544E= +github.com/Mrs4s/MiraiGo v0.0.0-20211219072930-afb81a32aa75/go.mod h1:HI05ardsa5HE1Z2xTw69dytmaqqRWv4ajD5Jpnycqqw= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= From 897119cbcae9ebd84587be857b03d5841d63d4b3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 19 Dec 2021 15:48:07 +0800 Subject: [PATCH 226/612] ci: make lint happy --- coolq/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 7057ed14a..82cdba8b9 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1898,7 +1898,7 @@ func (bot *CQBot) CQGetVersionInfo() global.MSG { "version": base.Version, "protocol": func() int { switch client.SystemDeviceInfo.Protocol { - case client.IPad: + case client.Unset, client.IPad: return 0 case client.AndroidPhone: return 1 From c775d91e1c9cfef56a6849fe174ff05d049f13e0 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 19 Dec 2021 22:48:09 +0800 Subject: [PATCH 227/612] fix: private reply message serialize --- coolq/cqcode.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 84b2a1372..02974209b 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -76,10 +76,10 @@ type MessageSourceType byte // MessageSourceType 常量 const ( - MessageSourcePrivate MessageSourceType = 0 - MessageSourceGroup MessageSourceType = 1 - MessageSourceGuildChannel MessageSourceType = 2 - MessageSourceGuildDirect MessageSourceType = 3 + MessageSourcePrivate MessageSourceType = 1 << iota + MessageSourceGroup + MessageSourceGuildChannel + MessageSourceGuildDirect ) const ( @@ -111,7 +111,7 @@ func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []glob _, ok := e.(*message.ReplyElement) return ok }) - if reply != nil && source.SourceType == MessageSourceGroup { + if reply != nil && source.SourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) rid := int64(source.PrimaryID) if rid == 0 { @@ -277,7 +277,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. _, ok := e.(*message.ReplyElement) return ok }) - if reply != nil && source.SourceType == MessageSourceGroup { + if reply != nil && source.SourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) rid := int64(source.PrimaryID) if rid == 0 { @@ -672,7 +672,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType MessageSourceT d := make(map[string]string) convertElem := func(e gjson.Result) { t := e.Get("type").Str - if t == "reply" && sourceType == MessageSourceGroup { + if t == "reply" && sourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { if len(r) > 0 { if _, ok := r[0].(*message.ReplyElement); ok { log.Warnf("警告: 一条信息只能包含一个 Reply 元素.") From 9036b0ab012d5b212b0f66c6d729fbd656c0e075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 20 Dec 2021 16:35:26 +0800 Subject: [PATCH 228/612] =?UTF-8?q?feat:=20=E6=B6=88=E6=81=AF=E5=8F=91?= =?UTF-8?q?=E9=80=81=E6=97=B6=E8=A1=A8=E7=A4=BA=E5=A2=9E=E5=8A=A0=E6=89=93?= =?UTF-8?q?=E5=8D=B0ret=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index 901f8ab1d..90f2215bf 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -274,7 +274,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int } if ret == nil || ret.Id == -1 { - log.Warnf("群消息发送失败: 账号可能被风控.\ngroupID= %v 消息内容= %v", groupID, message.ToReadableString(m.Elements)) + log.Warnf("群消息发送失败: 账号可能被风控.\nret= %v\ngroupID= %v 消息内容= %v", ret, groupID, message.ToReadableString(m.Elements)) return -1 } return bot.InsertGroupMessage(ret) From c0a52263e8fe41a993ad2c0b53d0a03eb29576da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 20 Dec 2021 16:45:56 +0800 Subject: [PATCH 229/612] =?UTF-8?q?feat:=20=E6=89=93=E5=8D=B0=E6=9B=B4?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E7=9A=84=E8=B0=83=E8=AF=95=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index 90f2215bf..fa336e988 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -274,7 +274,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int } if ret == nil || ret.Id == -1 { - log.Warnf("群消息发送失败: 账号可能被风控.\nret= %v\ngroupID= %v 消息内容= %v", ret, groupID, message.ToReadableString(m.Elements)) + log.Warnf("群消息发送失败: 账号可能被风控.\nret= %+v\ngroupID= %v 消息内容= %v", ret, groupID, message.ToReadableString(m.Elements)) return -1 } return bot.InsertGroupMessage(ret) From 8e764177afa5a0f6d2c40d1b1106a0bdb09250a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 20 Dec 2021 16:51:50 +0800 Subject: [PATCH 230/612] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/coolq/bot.go b/coolq/bot.go index fa336e988..3e5296480 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -254,6 +254,8 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int var ret *message.GroupMessage + log.Warnf("调试日志:准备发送消息") + // 魔改一下,兼容过长的消息,使得青龙面板那边可以发送很多个账号的统计信息 msgLen := message.EstimateLength(m.Elements) if msgLen <= MaxMessageSize { @@ -273,6 +275,8 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int } } + log.Warnf("调试日志:完成发送消息") + if ret == nil || ret.Id == -1 { log.Warnf("群消息发送失败: 账号可能被风控.\nret= %+v\ngroupID= %v 消息内容= %v", ret, groupID, message.ToReadableString(m.Elements)) return -1 From 8694d5ebb2ca29c060b40291ca0f49877a3706c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 20 Dec 2021 17:06:34 +0800 Subject: [PATCH 231/612] =?UTF-8?q?test:=20=E7=A7=BB=E9=99=A4=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 3e5296480..901f8ab1d 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -254,8 +254,6 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int var ret *message.GroupMessage - log.Warnf("调试日志:准备发送消息") - // 魔改一下,兼容过长的消息,使得青龙面板那边可以发送很多个账号的统计信息 msgLen := message.EstimateLength(m.Elements) if msgLen <= MaxMessageSize { @@ -275,10 +273,8 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int } } - log.Warnf("调试日志:完成发送消息") - if ret == nil || ret.Id == -1 { - log.Warnf("群消息发送失败: 账号可能被风控.\nret= %+v\ngroupID= %v 消息内容= %v", ret, groupID, message.ToReadableString(m.Elements)) + log.Warnf("群消息发送失败: 账号可能被风控.\ngroupID= %v 消息内容= %v", groupID, message.ToReadableString(m.Elements)) return -1 } return bot.InsertGroupMessage(ret) From dba6932ca1476f3b872d9f59bf68460c5d1e1cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 20 Dec 2021 17:14:50 +0800 Subject: [PATCH 232/612] =?UTF-8?q?feat:=20=E8=A1=A5=E5=85=85=E8=AF=B4?= =?UTF-8?q?=E6=98=8Eid=E4=B8=BA-1=E7=9A=84=E5=8F=A6=E4=B8=80=E7=A7=8D?= =?UTF-8?q?=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index 901f8ab1d..d0401e028 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -274,7 +274,10 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int } if ret == nil || ret.Id == -1 { - log.Warnf("群消息发送失败: 账号可能被风控.\ngroupID= %v 消息内容= %v", groupID, message.ToReadableString(m.Elements)) + log.Warnf("群消息发送失败: 账号可能被风控."+ + "(如果消息发送很慢,同时 ctrl+c 退出也很慢,也有可能是DNS问题,推荐改成 101.6.6.6, 223.6.6.6, 114.114.114.114 这三个一起)"+ + "\ngroupID= %v 消息内容= %v", + groupID, message.ToReadableString(m.Elements)) return -1 } return bot.InsertGroupMessage(ret) From 481523bd67e370a2819d127806120092cfc53a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 21 Dec 2021 15:19:03 +0800 Subject: [PATCH 233/612] =?UTF-8?q?test:=20=E8=B0=83=E6=95=B4=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=A8=8B=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/cmd/main.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qqrobot/qinglong/cmd/main.go b/qqrobot/qinglong/cmd/main.go index 1a368d04f..4d06ad72b 100644 --- a/qqrobot/qinglong/cmd/main.go +++ b/qqrobot/qinglong/cmd/main.go @@ -17,15 +17,17 @@ func main() { logger.Infof("") logger.Infof("使用不同方式查询cookie信息") - for _, param := range []string{"", "jd_70a2bcede031c", "风之凌殇"} { + for _, param := range []string{"", "pin_1", "测试账号-1"} { info = qinglong.QueryCookieInfo(param) logger.Infof("%v: %v", param, info) } chart := qinglong.QueryChartPath(info) summary := qinglong.QuerySummary(info) + expired := qinglong.QueryCookieExpired(info) logger.Infof("") logger.Infof("解析的统计信息如下") logger.Infof("chart: %v", chart) - logger.Infof("summary\n%v", summary) + logger.Infof("summary: \n%v\n", summary) + logger.Infof("expired: \n%v\n", expired) } From 41873b60a27f5827004c375a73abf5a5ba3ba095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 21 Dec 2021 15:19:29 +0800 Subject: [PATCH 234/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=85=B7=E4=BD=93=E4=BB=8E=E4=BD=95=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=A7=A3=E6=9E=90=E5=BE=97=E5=88=B0=E7=9A=84?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index cf34d2e2f..88f53bde9 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -6,6 +6,7 @@ import ( "path/filepath" "sort" "strings" + "time" logger "github.com/sirupsen/logrus" @@ -84,7 +85,7 @@ func QuerySummary(info *JdCookieInfo) string { summary := parseSummary(info, filepath.Join(summaryDir, logFile.Name())) if summary != "" { - return summary + return appendLogFileInfo(summary, logFile.Name()) } } @@ -127,3 +128,10 @@ func isCookieExpired(info *JdCookieInfo) bool { result := QueryCookieExpired(info) return strings.Contains(result, "已失效") } + +func appendLogFileInfo(parsedContents string, logFileName string) string { + logTime := strings.TrimSuffix(logFileName, ".log") + parsedTime, _ := time.Parse("2006-01-02-15-04-05", logTime) + + return fmt.Sprintf("%s\n\n-- 从 %s 更新的日志中解析得到", parsedContents, parsedTime.Format("2006-01-02 15:04:05")) +} From 59c9c90277b49fbfe77641a578c58e2cd80f313b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 21 Dec 2021 15:19:47 +0800 Subject: [PATCH 235/612] =?UTF-8?q?feat:=20=E8=BF=87=E6=9C=9F=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E4=B9=9F=E6=9C=80=E5=A4=9A=E6=90=9C=E7=B4=A2=E8=8B=A5?= =?UTF-8?q?=E5=B9=B2=E4=B8=AA=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 88f53bde9..d198b87f2 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -114,11 +114,17 @@ func QueryCookieExpired(info *JdCookieInfo) string { return logFiles[i].Name() > logFiles[j].Name() }) - // 只取最新的一个日志 - latestLogFile := logFiles[0] - result := parseCookieExpired(info, filepath.Join(checkCookieDir, latestLogFile.Name())) - if result != "" { - return result + // 因为有可能最新的日志还在处理中,因此逆序搜索一定数目的日志,直到搜索到为止 + const MaxCheckCount = 6 + for idx, logFile := range logFiles { + if idx >= MaxCheckCount { + break + } + + result := parseCookieExpired(info, filepath.Join(checkCookieDir, logFile.Name())) + if result != "" { + return appendLogFileInfo(result, logFile.Name()) + } } return "" From 3bc2bf2b045906c8fa5feaea9f0dbb4dec8afcb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 21 Dec 2021 15:21:15 +0800 Subject: [PATCH 236/612] =?UTF-8?q?feat:=20=E6=8A=BD=E5=8F=96=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E5=B8=B8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index d198b87f2..56103af4f 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -13,6 +13,9 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) +// maxCheckCount 通过搜索历史日志搜寻信息时,最多尝试的日志文件数目 +const maxCheckCount = 6 + // QueryCookieInfo 尝试通过pt_pin/序号/昵称等来查询cookie信息 func QueryCookieInfo(param string) *JdCookieInfo { if param == "" { @@ -77,9 +80,8 @@ func QuerySummary(info *JdCookieInfo) string { }) // 因为有可能最新的日志还在处理中,因此逆序搜索一定数目的日志,直到搜索到为止 - const MaxCheckCount = 6 for idx, logFile := range logFiles { - if idx >= MaxCheckCount { + if idx >= maxCheckCount { break } @@ -115,9 +117,8 @@ func QueryCookieExpired(info *JdCookieInfo) string { }) // 因为有可能最新的日志还在处理中,因此逆序搜索一定数目的日志,直到搜索到为止 - const MaxCheckCount = 6 for idx, logFile := range logFiles { - if idx >= MaxCheckCount { + if idx >= maxCheckCount { break } From 1f4fb8e9ebafd51432c8ec700dbd3607697eabe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 21 Dec 2021 15:21:56 +0800 Subject: [PATCH 237/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E4=B8=80=E4=B8=AA=E6=B5=8B=E8=AF=95=E7=94=9F=E6=88=90?= =?UTF-8?q?=E7=9A=84=E4=B8=AD=E9=97=B4=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9b87e7a87..1fa030e24 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ logs/ resource.syso !modules/config/default_config.yml !qqrobot/config.toml +internal/btree/temp.*.db From 37472367b1978d88f47e859e459a1b4482445738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 21 Dec 2021 16:40:06 +0800 Subject: [PATCH 238/612] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E8=AF=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 56103af4f..f544bc759 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -140,5 +140,5 @@ func appendLogFileInfo(parsedContents string, logFileName string) string { logTime := strings.TrimSuffix(logFileName, ".log") parsedTime, _ := time.Parse("2006-01-02-15-04-05", logTime) - return fmt.Sprintf("%s\n\n-- 从 %s 更新的日志中解析得到", parsedContents, parsedTime.Format("2006-01-02 15:04:05")) + return fmt.Sprintf("%s\n【解析于 %s 的日志】", parsedContents, parsedTime.Format("2006-01-02 15:04:05")) } From cc96940422caf44455d9e68b2013b8a515495df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 22 Dec 2021 03:16:08 +0800 Subject: [PATCH 239/612] =?UTF-8?q?feat:=20=E4=BF=AE=E6=AD=A3remark?= =?UTF-8?q?=EF=BC=8C=E5=85=BC=E5=AE=B9=E5=8C=85=E5=90=AB=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E5=A4=87=E6=B3=A8=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 14 +++++++++----- qqrobot/qinglong/env_test.go | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index 28a614431..2df198dff 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -174,11 +174,15 @@ func getCookie(cookie string, cookieKey string) string { } func getRemark(remarks string) string { - if strings.HasPrefix(remarks, "remark=") { - // remark=备注; - remarks = strings.TrimPrefix(remarks, "remark=") - remarks = strings.TrimSuffix(remarks, ";") + remark := remarks + + for _, remarkPart := range strings.Split(remarks, ";") { + if strings.HasPrefix(remarkPart, "remark=") { + // remark=备注; + remark = strings.TrimPrefix(remarkPart, "remark=") + break + } } - return remarks + return remark } diff --git a/qqrobot/qinglong/env_test.go b/qqrobot/qinglong/env_test.go index 64834d382..6f7b349d7 100644 --- a/qqrobot/qinglong/env_test.go +++ b/qqrobot/qinglong/env_test.go @@ -65,6 +65,8 @@ func Test_getCookie(t *testing.T) { func Test_getRemark(t *testing.T) { assert.Equal(t, "test", getRemark("remark=test;")) + assert.Equal(t, "test", getRemark("remark=test;a=b")) + assert.Equal(t, "test", getRemark("remark=test;a=b;")) assert.Equal(t, "remark=test", getRemark("remark=remark=test;")) assert.Equal(t, "test", getRemark("test")) assert.Equal(t, "", getRemark("")) From e9fdefd16241ebd1c7d51ce658b980a11949a740 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 23 Dec 2021 17:29:36 +0800 Subject: [PATCH 240/612] [update] Bumped version (#1284) --- .github/ISSUE_TEMPLATE/bug-report.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index f799746ca..be575c6dc 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -45,6 +45,7 @@ body: label: go-cqhttp 版本 description: 选择正在使用的版本 (未列出的版本为已不支持,请更新到最新版本。) options: + - v1.0.0-beta8-fix2 - v1.0.0-beta8-fix1 - v1.0.0-beta8 - v1.0.0-beta7-fix2 From 024ec34fbeef50ebc840299c441f2be6d0a2f9ed Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 23 Dec 2021 21:18:01 +0800 Subject: [PATCH 241/612] fix: `bus_id` compatibility --- coolq/api.go | 4 ++-- modules/api/api.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 82cdba8b9..600d86188 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -568,7 +568,7 @@ func (bot *CQBot) CQGetGroupFilesByFolderID(groupID int64, folderID string) glob // // https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%96%87%E4%BB%B6%E8%B5%84%E6%BA%90%E9%93%BE%E6%8E%A5 // @route(get_group_file_url) -// @rename(bus_id->busid) +// @rename(bus_id->"[busid\x2Cbus_id].0") func (bot *CQBot) CQGetGroupFileURL(groupID int64, fileID string, busID int32) global.MSG { url := bot.Client.GetGroupFileUrl(groupID, fileID, busID) if url == "" { @@ -639,7 +639,7 @@ func (bot *CQBot) CQGroupFileDeleteFolder(groupID int64, id string) global.MSG { // CQGroupFileDeleteFile 拓展API-删除群文件 // // @route(delete_group_file) -// @rename(id->file_id, bus_id->busid) +// @rename(id->file_id, bus_id->"[busid\x2Cbus_id].0") func (bot *CQBot) CQGroupFileDeleteFile(groupID int64, id string, busID int32) global.MSG { fs, err := bot.Client.GetGroupFileSystem(groupID) if err != nil { diff --git a/modules/api/api.go b/modules/api/api.go index b22d12528..6687985ec 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -61,7 +61,7 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "delete_group_file": p0 := p.Get("group_id").Int() p1 := p.Get("file_id").String() - p2 := int32(p.Get("busid").Int()) + p2 := int32(p.Get("[busid,bus_id].0").Int()) return c.bot.CQGroupFileDeleteFile(p0, p1, p2) case "delete_group_folder": p0 := p.Get("group_id").Int() @@ -99,7 +99,7 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "get_group_file_url": p0 := p.Get("group_id").Int() p1 := p.Get("file_id").String() - p2 := int32(p.Get("busid").Int()) + p2 := int32(p.Get("[busid,bus_id].0").Int()) return c.bot.CQGetGroupFileURL(p0, p1, p2) case "get_group_files_by_folder": p0 := p.Get("group_id").Int() From bfc29a8c97a2be1d68af65fda658fe2e193b0ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=83=E6=A9=98=20=E9=9B=AB=E9=9C=9E?= Date: Fri, 24 Dec 2021 22:27:07 +0800 Subject: [PATCH 242/612] feat: support set max retries and retries interval (close #1252) (#1289) * feat: support set max retries and retries interval * fix: httpresponse using `res` before checking for errors * fix: `HttpServerPost` now be unexported * refactor: pretty `httpDefault` --- server/http.go | 185 ++++++++++++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 85 deletions(-) diff --git a/server/http.go b/server/http.go index fc233c2fc..c89d240fe 100644 --- a/server/http.go +++ b/server/http.go @@ -9,7 +9,6 @@ import ( "encoding/json" "fmt" "io" - "math/rand" "net/http" "net/url" "os" @@ -40,14 +39,18 @@ type HTTPServer struct { Enabled bool `yaml:"enabled"` MaxQueueSize int `yaml:"max-queue-size"` } `yaml:"long-polling"` - Post []struct { - URL string `yaml:"url"` - Secret string `yaml:"secret"` - } + Post []httpServerPost `yaml:"post"` MiddleWares `yaml:"middlewares"` } +type httpServerPost struct { + URL string `yaml:"url"` + Secret string `yaml:"secret"` + MaxRetries *uint64 `yaml:"max-retries"` + RetriesInterval *uint64 `yaml:"retries-interval"` +} + type httpServer struct { HTTP *http.Server api *api.Caller @@ -56,12 +59,14 @@ type httpServer struct { // HTTPClient 反向HTTP上报客户端 type HTTPClient struct { - bot *coolq.CQBot - secret string - addr string - filter string - apiPort int - timeout int32 + bot *coolq.CQBot + secret string + addr string + filter string + apiPort int + timeout int32 + MaxRetries uint64 + RetriesInterval uint64 } type httpCtx struct { @@ -70,66 +75,72 @@ type httpCtx struct { postForm url.Values } -const httpDefault = ` # HTTP 通信设置 - - http: - # 服务端监听地址 - host: 127.0.0.1 - # 服务端监听端口 - port: 5700 - # 反向HTTP超时时间, 单位秒 - # 最小值为5,小于5将会忽略本项设置 - timeout: 5 - # 长轮询拓展 - long-polling: - # 是否开启 - enabled: false - # 消息队列大小,0 表示不限制队列大小,谨慎使用 - max-queue-size: 2000 +const httpDefault = ` + - http: # HTTP 通信设置 + host: 127.0.0.1 # 服务端监听地址 + port: 5700 # 服务端监听端口 + timeout: 5 # 反向 HTTP 超时时间, 单位秒,<5 时将被忽略 + long-polling: # 长轮询拓展 + enabled: false # 是否开启 + max-queue-size: 2000 # 消息队列大小,0 表示不限制队列大小,谨慎使用 middlewares: <<: *default # 引用默认中间件 - # 反向HTTP POST地址列表 - post: - #- url: '' # 地址 - # secret: '' # 密钥 + post: # 反向HTTP POST地址列表 + #- url: '' # 地址 + # secret: '' # 密钥 + # max-retries: 3 # 最大重试,0 时禁用 + # retries-interval: 1500 # 重试时间,单位毫秒,0 时立即 #- url: http://127.0.0.1:5701/ # 地址 - # secret: '' # 密钥 + # secret: '' # 密钥 + # max-retries: 10 # 最大重试,0 时禁用 + # retries-interval: 1000 # 重试时间,单位毫秒,0 时立即 ` -func init() { - config.AddServer(&config.Server{ - Brief: "HTTP通信", - Default: httpDefault, - ParseEnv: func() (string, *yaml.Node) { - if os.Getenv("GCQ_HTTP_PORT") != "" { - // type convert tools - toInt64 := func(str string) int64 { - i, _ := strconv.ParseInt(str, 10, 64) - return i - } - accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") - node := &yaml.Node{} - httpConf := &HTTPServer{ - Host: "0.0.0.0", - Port: 5700, - MiddleWares: MiddleWares{ - AccessToken: accessTokenEnv, - }, - } - param.SetExcludeDefault(&httpConf.Disabled, param.EnsureBool(os.Getenv("GCQ_HTTP_DISABLE"), false), false) - param.SetExcludeDefault(&httpConf.Host, os.Getenv("GCQ_HTTP_HOST"), "") - param.SetExcludeDefault(&httpConf.Port, int(toInt64(os.Getenv("GCQ_HTTP_PORT"))), 0) - if os.Getenv("GCQ_HTTP_POST_URL") != "" { - httpConf.Post = append(httpConf.Post, struct { - URL string `yaml:"url"` - Secret string `yaml:"secret"` - }{os.Getenv("GCQ_HTTP_POST_URL"), os.Getenv("GCQ_HTTP_POST_SECRET")}) - } - _ = node.Encode(httpConf) - return "http", node - } - return "", nil +func nilParseUint(s string, base int, bitSize int) *uint64 { + pu, err := strconv.ParseUint(s, base, bitSize) + if err != nil { + return nil + } + return &pu +} + +func readEnvConfig() (string, *yaml.Node) { + + if s, ok := os.LookupEnv("GCQ_HTTP_PORT"); !ok || s == "" { + return "", nil + } + + // type convert tools + toInt64 := func(str string) int64 { + i, _ := strconv.ParseInt(str, 10, 64) + return i + } + accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") + node := &yaml.Node{} + httpConf := &HTTPServer{ + Host: "0.0.0.0", + Port: 5700, + MiddleWares: MiddleWares{ + AccessToken: accessTokenEnv, }, - }) + } + param.SetExcludeDefault(&httpConf.Disabled, param.EnsureBool(os.Getenv("GCQ_HTTP_DISABLE"), false), false) + param.SetExcludeDefault(&httpConf.Host, os.Getenv("GCQ_HTTP_HOST"), "") + param.SetExcludeDefault(&httpConf.Port, int(toInt64(os.Getenv("GCQ_HTTP_PORT"))), 0) + if os.Getenv("GCQ_HTTP_POST_URL") != "" { + httpConf.Post = append(httpConf.Post, httpServerPost{ + os.Getenv("GCQ_HTTP_POST_URL"), + os.Getenv("GCQ_HTTP_POST_SECRET"), + nilParseUint(os.Getenv("GCQ_HTTP_POST_MAXRETRIES"), 10, 64), + nilParseUint(os.Getenv("GCQ_HTTP_POST_RETRIESINTERVAL"), 10, 64), + }) + } + _ = node.Encode(httpConf) + return "http", node +} + +func init() { + config.AddServer(&config.Server{Brief: "HTTP通信", Default: httpDefault, ParseEnv: readEnvConfig}) } func (h *httpCtx) Get(s string) gjson.Result { @@ -242,6 +253,13 @@ func checkAuth(req *http.Request, token string) int { } } +func puint64Operator(p *uint64, def uint64) uint64 { + if p == nil { + return def + } + return *p +} + // runHTTP 启动HTTP服务器与HTTP上报客户端 func runHTTP(bot *coolq.CQBot, node yaml.Node) { var conf HTTPServer @@ -285,12 +303,14 @@ client: for _, c := range conf.Post { if c.URL != "" { go HTTPClient{ - bot: bot, - secret: c.Secret, - addr: c.URL, - apiPort: conf.Port, - filter: conf.Filter, - timeout: conf.Timeout, + bot: bot, + secret: c.Secret, + addr: c.URL, + apiPort: conf.Port, + filter: conf.Filter, + timeout: conf.Timeout, + MaxRetries: puint64Operator(c.MaxRetries, 3), + RetriesInterval: puint64Operator(c.RetriesInterval, 1500), }.Run() } } @@ -330,10 +350,7 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { } var res *http.Response - var err error - const maxAttemptTimes = 5 - - for i := 0; i <= maxAttemptTimes; i++ { + for i := uint64(0); i <= c.MaxRetries; i++ { // see https://stackoverflow.com/questions/31337891/net-http-http-contentlength-222-with-body-length-0 // we should create a new request for every single post trial req, err := http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) @@ -344,24 +361,22 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { req.Header = header res, err = client.Do(req) - if err == nil { + if res != nil { //goland:noinspection GoDeferInLoop defer res.Body.Close() + } + if err == nil { break } - if i != maxAttemptTimes { + if i < c.MaxRetries { log.Warnf("上报 Event 数据到 %v 失败: %v 将进行第 %d 次重试", c.addr, err, i+1) + } else { + log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上线", e.JSONBytes(), c.addr, err) + return } - const maxWait = int64(time.Second * 3) - const minWait = int64(time.Millisecond * 500) - wait := rand.Int63n(maxWait-minWait) + minWait - time.Sleep(time.Duration(wait)) + time.Sleep(time.Millisecond * time.Duration(c.RetriesInterval)) } - if err != nil { - log.Warnf("上报Event数据 %s 到 %v 失败: %v", e.JSONBytes(), c.addr, err) - return - } log.Debugf("上报Event数据 %s 到 %v", e.JSONBytes(), c.addr) r, err := io.ReadAll(res.Body) From 94ec3ccbcdc0157769fd62bae4072891d7a8e7bf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Dec 2021 14:27:39 +0000 Subject: [PATCH 243/612] ci(chore): Fix stylings --- server/http.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/http.go b/server/http.go index c89d240fe..d0bebcd77 100644 --- a/server/http.go +++ b/server/http.go @@ -105,7 +105,6 @@ func nilParseUint(s string, base int, bitSize int) *uint64 { } func readEnvConfig() (string, *yaml.Node) { - if s, ok := os.LookupEnv("GCQ_HTTP_PORT"); !ok || s == "" { return "", nil } From ef2223828fc57994dffbc392b733d6a0486f43fb Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 26 Dec 2021 13:09:08 +0800 Subject: [PATCH 244/612] feat: add WriteDeadline --- server/websocket.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/websocket.go b/server/websocket.go index 6cdf658a9..fe0cab245 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -60,6 +60,7 @@ type wsConn struct { func (c *wsConn) WriteText(b []byte) error { c.mu.Lock() defer c.mu.Unlock() + _ = c.conn.SetWriteDeadline(time.Now().Add(time.Second * 15)) return c.conn.WriteMessage(websocket.TextMessage, b) } @@ -460,6 +461,7 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { c.mu.Lock() defer c.mu.Unlock() + _ = c.conn.SetWriteDeadline(time.Now().Add(time.Second * 15)) writer, _ := c.conn.NextWriter(websocket.TextMessage) _ = json.NewEncoder(writer).Encode(ret) _ = writer.Close() From 80e35fc8005b7fa8d0ab889c7c8985ef2ed0e076 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 26 Dec 2021 15:48:08 +0800 Subject: [PATCH 245/612] fix(server): fix Mutex change in reconnect --- server/websocket.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/server/websocket.go b/server/websocket.go index fe0cab245..e584f41a1 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -270,13 +270,21 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { } log.Infof("已连接到反向WebSocket %s服务器 %v", typ, url) - wrappedConn := &wsConn{conn: conn, apiCaller: api.NewCaller(c.bot)} - if c.limiter != nil { - wrappedConn.apiCaller.Use(c.limiter) + + var wrappedConn *wsConn + if conptr != nil && *conptr != nil { + wrappedConn = *conptr + } else { + wrappedConn = new(wsConn) + if conptr != nil { + *conptr = wrappedConn + } } - if conptr != nil { - *conptr = wrappedConn + wrappedConn.conn = conn + wrappedConn.apiCaller = api.NewCaller(c.bot) + if c.limiter != nil { + wrappedConn.apiCaller.Use(c.limiter) } if typ != "Event" { From b8fe459c757016aafcb7d5a9c9018c6fc6f63ccc Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 26 Dec 2021 17:50:40 +0800 Subject: [PATCH 246/612] feat(coolq): upload images/videos in forward message concurrently --- coolq/api.go | 76 +++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 600d86188..5a1694103 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -12,6 +12,7 @@ import ( "runtime" "strconv" "strings" + "sync" "time" "github.com/segmentio/asm/base64" @@ -787,36 +788,26 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R return OK(global.MSG{"message_id": mid}) } -// CQSendGroupForwardMessage 扩展API-发送合并转发(群) -// -// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4 -// @route(send_group_forward_msg) -// @rename(m->messages) -func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) global.MSG { - if m.Type != gjson.JSON { - return Failed(100) - } - fm := message.NewForwardMessage() +func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.ForwardElement { ts := time.Now().Add(-time.Minute * 5) - hasCustom := false - m.ForEach(func(_, item gjson.Result) bool { - if item.Get("data.uin").Exists() || item.Get("data.user_id").Exists() { - hasCustom = true - return false - } - return true - }) + fm := message.NewForwardMessage() + var lazyUpload []func() + var wg sync.WaitGroup resolveElement := func(elems []message.IMessageElement) []message.IMessageElement { for i, elem := range elems { switch elem.(type) { case *LocalImageElement, *LocalVideoElement: - gm, err := bot.uploadMedia(elem, groupID, true) - if err != nil { - log.Warnf("警告: 群 %d %s上传失败: %v", groupID, elem.Type().String(), err) - continue - } - elems[i] = gm + wg.Add(1) + lazyUpload = append(lazyUpload, func() { + defer wg.Done() + gm, err := bot.uploadMedia(elem, groupID, true) + if err != nil { + log.Warnf("警告: 群 %d %s上传失败: %v", groupID, elem.Type().String(), err) + } else { + elems[i] = gm + } + }) } } return elems @@ -837,7 +828,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa SenderName: m.Attribute.SenderName, Time: func() int32 { msgTime := m.Attribute.Timestamp - if hasCustom && msgTime == 0 { + if msgTime == 0 { return int32(ts.Unix()) } return int32(msgTime) @@ -865,19 +856,12 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa return true }) if nested { // 处理嵌套 - nest := message.NewForwardMessage() - for _, item := range c.Array() { - node := convert(item) - if node != nil { - nest.AddNode(node) - } - } - elem := bot.Client.UploadGroupForwardMessage(groupID, nest) + fe := bot.uploadForwardElement(c, groupID) return &message.ForwardNode{ SenderId: uin, SenderName: name, Time: int32(msgTime), - Message: []message.IMessageElement{elem}, + Message: []message.IMessageElement{fe}, } } } @@ -893,6 +877,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa log.Warnf("警告: 非法 Forward node 将跳过. uin: %v name: %v content count: %v", uin, name, len(content)) return nil } + if m.IsArray() { for _, item := range m.Array() { node := convert(item) @@ -906,8 +891,27 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa fm.AddNode(node) } } - if fm.Length() > 0 { - fe := bot.Client.UploadGroupForwardMessage(groupID, fm) + + for _, upload := range lazyUpload { + go upload() + } + wg.Wait() + + return bot.Client.UploadGroupForwardMessage(groupID, fm) +} + +// CQSendGroupForwardMessage 扩展API-发送合并转发(群) +// +// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4 +// @route(send_group_forward_msg) +// @rename(m->messages) +func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) global.MSG { + if m.Type != gjson.JSON { + return Failed(100) + } + + fe := bot.uploadForwardElement(m, groupID) + if fe != nil { ret := bot.Client.SendGroupForwardMessage(groupID, fe) if ret == nil || ret.Id == -1 { log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.") From a10aee8c8da4464e02b45a1db139f0c1aa23ef75 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 26 Dec 2021 17:54:03 +0800 Subject: [PATCH 247/612] fix: make linter happy --- coolq/api.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 5a1694103..bde18ab57 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -813,8 +813,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F return elems } - var convert func(e gjson.Result) *message.ForwardNode - convert = func(e gjson.Result) *message.ForwardNode { + convert := func(e gjson.Result) *message.ForwardNode { if e.Get("type").Str != "node" { return nil } From 05f7eeb5c9dcdd39bece012f1fc6438e924e3da1 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 28 Dec 2021 16:56:36 +0800 Subject: [PATCH 248/612] fix: fix api-generator panic --- cmd/api-generator/main.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index 3a6a73b50..3e6c9b008 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -112,7 +112,7 @@ func main() { for _, decl := range file.Decls { switch decl := decl.(type) { case *ast.FuncDecl: - if decl.Recv == nil { + if !decl.Name.IsExported() || decl.Recv == nil { continue } if st, ok := decl.Recv.List[0].Type.(*ast.StarExpr); !ok || st.X.(*ast.Ident).Name != "CQBot" { @@ -137,8 +137,6 @@ func main() { for _, comment := range decl.Doc.List { annotation, args := match(comment.Text) switch annotation { - case "": - continue case "route": router.Path = args case "alias": From a49becfff43e0cc85d3528899d91e01afa82dcd9 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 28 Dec 2021 17:17:47 +0800 Subject: [PATCH 249/612] api-gen: replace aliases with multi-path --- cmd/api-generator/main.go | 33 ++++++++++++++++++++------------- coolq/api.go | 3 +-- modules/api/api.go | 12 ++++++------ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index 3e6c9b008..cf3a2367e 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -22,10 +22,9 @@ type Param struct { } type Router struct { - Func string - Path string - Aliases []string - Params []Param + Func string + Path []string + Params []Param } type generator struct { @@ -53,9 +52,12 @@ func (g *generator) generate(routers []Router) { } func (g *generator) router(router Router) { - g.WriteString(`case ` + strconv.Quote(router.Path)) - for _, alias := range router.Aliases { - g.WriteString(`, ` + strconv.Quote(alias)) + g.WriteString(`case `) + for i, p := range router.Path { + if i != 0 { + g.WriteString(`, `) + } + g.WriteString(strconv.Quote(p)) } g.WriteString(":\n") @@ -92,10 +94,12 @@ func conv(v, t string) string { return v + ".Bool()" case "string": return v + ".String()" - case "int32", "uint32", "int": + case "int32", "int": return t + "(" + v + ".Int())" case "uint64": return v + ".Uint()" + case "uint32": + return "uint32(" + v + ".Uint())" } } @@ -138,9 +142,9 @@ func main() { annotation, args := match(comment.Text) switch annotation { case "route": - router.Path = args - case "alias": - router.Aliases = append(router.Aliases, args) + for _, route := range strings.Split(args, ",") { + router.Path = append(router.Path, unquote(route)) + } case "default": for name, value := range parseMap(args, "=") { for i, p := range router.Params { @@ -158,8 +162,11 @@ func main() { } } } + sort.Slice(router.Path, func(i, j int) bool { + return router.Path[i] < router.Path[j] + }) } - if router.Path != "" { + if router.Path != nil { routers = append(routers, router) } else { println(decl.Name.Name) @@ -168,7 +175,7 @@ func main() { } sort.Slice(routers, func(i, j int) bool { - return routers[i].Path < routers[j].Path + return routers[i].Path[0] < routers[j].Path[0] }) out := new(bytes.Buffer) diff --git a/coolq/api.go b/coolq/api.go index bde18ab57..21defb133 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1733,8 +1733,7 @@ func (bot *CQBot) CQCanSendRecord() global.MSG { // CQOcrImage 扩展API-图片OCR // // https://docs.go-cqhttp.org/api/#%E5%9B%BE%E7%89%87-ocr -// @route(ocr_image) -// @alias(.ocr_image) +// @route(ocr_image,".ocr_image") // @rename(image_id->image) func (bot *CQBot) CQOcrImage(imageID string) global.MSG { img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, MessageSourceGroup) diff --git a/modules/api/api.go b/modules/api/api.go index 6687985ec..5ece47689 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -18,6 +18,9 @@ func (c *Caller) call(action string, p Getter) global.MSG { p0 := p.Get("context") p1 := p.Get("operation") return c.bot.CQHandleQuickOperation(p0, p1) + case ".ocr_image", "ocr_image": + p0 := p.Get("image").String() + return c.bot.CQOcrImage(p0) case "_get_model_show": p0 := p.Get("model").String() return c.bot.CQGetModelShow(p0) @@ -48,7 +51,7 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "create_guild_role": p0 := p.Get("guild_id").Uint() p1 := p.Get("name").String() - p2 := uint32(p.Get("color").Int()) + p2 := uint32(p.Get("color").Uint()) p3 := p.Get("independent").Bool() p4 := p.Get("initial_users") return c.bot.CQCreateGuildRole(p0, p1, p2, p3, p4) @@ -187,9 +190,6 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "mark_msg_as_read": p0 := int32(p.Get("message_id").Int()) return c.bot.CQMarkMessageAsRead(p0) - case "ocr_image", ".ocr_image": - p0 := p.Get("image").String() - return c.bot.CQOcrImage(p0) case "qidian_get_account_info": return c.bot.CQGetQiDianAccountInfo() case "reload_event_filter": @@ -260,7 +260,7 @@ func (c *Caller) call(action string, p Getter) global.MSG { p1 := p.Get("user_id").Int() p2 := uint32(1800) if pt := p.Get("duration"); pt.Exists() { - p2 = uint32(pt.Int()) + p2 = uint32(pt.Uint()) } return c.bot.CQSetGroupBan(p0, p1, p2) case "set_group_card": @@ -308,7 +308,7 @@ func (c *Caller) call(action string, p Getter) global.MSG { p0 := p.Get("guild_id").Uint() p1 := p.Get("role_id").Uint() p2 := p.Get("name").String() - p3 := uint32(p.Get("color").Int()) + p3 := uint32(p.Get("color").Uint()) p4 := p.Get("indepedent").Bool() return c.bot.CQModifyRoleInGuild(p0, p1, p2, p3, p4) case "upload_group_file": From 59bc7b4bae268637487480e48a3fdc8be23aaffa Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 28 Dec 2021 17:33:14 +0800 Subject: [PATCH 250/612] internal/cache: always use CacheDB Updates #1075 --- coolq/api.go | 2 +- coolq/cqcode.go | 4 ++-- coolq/event.go | 32 +++++++------------------------- global/net.go | 3 --- internal/cache/cache.go | 17 +++++------------ 5 files changed, 15 insertions(+), 43 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 21defb133..6005c108b 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1427,7 +1427,7 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) global func (bot *CQBot) CQGetImage(file string) global.MSG { var b []byte var err error - if cache.EnableCacheDB && strings.HasSuffix(file, ".image") { + if strings.HasSuffix(file, ".image") { var f []byte f, err = hex.DecodeString(strings.TrimSuffix(file, ".image")) b = cache.Image.Get(f) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 02974209b..eb6c8fd50 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -1309,7 +1309,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy } rawPath := path.Join(global.ImagePath, f) if video { - if strings.HasSuffix(f, ".video") && cache.EnableCacheDB { + if strings.HasSuffix(f, ".video") { hash, err := hex.DecodeString(strings.TrimSuffix(f, ".video")) if err == nil { if b := cache.Video.Get(hash); b != nil { @@ -1334,7 +1334,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return &LocalImageElement{File: cacheFile}, nil } } - if strings.HasSuffix(f, ".image") && cache.EnableCacheDB { + if strings.HasSuffix(f, ".image") { hash, err := hex.DecodeString(strings.TrimSuffix(f, ".image")) if err == nil { if b := cache.Image.Get(hash); b != nil { diff --git a/coolq/event.go b/coolq/event.go index 0199bf660..d89707dbe 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -726,7 +726,6 @@ func (bot *CQBot) groupDecrease(groupCode, userUin int64, operator *client.Group } func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { - // TODO(wdvxdr): remove these old cache file in v1.0.0 for _, elem := range e { switch i := elem.(type) { case *message.GroupImageElement: @@ -744,12 +743,8 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { w.WriteString(i.ImageId) w.WriteString(i.Url) }) - filename := hex.EncodeToString(i.Md5) + ".image" - if cache.EnableCacheDB { - cache.Image.Insert(i.Md5, data) - } else if !global.PathExists(path.Join(global.ImagePath, filename)) { - _ = os.WriteFile(path.Join(global.ImagePath, filename), data, 0o644) - } + cache.Image.Insert(i.Md5, data) + case *message.GuildImageElement: data := binary.NewWriterF(func(w *binary.Writer) { w.Write(i.Md5) @@ -758,14 +753,9 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { w.WriteString(i.Url) }) filename := hex.EncodeToString(i.Md5) + ".image" - if cache.EnableCacheDB { - cache.Image.Insert(i.Md5, data) - } else if !global.PathExists(path.Join(global.ImagePath, filename)) { - _ = os.WriteFile(path.Join(global.ImagePath, filename), data, 0o644) - } - + cache.Image.Insert(i.Md5, data) if i.Url != "" && !global.PathExists(path.Join(global.ImagePath, "guild-images", filename)) { - if err := global.DownloadFile(i.Url, path.Join(global.ImagePath, "guild-images", filename), -1, map[string]string{}); err != nil { + if err := global.DownloadFile(i.Url, path.Join(global.ImagePath, "guild-images", filename), -1, nil); err != nil { log.Warnf("下载频道图片时出现错误: %v", err) } } @@ -776,12 +766,8 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { w.WriteString(i.ImageId) w.WriteString(i.Url) }) - filename := hex.EncodeToString(i.Md5) + ".image" - if cache.EnableCacheDB { - cache.Image.Insert(i.Md5, data) - } else if !global.PathExists(path.Join(global.ImagePath, filename)) { - _ = os.WriteFile(path.Join(global.ImagePath, filename), data, 0o644) - } + cache.Image.Insert(i.Md5, data) + case *message.VoiceElement: // todo: don't download original file? i.Name = strings.ReplaceAll(i.Name, "{", "") @@ -804,11 +790,7 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { w.Write(i.Uuid) }) filename := hex.EncodeToString(i.Md5) + ".video" - if cache.EnableCacheDB { - cache.Video.Insert(i.Md5, data) - } else if !global.PathExists(path.Join(global.VideoPath, filename)) { - _ = os.WriteFile(path.Join(global.VideoPath, filename), data, 0o644) - } + cache.Video.Insert(i.Md5, data) i.Name = filename i.Url = bot.Client.GetShortVideoUrl(i.Uuid, i.Md5) } diff --git a/global/net.go b/global/net.go index 541c54cae..ce74edbe9 100644 --- a/global/net.go +++ b/global/net.go @@ -71,9 +71,6 @@ func DownloadFile(url, path string, limit int64, headers map[string]string) erro req.Header.Set(k, v) } - if _, ok := headers["User-Agent"]; !ok { - req.Header["User-Agent"] = []string{UserAgent} - } resp, err := client.Do(req) if err != nil { return err diff --git a/internal/cache/cache.go b/internal/cache/cache.go index d4f4e7fcb..f410626cb 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -12,11 +12,6 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/btree" ) -// todo(wdvxdr): always enable db-cache in v1.0.0 - -// EnableCacheDB 是否启用 btree db缓存图片等 -var EnableCacheDB bool - // Media Cache DBs var ( Image Cache @@ -63,14 +58,12 @@ func (c *Cache) Delete(md5 []byte) { // Init 初始化 Cache func Init() { node, ok := base.Database["cache"] - if !ok { - return - } - EnableCacheDB = true var conf map[string]string - err := node.Decode(&conf) - if err != nil { - log.Fatalf("failed to read cache config: %v", err) + if ok { + err := node.Decode(&conf) + if err != nil { + log.Fatalf("failed to read cache config: %v", err) + } } var open = func(typ string, cache *Cache) { From 18487d63530d70855ab2df583f0c82a48df50903 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 28 Dec 2021 22:12:24 +0800 Subject: [PATCH 251/612] dep: update dependencies --- go.mod | 36 +++++---- go.sum | 231 ++++++++------------------------------------------------- 2 files changed, 48 insertions(+), 219 deletions(-) diff --git a/go.mod b/go.mod index 3da5c9a58..74546121b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211219072930-afb81a32aa75 + github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 @@ -14,31 +14,29 @@ require ( github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/klauspost/compress v1.13.6 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible - github.com/mattn/go-colorable v0.1.11 + github.com/mattn/go-colorable v0.1.12 github.com/pkg/errors v0.9.1 - github.com/segmentio/asm v1.1.0 + github.com/segmentio/asm v1.1.3 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 - github.com/tidwall/gjson v1.11.0 + github.com/tidwall/gjson v1.12.1 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 - go.mongodb.org/mongo-driver v1.7.4 - golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa + go.mongodb.org/mongo-driver v1.8.1 + golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 - golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac + golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) require ( - github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 // indirect + github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-stack/stack v1.8.1 // indirect + github.com/go-stack/stack v1.8.0 // indirect github.com/gocq/rs v1.0.1 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.5 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/uuid v1.1.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/pierrec/lz4/v4 v4.1.11 // indirect @@ -50,13 +48,13 @@ require ( github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.0.2 // indirect github.com/xdg-go/stringprep v1.0.2 // indirect - github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 // indirect - golang.org/x/text v0.3.7 // indirect - modernc.org/libc v1.11.70 // indirect - modernc.org/mathutil v1.4.1 // indirect - modernc.org/memory v1.0.5 // indirect + golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 // indirect + golang.org/x/text v0.3.6 // indirect + modernc.org/libc v1.8.1 // indirect + modernc.org/mathutil v1.2.2 // indirect + modernc.org/memory v1.0.4 // indirect ) diff --git a/go.sum b/go.sum index 33b9dc8df..9a32f5955 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,11 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211219072930-afb81a32aa75 h1:4xgd1/lhAKHv/2sAbxbR38hDwt/HfOiWlVOqSJc544E= -github.com/Mrs4s/MiraiGo v0.0.0-20211219072930-afb81a32aa75/go.mod h1:HI05ardsa5HE1Z2xTw69dytmaqqRWv4ajD5Jpnycqqw= -github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737 h1:p4o7/eSoP39jwnGZz08N1IpH/mNzg9SdCn7kPM9A9BE= -github.com/RomiChan/protobuf v0.0.0-20211204042931-ff4f35848737/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da h1:uqY6vOKvcCBKlIDaZA6/YiDNuDP5VuACmWe2mntIwfc= +github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da/go.mod h1:VrmhZC9o+uKB9AKRy5bZZxE5EJwJhAmo7Xn/c2tzkZI= +github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpSNVp5mnuloRMd3Rj2Lfg5biZ3emv//c= +github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -19,70 +18,30 @@ github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98 h1:NJDZEa7gibUa0w4tie8qKeQFKdeKFUbecWyQDPdRx40= github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98/go.mod h1:E5TBHc60dsWtOL7sbXCb3P9i4xrj2J7Zm5sEJftIc1w= github.com/gocq/rs v1.0.1 h1:ng7nhXmnx3SnfM0DOqmbP6GmQp1xGwRG9XmBiLFDWuM= github.com/gocq/rs v1.0.1/go.mod h1:8oaQnRvqn1fMh8i5zsetgQo03OUXksJV1k+dpmExxcY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -94,10 +53,8 @@ github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkL github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -107,10 +64,8 @@ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pierrec/lz4/v4 v4.1.11 h1:LVs17FAZJFOjgmJXl9Tf13WfLUvZq7/RjfEJrnwZ9OE= github.com/pierrec/lz4/v4 v4.1.11/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -118,23 +73,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/segmentio/asm v1.1.0 h1:fkVr8k5J4sKoFjTGVD6r1yKvDKqmvrEh3K7iyVxgBs8= -github.com/segmentio/asm v1.1.0/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= +github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -142,8 +88,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4= -github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -157,184 +103,69 @@ github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.mongodb.org/mongo-driver v1.7.4 h1:sllcioag8Mec0LYkftYWq+cKNPIR4Kqq3iv9ZXY0g/E= -go.mongodb.org/mongo-driver v1.7.4/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.8.1 h1:OZE4Wni/SJlrcmSIBRYNzunX5TKxjrTS4jKSnA99oKU= +go.mongodb.org/mongo-driver v1.8.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 h1:7NCfEGl0sfUojmX78nK9pBJuUlSZWEJA/TwASvfiPLo= -golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 h1:WecRHqgE09JBkh/584XIE6PMz5KKE/vER4izNUi30AQ= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= -modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= -modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= -modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= -modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= -modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= -modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= -modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= -modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= -modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= -modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= -modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= -modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= -modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= -modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= -modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= -modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= -modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= -modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= -modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= -modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= -modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= -modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= -modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= -modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= -modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= -modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= -modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= -modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= -modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= -modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= -modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= -modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= -modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= -modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= -modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= -modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= -modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= -modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= -modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= -modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= -modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= -modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= -modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= -modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= -modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= -modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= -modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= -modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= -modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= -modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= -modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= -modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= -modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= -modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= -modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= -modernc.org/libc v1.11.70 h1:OHnBZYEJF8CuLOH++G4XYL2lZ4yLH/kkKTRf6gqV5UE= -modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= -modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= From b98f75ccabaeb2408e4eb7b51dca29dffe75017f Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 29 Dec 2021 14:04:51 +0800 Subject: [PATCH 252/612] style: run gofumpt --- .golangci.yml | 1 + coolq/bot.go | 1 - internal/btree/btree_test.go | 6 +++--- internal/btree/helper.go | 2 +- internal/cache/cache.go | 2 +- modules/config/config.go | 6 ++++-- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ca674a0e7..4ff3d91cc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -28,6 +28,7 @@ linters: - errcheck - exportloopref - exhaustive + - bidichk #- funlen #- goconst - gocritic diff --git a/coolq/bot.go b/coolq/bot.go index b19004973..2b7cafad6 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -542,7 +542,6 @@ func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) stri // Release 释放Bot实例 func (bot *CQBot) Release() { - } func (bot *CQBot) dispatchEventMessage(m global.MSG) { diff --git a/internal/btree/btree_test.go b/internal/btree/btree_test.go index 5afc4aed8..d617570b3 100644 --- a/internal/btree/btree_test.go +++ b/internal/btree/btree_test.go @@ -28,15 +28,15 @@ func TestBtree(t *testing.T) { bt, err := Create(f) assert2.NoError(t, err) - var tests = []string{ + tests := []string{ "hello world", "123", "We are met on a great battle-field of that war.", "Abraham Lincoln, November 19, 1863, Gettysburg, Pennsylvania", } - var sha = make([]*byte, len(tests)) + sha := make([]*byte, len(tests)) for i, tt := range tests { - var hash = sha1.New() + hash := sha1.New() hash.Write([]byte(tt)) sha[i] = &hash.Sum(nil)[0] bt.Insert(sha[i], []byte(tt)) diff --git a/internal/btree/helper.go b/internal/btree/helper.go index 4a9761041..eb9c1f78c 100644 --- a/internal/btree/helper.go +++ b/internal/btree/helper.go @@ -45,7 +45,7 @@ func resethash(sha1 *byte) { // reading table func read32(r io.Reader) (int32, error) { - var b = make([]byte, 4) + b := make([]byte, 4) _, err := r.Read(b) if err != nil { return 0, err diff --git a/internal/cache/cache.go b/internal/cache/cache.go index f410626cb..30cb53899 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -66,7 +66,7 @@ func Init() { } } - var open = func(typ string, cache *Cache) { + open := func(typ string, cache *Cache) { file := conf[typ] if file == "" { file = fmt.Sprintf("data/%s.db", typ) diff --git a/modules/config/config.go b/modules/config/config.go index 4dee91577..56250d618 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -141,8 +141,10 @@ func Parse(path string) *Config { return config } -var serverconfs []*Server -var mu sync.Mutex +var ( + serverconfs []*Server + mu sync.Mutex +) // AddServer 添加该服务的简介和默认配置 func AddServer(s *Server) { From 9152185ebc25be30b90878005ab3ed23af2d80ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Fri, 31 Dec 2021 11:19:58 +0800 Subject: [PATCH 253/612] fix: PutBuffer panic on nil event buffer (#1299) --- coolq/bot.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index 2b7cafad6..2b7ce53d9 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -569,7 +569,9 @@ func (bot *CQBot) dispatchEventMessage(m global.MSG) { }(f) } wg.Wait() - global.PutBuffer(event.buffer) + if event.buffer != nil { + global.PutBuffer(event.buffer) + } } func formatGroupName(group *client.GroupInfo) string { From 64075f075b3d09993f2b94747bd557c9f972b963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 31 Dec 2021 12:01:34 +0800 Subject: [PATCH 254/612] =?UTF-8?q?feat:=20=E8=BF=87=E6=9C=9F=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E4=BB=85=E6=9C=80=E5=A4=9A=E5=B0=9D=E8=AF=95=E4=B8=A4?= =?UTF-8?q?=E4=B8=AA=E6=97=A5=E5=BF=97=EF=BC=8C=E4=B8=94=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=88=B0=E5=AE=8C=E6=95=B4=E6=97=A5=E5=BF=97=E5=90=8E=E7=AB=8B?= =?UTF-8?q?=E5=8D=B3=E7=BB=93=E6=9D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/expired.go | 17 ++++++++++------- qqrobot/qinglong/expired_test.go | 13 +++++++++---- qqrobot/qinglong/query.go | 10 ++++++++-- .../2021-12-17-12-00-01.log | 10 ++++++++-- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/qqrobot/qinglong/expired.go b/qqrobot/qinglong/expired.go index 99718aaf3..2e7386445 100644 --- a/qqrobot/qinglong/expired.go +++ b/qqrobot/qinglong/expired.go @@ -7,17 +7,20 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) -func parseCookieExpired(info *JdCookieInfo, logFilePath string) string { +func parseCookieExpired(info *JdCookieInfo, logFilePath string) (result string, isLogComplete bool) { if !global.PathExists(logFilePath) { - return "" + return "", false } contentBytes, err := ioutil.ReadFile(logFilePath) if err != nil { - return "" + return "", false } content := string(contentBytes) + // 判断这个日志是否运行完整 + isLogComplete = strings.Contains(content, "开始发送通知...") + prefixToRemove := " : " prefix := prefixToRemove + info.QueryUnescapedPtPin() suffix := "\n\n" @@ -25,18 +28,18 @@ func parseCookieExpired(info *JdCookieInfo, logFilePath string) string { // 定位前缀 prefixIndex := strings.Index(content, prefix) if prefixIndex == -1 { - return "" + return "", isLogComplete } prefixIndex += len(prefixToRemove) // 定位后缀 suffixIndex := strings.Index(content[prefixIndex:], suffix) if suffixIndex == -1 { - return "" + return "", isLogComplete } suffixIndex += prefixIndex - result := content[prefixIndex:suffixIndex] + result = content[prefixIndex:suffixIndex] - return result + return result, isLogComplete } diff --git a/qqrobot/qinglong/expired_test.go b/qqrobot/qinglong/expired_test.go index 0a0e4dbc5..d0df8049e 100644 --- a/qqrobot/qinglong/expired_test.go +++ b/qqrobot/qinglong/expired_test.go @@ -8,17 +8,22 @@ import ( ) func Test_parseCookieExpired(t *testing.T) { + getResult := func(info *JdCookieInfo, logFilePath string) string { + result, _ := parseCookieExpired(info, logFilePath) + return result + } + logPath := getPath("log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log") info := QueryCookieInfo("pin_1") - assert.Equal(t, "pin_1 状态正常!", parseCookieExpired(info, logPath)) + assert.Equal(t, "pin_1 状态正常!", getResult(info, logPath)) info = QueryCookieInfo("pin_2") - assert.Equal(t, "pin_2 已失效,自动禁用成功!", parseCookieExpired(info, logPath)) + assert.Equal(t, "pin_2 已失效,自动禁用成功!", getResult(info, logPath)) info = QueryCookieInfo("pin_3") - assert.Equal(t, "pin_3 已失效,已禁用!", parseCookieExpired(info, logPath)) + assert.Equal(t, "pin_3 已失效,已禁用!", getResult(info, logPath)) info = QueryCookieInfo(url.QueryEscape("中文pin")) - assert.Equal(t, "中文pin 状态正常!", parseCookieExpired(info, logPath)) + assert.Equal(t, "中文pin 状态正常!", getResult(info, logPath)) } diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index f544bc759..c68eb7a75 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -16,6 +16,9 @@ import ( // maxCheckCount 通过搜索历史日志搜寻信息时,最多尝试的日志文件数目 const maxCheckCount = 6 +// maxExpireCheckCount 由于可能最新的还在处理中,因此最多尝试两个,避免过多查询导致之前的过期记录也影响这个 +const maxExpireCheckCount = 2 + // QueryCookieInfo 尝试通过pt_pin/序号/昵称等来查询cookie信息 func QueryCookieInfo(param string) *JdCookieInfo { if param == "" { @@ -118,13 +121,16 @@ func QueryCookieExpired(info *JdCookieInfo) string { // 因为有可能最新的日志还在处理中,因此逆序搜索一定数目的日志,直到搜索到为止 for idx, logFile := range logFiles { - if idx >= maxCheckCount { + if idx >= maxExpireCheckCount { break } - result := parseCookieExpired(info, filepath.Join(checkCookieDir, logFile.Name())) + result, isLogComplete := parseCookieExpired(info, filepath.Join(checkCookieDir, logFile.Name())) if result != "" { return appendLogFileInfo(result, logFile.Name()) + } else if isLogComplete { + // 没有解析到该账号,但该日志是完整日志,说明这个账号未过期 + return "" } } diff --git a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log index e6b4ff973..68cfb3df3 100644 --- a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log +++ b/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log @@ -59,6 +59,12 @@ 开始发送通知... 通知标题: 京东CK检测 -正在处理账号Remark..... +sendNotify正在处理账号Remark..... 处理完成,开始发送通知... -发送go-cqhttp通知调用API失败!! +go-cqhttp发送通知消息成功🎉 + + +🔔京东CK检测, 结束! 🕛 671.373 秒 + + +## 执行结束... 2021-12-31 06:11:26 耗时 685 秒 From cad88a1f42ce9e9050c2f156fb349bbb55960204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 11 Jan 2022 15:57:35 +0800 Subject: [PATCH 255/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=A4=84?= =?UTF-8?q?=E7=90=86nvjdc=E7=89=B9=E6=AE=8A=E6=A0=87=E8=AE=B0=E7=9A=84?= =?UTF-8?q?=E5=A4=87=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 10 ++++++++++ qqrobot/qinglong/env_test.go | 3 +++ 2 files changed, 13 insertions(+) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index 2df198dff..2c8b569de 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -174,8 +174,10 @@ func getCookie(cookie string, cookieKey string) string { } func getRemark(remarks string) string { + // 如果有备注,则使用备注 remark := remarks + // 如果备注中有remark=字样 for _, remarkPart := range strings.Split(remarks, ";") { if strings.HasPrefix(remarkPart, "remark=") { // remark=备注; @@ -184,5 +186,13 @@ func getRemark(remarks string) string { } } + // 特殊处理nvdjc附加的备注 + // remark=test;@@UID_xxxxxx + // test@@UID_xxxxxx + // test@@1640780099690@@UID_xxxxxx + if strings.Contains(remark, "@@") { + remark = strings.Split(remark, "@@")[0] + } + return remark } diff --git a/qqrobot/qinglong/env_test.go b/qqrobot/qinglong/env_test.go index 6f7b349d7..dd91e8e55 100644 --- a/qqrobot/qinglong/env_test.go +++ b/qqrobot/qinglong/env_test.go @@ -70,4 +70,7 @@ func Test_getRemark(t *testing.T) { assert.Equal(t, "remark=test", getRemark("remark=remark=test;")) assert.Equal(t, "test", getRemark("test")) assert.Equal(t, "", getRemark("")) + assert.Equal(t, "test", getRemark("remark=test;@@UID_xxxxxx")) + assert.Equal(t, "test", getRemark("test@@UID_xxxxxx")) + assert.Equal(t, "test", getRemark("test@@1640780099690@@UID_xxxxxx")) } From 80865d33e3e2696e6183e3f052bf3de4b13b5be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 11 Jan 2022 17:22:19 +0800 Subject: [PATCH 256/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=A7=A3?= =?UTF-8?q?=E6=9E=90cookie=E7=9A=84=E5=88=9B=E5=BB=BA=E5=92=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=97=B6=E9=97=B4=E6=88=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 46 ++++++++++++++++++++++++++++++++++++ qqrobot/qinglong/env_test.go | 21 ++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index 2c8b569de..3f4351df9 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -6,7 +6,9 @@ import ( "fmt" "net/url" "os" + "strconv" "strings" + "time" ) // EnvDBEntry env.db中记录格式 @@ -21,6 +23,47 @@ type EnvDBEntry struct { Remarks string `json:"remarks"` } +// CreateTime 创建unix时间戳 +func (e *EnvDBEntry) CreateTime() int64 { + return time.UnixMilli(e.Created).Unix() +} + +// UpdateTime 更新unix时间戳 +func (e *EnvDBEntry) UpdateTime() int64 { + // 先尝试解析nvjdc设置的更新时间:test@@1640780099690@@UID_xxxxxx + if strings.Contains(e.Remarks, "@@") { + for _, remarkPart := range strings.Split(e.Remarks, "@@") { + if len(remarkPart) != 13 { + continue + } + + millTimeStamp, err := strconv.ParseInt(remarkPart, 10, 64) + if err != nil { + continue + } + + return time.UnixMilli(millTimeStamp).Unix() + } + } + + // 否则,解析青龙设置的更新时间 + // Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间) + t, _ := time.Parse("Mon Jan 02 2006 15:04:05 MST-0700 (中国标准时间)", e.Timestamp) + return t.Unix() +} + +// UsedDays 已使用天数 +func (e *EnvDBEntry) UsedDays() int { + return int(time.Now().Unix()-e.CreateTime()) / 86400 +} + +const maxValidDays = 30 + +// EstimateRemainingDays 预计剩余天数 +func (e *EnvDBEntry) EstimateRemainingDays() int { + return maxValidDays - int(time.Now().Unix()-e.UpdateTime())/86400 +} + // JdCookieInfo 所需的京东cookie信息 type JdCookieInfo struct { PtPin string // pt_pin, note: 定位只能使用这个字段,而不能使用index,因青龙不是依据env.sh来生成index的 @@ -104,6 +147,9 @@ func parseEnvDB() (map[string]*JdCookieInfo, error) { continue } + v2 := envEntry.UpdateTime() + _ = v2 + ptPin := getPtPin(envEntry.Value) remark := getRemark(envEntry.Remarks) diff --git a/qqrobot/qinglong/env_test.go b/qqrobot/qinglong/env_test.go index dd91e8e55..63cad1267 100644 --- a/qqrobot/qinglong/env_test.go +++ b/qqrobot/qinglong/env_test.go @@ -1,6 +1,7 @@ package qinglong import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -74,3 +75,23 @@ func Test_getRemark(t *testing.T) { assert.Equal(t, "test", getRemark("test@@UID_xxxxxx")) assert.Equal(t, "test", getRemark("test@@1640780099690@@UID_xxxxxx")) } + +func TestEnvDBEntry_CreateTime(t *testing.T) { + expectedCreateTimeStamp := int64(1636536508) + withoutExtraRemark := &EnvDBEntry{ + Created: expectedCreateTimeStamp * 1000, + Timestamp: "Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)", + Remarks: "test", + } + assert.Equal(t, expectedCreateTimeStamp, withoutExtraRemark.CreateTime()) + assert.Equal(t, expectedCreateTimeStamp, withoutExtraRemark.UpdateTime()) + + expectedNvjdcUpdateTimeStamp := int64(1636536538) + withExtraRemark := &EnvDBEntry{ + Created: 1636536508000, + Timestamp: "Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)", + Remarks: fmt.Sprintf("test@@%v@@UID_xxxxxx", expectedNvjdcUpdateTimeStamp*1000), + } + assert.Equal(t, expectedCreateTimeStamp, withExtraRemark.CreateTime()) + assert.Equal(t, expectedNvjdcUpdateTimeStamp, withExtraRemark.UpdateTime()) +} From a9d6254d05106095d268166cbdebb6482dfbe1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 11 Jan 2022 17:27:24 +0800 Subject: [PATCH 257/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E9=A2=84=E6=9C=9F=E8=BF=87=E6=9C=9F=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index 3f4351df9..ff1c037a5 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -66,8 +66,10 @@ func (e *EnvDBEntry) EstimateRemainingDays() int { // JdCookieInfo 所需的京东cookie信息 type JdCookieInfo struct { - PtPin string // pt_pin, note: 定位只能使用这个字段,而不能使用index,因青龙不是依据env.sh来生成index的 - Remark string // remark + PtPin string // pt_pin, note: 定位只能使用这个字段,而不能使用index,因青龙不是依据env.sh来生成index的 + Remark string // remark + UsedDays int // 已使用天数 + EstimateRemainingDays int // 预计剩余天数 } // QueryUnescapedPtPin 返回url解码后的pt_pin @@ -87,6 +89,8 @@ func (info *JdCookieInfo) ToChatMessage() string { // 检查是否过期 if isCookieExpired(info) { expiredInfo = "已过期,请更新cookie(每六个小时重新检测并自动启用)" + } else if info.UsedDays != 0 { + expiredInfo += fmt.Sprintf(",已使用 %d 天, 预计 %d 天后过期", info.UsedDays, info.EstimateRemainingDays) } return fmt.Sprintf("\n"+ "\npt_pin: %v"+ @@ -147,15 +151,14 @@ func parseEnvDB() (map[string]*JdCookieInfo, error) { continue } - v2 := envEntry.UpdateTime() - _ = v2 - ptPin := getPtPin(envEntry.Value) remark := getRemark(envEntry.Remarks) ptPinToCookieInfo[ptPin] = &JdCookieInfo{ - PtPin: ptPin, - Remark: remark, + PtPin: ptPin, + Remark: remark, + UsedDays: envEntry.UsedDays(), + EstimateRemainingDays: envEntry.EstimateRemainingDays(), } } From 6916fb7d47a9053b74453ddf42ae6e7a2133a03e Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 12 Jan 2022 22:08:39 +0800 Subject: [PATCH 258/612] dep: use RomiChan forked websocket --- go.mod | 4 ++-- go.sum | 8 ++++---- server/websocket.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 74546121b..e78853664 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,11 @@ require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da + github.com/RomiChan/websocket v1.4.3-0.20220112132122-0fb50fcee646 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98 - github.com/gorilla/websocket v1.4.2 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/klauspost/compress v1.13.6 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible @@ -52,7 +52,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 // indirect + golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect golang.org/x/text v0.3.6 // indirect modernc.org/libc v1.8.1 // indirect modernc.org/mathutil v1.2.2 // indirect diff --git a/go.sum b/go.sum index 9a32f5955..c6f226871 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da h1:uqY6vOKvcCBKlIDaZ github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da/go.mod h1:VrmhZC9o+uKB9AKRy5bZZxE5EJwJhAmo7Xn/c2tzkZI= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpSNVp5mnuloRMd3Rj2Lfg5biZ3emv//c= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/RomiChan/websocket v1.4.3-0.20220112132122-0fb50fcee646 h1:v9aUYiWu84Z6UrW+mkVvw/2mYJx41OFK26Hrr+FgqeM= +github.com/RomiChan/websocket v1.4.3-0.20220112132122-0fb50fcee646/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -32,8 +34,6 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= @@ -134,8 +134,8 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 h1:WecRHqgE09JBkh/584XIE6PMz5KKE/vER4izNUi30AQ= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= diff --git a/server/websocket.go b/server/websocket.go index e584f41a1..964b926cd 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -13,7 +13,7 @@ import ( "time" "github.com/Mrs4s/MiraiGo/utils" - "github.com/gorilla/websocket" + "github.com/RomiChan/websocket" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "gopkg.in/yaml.v3" From 4cb848d50c3ec1e02910521851f7b6bf6659dc47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 22 Jan 2022 19:14:45 +0800 Subject: [PATCH 259/612] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E8=A7=A6=E5=8F=91=E6=9B=B4=E6=96=B0=E5=85=AC=E5=91=8A?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=B9=E4=B8=BA=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E6=8C=87=E5=AE=9A=E7=9A=84=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E8=A7=84=E5=88=99=EF=BC=8C=E5=B9=B6=E9=80=9A=E7=9F=A5=E5=88=B0?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=A7=84=E5=88=99=E4=B8=AD=E8=AE=BE=E5=AE=9A?= =?UTF-8?q?=E7=9A=84=E7=BE=A4=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 33 ++++++++++++++++++++++----------- qqrobot/config.go | 13 +++++++------ qqrobot/qq_robot.go | 6 +++--- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 330d5cb6a..3efff39f6 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -45,24 +45,35 @@ func (r *QQRobot) checkUpdates() { } } -func (r *QQRobot) manualTriggerUpdateMessage(groupID int64) (replies *message.SendingMessage) { +func (r *QQRobot) manualTriggerUpdateNotify(triggerRule *Rule) (replies *message.SendingMessage) { for _, rule := range r.Config.NotifyUpdate.Rules { - inRange := false - for _, group := range rule.NotifyGroups { - if groupID == group { - inRange = true - break - } - } - if !inRange { + if rule.Name != triggerRule.Config.TargetUpdateRuleName { continue } latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogPage) + if latestVersion == VersionNone { + break + } - replies = r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) - logger.Infof("manualTriggerUpdateMessage %v, version=%v", rule.Name, latestVersion) + updateMessages := r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) + logger.Infof("manualTriggerUpdateNotify %v, version=%v", rule.Name, latestVersion) + + // 发送给配置的目标群组 + nowStr := r.currentTime() + for _, groupID := range rule.NotifyGroups { + rspID := r.cqBot.SendGroupMessage(groupID, updateMessages) + // 广播消息间强行间隔一秒 + time.Sleep(time.Second) + if rspID == -1 { + logger.Errorf("【%v Failed】 %v groupID=%v updateMessages=%v err=%v", rule.Name, nowStr, groupID, updateMessages, rspID) + continue + } + logger.Infof("【%v】 %v groupID=%v updateMessages=%v", rule.Name, nowStr, groupID, updateMessages) + } + replies = message.NewSendingMessage() + replies.Append(message.NewText("已发送更新公告到更新规则中指定的群组")) return replies } diff --git a/qqrobot/config.go b/qqrobot/config.go index 0ae22411b..b0e3ea360 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -82,12 +82,12 @@ type GroupTypeConfig struct { type ActionType string const ( - actionTypeGuide ActionType = "guide" - actionTypeCommand ActionType = "command" - actionTypeFood ActionType = "food" - actiontypeAichat ActionType = "ai_chat" - actiontypeSendupdatemessage ActionType = "send_update_message" - actiontypeRepeater ActionType = "repeater" + actionTypeGuide ActionType = "guide" + actionTypeCommand ActionType = "command" + actionTypeFood ActionType = "food" + actiontypeAichat ActionType = "ai_chat" + actiontypeManaualTriggerUpdateNotify ActionType = "manaual_trigger_update_notify" + actiontypeRepeater ActionType = "repeater" ) var ( @@ -140,6 +140,7 @@ type RuleConfig struct { GitChangelogPage string `toml:"git_changelog_page"` // 某git仓库的changelog的url,若设定,则将请求这个网页,从中解析出最新的版本号和更新信息,并替换到GuideContent中的$git_version$和$update_message$ GuideContentHasPermission string `toml:"guide_content_has_permission"` // 当有权限触发该指令时的回复 GuideContentHasNoPermission string `toml:"guide_content_has_no_permission"` // 当无权限触发该指令时的回复 + TargetUpdateRuleName string `toml:"target_update_rule_name"` // 目标更新规则的名称 } // TimePeriod 时间规则 diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 5767e7e36..c5c538300 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -537,13 +537,13 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { if reply != "" { replies.Append(message.NewText(reply)) } - case actiontypeSendupdatemessage: + case actiontypeManaualTriggerUpdateNotify: if isAdmin { // 手动触发更新通知 - if res := r.manualTriggerUpdateMessage(groupID); res != nil { + if res := r.manualTriggerUpdateNotify(rule); res != nil { replies.Elements = append(replies.Elements, res.Elements...) } else { - replies.Append(message.NewText("当前群组没有配置检查更新哦~")) + replies.Append(message.NewText("未找到指定更新规则,或者网络请求失败")) } } else { replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) From 0cc0a20848d5e861014379f639cfa4052cde377c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 22 Jan 2022 21:51:20 +0800 Subject: [PATCH 260/612] .github: update bug-report template --- .github/ISSUE_TEMPLATE/bug-report.yaml | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index be575c6dc..0aacee17e 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -26,7 +26,9 @@ body: options: - label: "我已经仔细阅读上述教程和 [\"提问前需知\"](https://github.com/Mrs4s/go-cqhttp/issues/633)" required: true - - label: 我已知晓并同意,如果我不遵循以下格式提交 Issue,或者我使用的并非最新版本,或者我没有提供足够的环境信息,我的 Issue 可能会被无条件自动关闭或/并锁定。 + - label: "我已经使用 dev 分支版本测试过,问题依旧存在。" + required: true + - label: "我已经在 [Issue Tracker](……/) 中找过我要提出的问题,没有找到相同问题的ISSUE。" required: true - label: 我已知晓并同意,此处仅用于汇报程序中存在的问题。若这个 Issue 是关于其他非程序本身问题,则我的 Issue 可能会被无条件自动关闭或/并锁定。(这些问题应当在 Discussion 板块提出。) required: true @@ -39,25 +41,10 @@ body: 请根据实际使用环境修改以下信息。 # Env | go-cqhttp Version - - type: dropdown + - type: input id: env-gocq-ver attributes: label: go-cqhttp 版本 - description: 选择正在使用的版本 (未列出的版本为已不支持,请更新到最新版本。) - options: - - v1.0.0-beta8-fix2 - - v1.0.0-beta8-fix1 - - v1.0.0-beta8 - - v1.0.0-beta7-fix2 - - v1.0.0-beta7-fix1 - - v1.0.0-beta7 - - v1.0.0-beta6 - - v1.0.0-beta5 - - v1.0.0-beta4 - - v1.0.0-beta3 - - v1.0.0-beta2 - - v1.0.0-beta1 - - 更老的版本 validations: required: true From 81d910fb0578bff24fba5c9cc0d400ccd193e692 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 23 Jan 2022 22:47:13 +0800 Subject: [PATCH 261/612] dep: update MiraiGo & websocket --- go.mod | 5 +++-- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index e78853664..50855b431 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da - github.com/RomiChan/websocket v1.4.3-0.20220112132122-0fb50fcee646 + github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747 + github.com/RomiChan/websocket v1.4.3-0.20220123141821-6e64c2ab603f github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 @@ -32,6 +32,7 @@ require ( require ( github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/gocq/rs v1.0.1 // indirect github.com/golang/snappy v0.0.1 // indirect diff --git a/go.sum b/go.sum index c6f226871..a33aad27e 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,12 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da h1:uqY6vOKvcCBKlIDaZA6/YiDNuDP5VuACmWe2mntIwfc= -github.com/Mrs4s/MiraiGo v0.0.0-20211228140953-ab72f3dc28da/go.mod h1:VrmhZC9o+uKB9AKRy5bZZxE5EJwJhAmo7Xn/c2tzkZI= +github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747 h1:i9IcyQOVc3eYHXAXgTAMsEdAcGkxpbjBEvVb87z50ZA= +github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747/go.mod h1:rtKLkhMEi2YjsrXaNztT4uagUOPBxf6a+TNREkG097I= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpSNVp5mnuloRMd3Rj2Lfg5biZ3emv//c= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= -github.com/RomiChan/websocket v1.4.3-0.20220112132122-0fb50fcee646 h1:v9aUYiWu84Z6UrW+mkVvw/2mYJx41OFK26Hrr+FgqeM= -github.com/RomiChan/websocket v1.4.3-0.20220112132122-0fb50fcee646/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= +github.com/RomiChan/websocket v1.4.3-0.20220123141821-6e64c2ab603f h1:DNf7kjDusL3dY9HW2CnHDYNCEEzcFdLmDfb0BJV/tzk= +github.com/RomiChan/websocket v1.4.3-0.20220123141821-6e64c2ab603f/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -18,6 +18,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= +github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= +github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4= github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= From 8723ff87135ce38141f64fa9559047e659719d1f Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 23 Jan 2022 22:58:17 +0800 Subject: [PATCH 262/612] dep: update websocket again --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 50855b431..cc2f99802 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747 - github.com/RomiChan/websocket v1.4.3-0.20220123141821-6e64c2ab603f + github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index a33aad27e..47235e346 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747 h1:i9IcyQOVc3eYHXAXg github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747/go.mod h1:rtKLkhMEi2YjsrXaNztT4uagUOPBxf6a+TNREkG097I= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpSNVp5mnuloRMd3Rj2Lfg5biZ3emv//c= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= -github.com/RomiChan/websocket v1.4.3-0.20220123141821-6e64c2ab603f h1:DNf7kjDusL3dY9HW2CnHDYNCEEzcFdLmDfb0BJV/tzk= -github.com/RomiChan/websocket v1.4.3-0.20220123141821-6e64c2ab603f/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= +github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= +github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From a5dd0bfa1ff9f5f8e6867b879248a9d17bfec539 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 24 Jan 2022 19:52:13 +0800 Subject: [PATCH 263/612] feat: disable temp session by default --- coolq/bot.go | 4 ++++ docs/guild.md | 9 +++++---- go.mod | 1 + go.sum | 10 +++++++++- internal/base/flag.go | 2 ++ modules/config/config.go | 13 +++++++------ modules/config/default_config.yml | 2 ++ 7 files changed, 30 insertions(+), 11 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 2b7ce53d9..099be8aa7 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -314,6 +314,10 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen id = bot.InsertPrivateMessage(msg) } case ok || groupID != 0: // 临时会话 + if !base.AllowTempSession { + log.Warnf("发送临时会话消息失败: 已关闭临时会话信息发送功能") + return -1 + } switch { case groupID != 0 && bot.Client.FindGroup(groupID) == nil: log.Errorf("错误: 找不到群(%v)", groupID) diff --git a/docs/guild.md b/docs/guild.md index 93582cfd7..9a20285aa 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -30,7 +30,8 @@ API以及字段相关命名均为参考QQ官方命名或相似产品命名规则 - 所有事件的 `self_id` 均为 BOT 的QQ号. `tiny_id` 将放在 `self_tiny_id` 字段 - 遵循我们一贯的原则, 将不会支持主动加频道/主动拉人/红包相关消息类型 - 频道相关的API仅能在 `Android Phone` 和 `iPad` 协议上使用. -- 由于频道相关ID的数据类型均为 `uint64` , 为保证不超过某些语言的安全值范围, 在 `v1.0.0-beta8-fix3` 以后, 所有ID相关数据将转换为 `string` 类型, API调用 `uint64` 或 `string` 均可接受. +- 由于频道相关ID的数据类型均为 `uint64` , 为保证不超过某些语言的安全值范围, 在 `v1.0.0-beta8-fix3` 以后, 所有ID相关数据将转换为 `string` 类型, API调用 `uint64` + 或 `string` 均可接受. - 为保证一致性, 所有频道接口返回的 `用户ID` 均命名为 `tiny_id`, 所有频道相关接口的 `用户ID` 入参均命名为 `user_id` ## API @@ -140,7 +141,7 @@ SlowModeInfo: 终结点: `/get_guild_member_list` > 由于频道人数较多(数万), 请尽量不要全量拉取成员列表, 这将会导致严重的性能问题 -> +> > 尽量使用 `get_guild_member_profile` 接口代替全量拉取 **参数** @@ -171,7 +172,7 @@ GuildMemberInfo: | `role_name` | string | 所在权限组名称 | > 默认情况下频道管理员的权限组ID为 `2`, 部分频道可能会另行创建, 需手动判断 -> +> > 此接口仅展现最新的权限组, 获取用户加入的所有权限组请使用 `get_guild_member_profile` 接口 ### 单独获取频道成员信息 @@ -286,7 +287,7 @@ FeedContent: | `url_quote` | 链接引用 | | `channel_quote` | 子频道引用 | -#### 内容类型对应数据列表: +#### 内容类型对应数据列表: - `text` diff --git a/go.mod b/go.mod index cc2f99802..1bc39f8a3 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/go-stack/stack v1.8.0 // indirect github.com/gocq/rs v1.0.1 // indirect github.com/golang/snappy v0.0.1 // indirect + github.com/google/go-cmp v0.5.5 // indirect github.com/google/uuid v1.1.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect diff --git a/go.sum b/go.sum index 47235e346..597fdb73b 100644 --- a/go.sum +++ b/go.sum @@ -29,11 +29,13 @@ github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98/go.mod h1:E5TBHc60dsWt github.com/gocq/rs v1.0.1 h1:ng7nhXmnx3SnfM0DOqmbP6GmQp1xGwRG9XmBiLFDWuM= github.com/gocq/rs v1.0.1/go.mod h1:8oaQnRvqn1fMh8i5zsetgQo03OUXksJV1k+dpmExxcY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -90,6 +92,7 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -119,6 +122,7 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -134,8 +138,10 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -152,6 +158,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/base/flag.go b/internal/base/flag.go index 8cc0ab6cb..933e8b68f 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -39,6 +39,7 @@ var ( LogForceNew bool // 是否在每次启动时强制创建全新的文件储存日志 LogColorful bool // 是否启用日志颜色 FastStart bool // 是否为快速启动 + AllowTempSession bool // 是否允许发送临时会话信息 PostFormat string // 上报格式 string or array Proxy string // 存储 proxy_rewrite,用于设置代理 @@ -85,6 +86,7 @@ func Init() { SkipMimeScan = conf.Message.SkipMimeScan ReportSelfMessage = conf.Message.ReportSelfMessage UseSSOAddress = conf.Account.UseSSOAddress + AllowTempSession = conf.Account.AllowTempSession } { // others Proxy = conf.Message.ProxyRewrite diff --git a/modules/config/config.go b/modules/config/config.go index 56250d618..89ee1c9e6 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -31,12 +31,13 @@ type Reconnect struct { // Account 账号配置 type Account struct { - Uin int64 `yaml:"uin"` - Password string `yaml:"password"` - Encrypt bool `yaml:"encrypt"` - Status int `yaml:"status"` - ReLogin *Reconnect `yaml:"relogin"` - UseSSOAddress bool `yaml:"use-sso-address"` + Uin int64 `yaml:"uin"` + Password string `yaml:"password"` + Encrypt bool `yaml:"encrypt"` + Status int `yaml:"status"` + ReLogin *Reconnect `yaml:"relogin"` + UseSSOAddress bool `yaml:"use-sso-address"` + AllowTempSession bool `yaml:"allow-temp-session"` } // Config 总配置文件 diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 50570a5f5..69e23cd93 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -13,6 +13,8 @@ account: # 账号相关 # 是否使用服务器下发的新地址进行重连 # 注意, 此设置可能导致在海外服务器上连接情况更差 use-sso-address: true + # 是否允许发送临时会话消息 + allow-temp-session: false heartbeat: # 心跳频率, 单位秒 From 54c659a30753e08bf011bae9b4da7d89d305654f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 25 Jan 2022 09:45:11 +0800 Subject: [PATCH 264/612] =?UTF-8?q?feat:=20=E6=9A=82=E6=97=B6=E5=B1=8F?= =?UTF-8?q?=E8=94=BD=E5=9B=9E=E5=A4=8D=E4=B8=B4=E6=97=B6=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 5767e7e36..aaf3dcb94 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -909,9 +909,15 @@ func (r *QQRobot) onPrivateOrTempMessage(senderFriendUin int64, tempGroupID int6 var rspID int32 if senderFriendUin != 0 { + // 好友 rspID = r.cqBot.SendPrivateMessage(senderFriendUin, 0, replies) } else { - rspID = r.cqBot.SendPrivateMessage(tempUin, tempGroupID, replies) + // 临时消息 + // 重要:现在观测到大量冻结,疑似腾讯暗改协议,请暂时不要使用gocq发送临时消息 - 摸摸摸鱼中中中 发表于 01-23 12:00 + rspID = -1 + logger.Warn("目前临时消息会被冻结,将跳过实际发送过程。", p(m), p(replies)) + + // rspID = r.cqBot.SendPrivateMessage(tempUin, tempGroupID, replies) } if rspID == -1 { From a4789d0469fc56a2a0f1261295c5767e82421494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 25 Jan 2022 09:52:04 +0800 Subject: [PATCH 265/612] fix: lint --- qqrobot/qq_robot.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index f123f315a..2bef9c260 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -917,7 +917,9 @@ func (r *QQRobot) onPrivateOrTempMessage(senderFriendUin int64, tempGroupID int6 rspID = -1 logger.Warn("目前临时消息会被冻结,将跳过实际发送过程。", p(m), p(replies)) - // rspID = r.cqBot.SendPrivateMessage(tempUin, tempGroupID, replies) + if false { + rspID = r.cqBot.SendPrivateMessage(tempUin, tempGroupID, replies) + } } if rspID == -1 { From c214026d15b82252526feb7ad738e09fb0e06fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 25 Jan 2022 10:27:30 +0800 Subject: [PATCH 266/612] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=EF=BC=8C=E8=A7=A3=E5=86=B3go=20mod=20tidy=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 5 ++++- go.sum | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index be82d2c9a..2a4f5d393 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,10 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 ) -require github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect +require ( + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect +) // 以下为go-cqhttp原本的依赖 require ( diff --git a/go.sum b/go.sum index 80cb74acb..567e6349c 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,7 @@ github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98/go.mod h1:E5TBHc60dsWt github.com/gocq/rs v1.0.1 h1:ng7nhXmnx3SnfM0DOqmbP6GmQp1xGwRG9XmBiLFDWuM= github.com/gocq/rs v1.0.1/go.mod h1:8oaQnRvqn1fMh8i5zsetgQo03OUXksJV1k+dpmExxcY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -64,6 +65,7 @@ github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkL github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -94,6 +96,7 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDq github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -103,6 +106,7 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 h1:2yIS github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 h1:m7y3kw3JfAmyIiz/mwWEcuV+WayjrxrfZpiqW94uJU4= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= +github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -134,6 +138,7 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -150,8 +155,10 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -168,6 +175,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -175,8 +184,9 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 7dc4a0bf5e718a9d9576a685efe09bc18a14e2b3 Mon Sep 17 00:00:00 2001 From: Ink33 Date: Wed, 26 Jan 2022 20:30:41 +0800 Subject: [PATCH 267/612] style: add some comments --- cmd/gocq/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 01bc77bdc..842a24526 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -134,9 +134,8 @@ func Main() { log.SetLevel(log.DebugLevel) log.SetReportCaller(true) log.Warnf("已开启Debug模式.") - log.Debugf("开发交流群: 192548878") + // log.Debugf("开发交流群: 192548878") } - log.Info("用户交流群: 721829413") if !global.PathExists("device.json") { log.Warn("虚拟设备信息不存在, 将自动生成随机设备.") client.GenRandomDevice() From be3b17dc6b9fcfd12637a891e2cfcf3ca6477a44 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 30 Jan 2022 14:28:22 +0800 Subject: [PATCH 268/612] fix: support join query from http query&form Fixes #1241 Fixes #1325 --- go.sum | 7 ------- server/http.go | 25 +++++++++++++++++++++---- server/http_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 server/http_test.go diff --git a/go.sum b/go.sum index 597fdb73b..62a10c1dd 100644 --- a/go.sum +++ b/go.sum @@ -29,7 +29,6 @@ github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98/go.mod h1:E5TBHc60dsWt github.com/gocq/rs v1.0.1 h1:ng7nhXmnx3SnfM0DOqmbP6GmQp1xGwRG9XmBiLFDWuM= github.com/gocq/rs v1.0.1/go.mod h1:8oaQnRvqn1fMh8i5zsetgQo03OUXksJV1k+dpmExxcY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -92,7 +91,6 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -122,7 +120,6 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -138,10 +135,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -158,8 +153,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/server/http.go b/server/http.go index d0bebcd77..1df3cb545 100644 --- a/server/http.go +++ b/server/http.go @@ -12,6 +12,7 @@ import ( "net/http" "net/url" "os" + "regexp" "strconv" "strings" "time" @@ -142,11 +143,19 @@ func init() { config.AddServer(&config.Server{Brief: "HTTP通信", Default: httpDefault, ParseEnv: readEnvConfig}) } -func (h *httpCtx) Get(s string) gjson.Result { - j := h.json.Get(s) - if j.Exists() { - return j +var joinQuery = regexp.MustCompile(`\[(.+?),(.+?)]\.0`) + +func (h *httpCtx) get(s string, join bool) gjson.Result { + // support gjson advanced syntax: + // h.Get("[a,b].0") see usage in http_test.go + if join && joinQuery.MatchString(s) { + matched := joinQuery.FindStringSubmatch(s) + if r := h.get(matched[1], false); r.Exists() { + return r + } + return h.get(matched[2], false) } + validJSONParam := func(p string) bool { return (strings.HasPrefix(p, "{") || strings.HasPrefix(p, "[")) && gjson.Valid(p) } @@ -169,6 +178,14 @@ func (h *httpCtx) Get(s string) gjson.Result { return gjson.Result{} } +func (h *httpCtx) Get(s string) gjson.Result { + j := h.json.Get(s) + if j.Exists() { + return j + } + return h.get(s, true) +} + func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request) { var ctx httpCtx contentType := request.Header.Get("Content-Type") diff --git a/server/http_test.go b/server/http_test.go new file mode 100644 index 000000000..1d2a92ca7 --- /dev/null +++ b/server/http_test.go @@ -0,0 +1,42 @@ +package server + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" +) + +func TestHttpCtx_Get(t *testing.T) { + cases := []struct { + ctx *httpCtx + key string + expected string + }{ + { + ctx: &httpCtx{ + json: gjson.Result{}, + query: url.Values{ + "sub_type": []string{"hello"}, + "type": []string{"world"}, + }, + }, + key: "[sub_type,type].0", + expected: "hello", + }, + { + ctx: &httpCtx{ + json: gjson.Result{}, + query: url.Values{ + "type": []string{"114514"}, + }, + }, + key: "[sub_type,type].0", + expected: "114514", + }, + } + for _, c := range cases { + assert.Equal(t, c.expected, c.ctx.Get(c.key).String()) + } +} From d2a58014bce4e9207ded2de34808e93b23048ec7 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 30 Jan 2022 14:52:29 +0800 Subject: [PATCH 269/612] feat: support foreach in btree --- internal/btree/btree.go | 39 +++++++++++++++++++++++++++--------- internal/btree/btree_test.go | 6 ++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/internal/btree/btree.go b/internal/btree/btree.go index ccbdb00b4..24b5f992c 100644 --- a/internal/btree/btree.go +++ b/internal/btree/btree.go @@ -475,15 +475,7 @@ func (d *DB) Insert(chash *byte, data []byte) { d.flushSuper() } -// Get look up item with the given key 'hash' in the database file. Length of the -// item is stored in 'len'. Returns a pointer to the contents of the item. -// The returned pointer should be released with free() after use. -func (d *DB) Get(hash *byte) []byte { - off := d.lookup(d.top, hash) - if off == 0 { - return nil - } - +func (d *DB) readValue(off int64) []byte { d.fd.Seek(off, io.SeekStart) length, err := read32(d.fd) if err != nil { @@ -497,6 +489,17 @@ func (d *DB) Get(hash *byte) []byte { return data[:n] } +// Get look up item with the given key 'hash' in the database file. Length of the +// item is stored in 'len'. Returns a pointer to the contents of the item. +// The returned pointer should be released with free() after use. +func (d *DB) Get(hash *byte) []byte { + off := d.lookup(d.top, hash) + if off == 0 { + return nil + } + return d.readValue(off) +} + // Delete remove item with the given key 'hash' from the database file. func (d *DB) Delete(hash *byte) error { var h [hashSize]byte @@ -522,3 +525,21 @@ func (d *DB) Delete(hash *byte) error { d.flushSuper() return nil } + +func (d *DB) Foreach(iter func(key [16]byte, value []byte)) { + top := d.get(d.top) + d.iterate(top, iter) +} + +func (d *DB) iterate(table *table, iter func(key [16]byte, value []byte)) { + for i := 0; i < table.size; i++ { + item := table.items[i] + offset := item.offset + iter(item.hash, d.readValue(offset)) + + if item.child != 0 { + child := d.get(item.child) + d.iterate(child, iter) + } + } +} diff --git a/internal/btree/btree_test.go b/internal/btree/btree_test.go index d617570b3..accec46b4 100644 --- a/internal/btree/btree_test.go +++ b/internal/btree/btree_test.go @@ -45,6 +45,12 @@ func TestBtree(t *testing.T) { bt, err = Open(f) assert2.NoError(t, err) + var ss []string + bt.Foreach(func(key [16]byte, value []byte) { + ss = append(ss, string(value)) + }) + assert2.ElementsMatch(t, tests, ss) + for i, tt := range tests { assert2.Equal(t, []byte(tt), bt.Get(sha[i])) } From 6c7445772cbe9dd1a6386b3a409c5504f50f5363 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 30 Jan 2022 15:35:14 +0800 Subject: [PATCH 270/612] fix: fix btree iterate --- internal/btree/btree.go | 6 ++++++ internal/btree/btree_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/internal/btree/btree.go b/internal/btree/btree.go index 24b5f992c..95068f65a 100644 --- a/internal/btree/btree.go +++ b/internal/btree/btree.go @@ -526,6 +526,7 @@ func (d *DB) Delete(hash *byte) error { return nil } +// Foreach iterates over all items in the database file. func (d *DB) Foreach(iter func(key [16]byte, value []byte)) { top := d.get(d.top) d.iterate(top, iter) @@ -542,4 +543,9 @@ func (d *DB) iterate(table *table, iter func(key [16]byte, value []byte)) { d.iterate(child, iter) } } + item := table.items[table.size] + if item.child != 0 { + child := d.get(item.child) + d.iterate(child, iter) + } } diff --git a/internal/btree/btree_test.go b/internal/btree/btree_test.go index accec46b4..a080fbdf9 100644 --- a/internal/btree/btree_test.go +++ b/internal/btree/btree_test.go @@ -5,6 +5,7 @@ import ( "os" "testing" + "github.com/Mrs4s/MiraiGo/utils" assert2 "github.com/stretchr/testify/assert" ) @@ -65,3 +66,27 @@ func TestBtree(t *testing.T) { assert2.NoError(t, bt.Close()) } + +func TestDB_Foreach(t *testing.T) { + const elemSize = 100 + set := make([]string, elemSize) + for i := 0; i < elemSize; i++ { + set[i] = utils.RandomString(20) + } + f := tempfile(t) + defer os.Remove(f) + bt, err := Create(f) + defer bt.Close() + assert2.NoError(t, err) + for _, v := range set { + hash := sha1.New() + hash.Write([]byte(v)) + bt.Insert(&hash.Sum(nil)[0], []byte(v)) + } + + var ss []string + bt.Foreach(func(key [16]byte, value []byte) { + ss = append(ss, string(value)) + }) + assert2.ElementsMatch(t, set, ss) +} From e8bf49702219dc0cda436a83c255daf8d51aa17d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 30 Jan 2022 15:50:02 +0800 Subject: [PATCH 271/612] fix: fix btree iterate for empty db --- internal/btree/btree.go | 6 ++++-- internal/btree/btree_test.go | 41 ++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/internal/btree/btree.go b/internal/btree/btree.go index 95068f65a..7b3b49cb7 100644 --- a/internal/btree/btree.go +++ b/internal/btree/btree.go @@ -528,8 +528,10 @@ func (d *DB) Delete(hash *byte) error { // Foreach iterates over all items in the database file. func (d *DB) Foreach(iter func(key [16]byte, value []byte)) { - top := d.get(d.top) - d.iterate(top, iter) + if d.top != 0 { + top := d.get(d.top) + d.iterate(top, iter) + } } func (d *DB) iterate(table *table, iter func(key [16]byte, value []byte)) { diff --git a/internal/btree/btree_test.go b/internal/btree/btree_test.go index a080fbdf9..dfb2b2e31 100644 --- a/internal/btree/btree_test.go +++ b/internal/btree/btree_test.go @@ -27,7 +27,8 @@ func TestBtree(t *testing.T) { f := tempfile(t) defer os.Remove(f) bt, err := Create(f) - assert2.NoError(t, err) + assert := assert2.New(t) + assert.NoError(err) tests := []string{ "hello world", @@ -42,51 +43,55 @@ func TestBtree(t *testing.T) { sha[i] = &hash.Sum(nil)[0] bt.Insert(sha[i], []byte(tt)) } - assert2.NoError(t, bt.Close()) + assert.NoError(bt.Close()) bt, err = Open(f) - assert2.NoError(t, err) + assert.NoError(err) var ss []string bt.Foreach(func(key [16]byte, value []byte) { ss = append(ss, string(value)) }) - assert2.ElementsMatch(t, tests, ss) + assert.ElementsMatch(tests, ss) for i, tt := range tests { - assert2.Equal(t, []byte(tt), bt.Get(sha[i])) + assert.Equal([]byte(tt), bt.Get(sha[i])) } for i := range tests { - assert2.NoError(t, bt.Delete(sha[i])) + assert.NoError(bt.Delete(sha[i])) } for i := range tests { - assert2.Equal(t, []byte(nil), bt.Get(sha[i])) + assert.Equal([]byte(nil), bt.Get(sha[i])) } - - assert2.NoError(t, bt.Close()) + assert.NoError(bt.Close()) } -func TestDB_Foreach(t *testing.T) { - const elemSize = 100 - set := make([]string, elemSize) +func testForeach(t *testing.T, elemSize int) { + expected := make([]string, elemSize) for i := 0; i < elemSize; i++ { - set[i] = utils.RandomString(20) + expected[i] = utils.RandomString(20) } f := tempfile(t) defer os.Remove(f) bt, err := Create(f) defer bt.Close() assert2.NoError(t, err) - for _, v := range set { + for _, v := range expected { hash := sha1.New() hash.Write([]byte(v)) bt.Insert(&hash.Sum(nil)[0], []byte(v)) } - - var ss []string + var got []string bt.Foreach(func(key [16]byte, value []byte) { - ss = append(ss, string(value)) + got = append(got, string(value)) }) - assert2.ElementsMatch(t, set, ss) + assert2.ElementsMatch(t, expected, got) +} + +func TestDB_Foreach(t *testing.T) { + elemSizes := []int{0, 5, 100, 200} + for _, size := range elemSizes { + testForeach(t, size) + } } From 1f55bde728767b7e4d52f32637af86f5c615ae68 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 30 Jan 2022 16:00:30 +0800 Subject: [PATCH 272/612] ci: make lint happy --- internal/btree/btree_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/btree/btree_test.go b/internal/btree/btree_test.go index dfb2b2e31..5d16c5410 100644 --- a/internal/btree/btree_test.go +++ b/internal/btree/btree_test.go @@ -75,7 +75,6 @@ func testForeach(t *testing.T, elemSize int) { f := tempfile(t) defer os.Remove(f) bt, err := Create(f) - defer bt.Close() assert2.NoError(t, err) for _, v := range expected { hash := sha1.New() @@ -87,6 +86,7 @@ func testForeach(t *testing.T, elemSize int) { got = append(got, string(value)) }) assert2.ElementsMatch(t, expected, got) + assert2.NoError(t, bt.Close()) } func TestDB_Foreach(t *testing.T) { From b0fb02e890c9afa69026ad21e2878e3c28db7170 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 30 Jan 2022 16:02:26 +0800 Subject: [PATCH 273/612] .github: update bug-report template --- .github/ISSUE_TEMPLATE/bug-report.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 0aacee17e..29fc048f2 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -26,9 +26,9 @@ body: options: - label: "我已经仔细阅读上述教程和 [\"提问前需知\"](https://github.com/Mrs4s/go-cqhttp/issues/633)" required: true - - label: "我已经使用 dev 分支版本测试过,问题依旧存在。" + - label: "我已经使用 [dev分支版本](https://github.com/Mrs4s/go-cqhttp/actions/workflows/ci.yml) 测试过,问题依旧存在。" required: true - - label: "我已经在 [Issue Tracker](……/) 中找过我要提出的问题,没有找到相同问题的ISSUE。" + - label: "我已经在 [Issue Tracker](https://github.com/Mrs4s/go-cqhttp/actions/workflows/ci.yml) 中找过我要提出的问题,没有找到相同问题的ISSUE。" required: true - label: 我已知晓并同意,此处仅用于汇报程序中存在的问题。若这个 Issue 是关于其他非程序本身问题,则我的 Issue 可能会被无条件自动关闭或/并锁定。(这些问题应当在 Discussion 板块提出。) required: true From f8fa906a952dff1cb111c40411d1ae8632e83326 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 30 Jan 2022 16:04:53 +0800 Subject: [PATCH 274/612] .github: fix issue link --- .github/ISSUE_TEMPLATE/bug-report.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 29fc048f2..1cee16c7b 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -28,7 +28,7 @@ body: required: true - label: "我已经使用 [dev分支版本](https://github.com/Mrs4s/go-cqhttp/actions/workflows/ci.yml) 测试过,问题依旧存在。" required: true - - label: "我已经在 [Issue Tracker](https://github.com/Mrs4s/go-cqhttp/actions/workflows/ci.yml) 中找过我要提出的问题,没有找到相同问题的ISSUE。" + - label: "我已经在 [Issue Tracker](https://github.com/Mrs4s/go-cqhttp/issues) 中找过我要提出的问题,没有找到相同问题的ISSUE。" required: true - label: 我已知晓并同意,此处仅用于汇报程序中存在的问题。若这个 Issue 是关于其他非程序本身问题,则我的 Issue 可能会被无条件自动关闭或/并锁定。(这些问题应当在 Discussion 板块提出。) required: true From f88f51ceed3665942e9da3b507640c11e1c65d5d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 6 Feb 2022 16:04:03 +0800 Subject: [PATCH 275/612] cmd/gocq: switch `faststart` over to a flag --- cmd/gocq/main.go | 2 -- internal/base/flag.go | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 01bc77bdc..84feb4aa8 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -115,8 +115,6 @@ func Main() { byteKey = []byte(arg[p]) para.Hide(p) } - case "faststart": - base.FastStart = true } } } diff --git a/internal/base/flag.go b/internal/base/flag.go index 933e8b68f..27940538a 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -64,6 +64,7 @@ func Parse() { flag.BoolVar(&LittleH, "h", false, "this Help") flag.StringVar(&LittleWD, "w", "", "cover the working directory") d := flag.Bool("D", false, "debug mode") + flag.BoolVar(&FastStart, "faststart", false, "skip waiting 5 seconds") flag.Parse() if *d { From 343cee25cf9de56a486914fe6f696a92c48ac45a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 6 Feb 2022 17:37:28 +0800 Subject: [PATCH 276/612] =?UTF-8?q?feat:=20faker2=E4=BB=93=E5=BA=93?= =?UTF-8?q?=E8=B7=91=E8=B7=AF=E4=BA=86=EF=BC=8C=E8=B0=83=E6=95=B4=E4=B8=80?= =?UTF-8?q?=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/expired_test.go | 2 +- qqrobot/qinglong/query.go | 4 ++-- qqrobot/qinglong/summary_test.go | 2 +- .../2021-12-17-12-00-01.log | 0 .../2021-12-13-09-30-00.log | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename qqrobot/qinglong/test_data/log/{shufflewzc_faker2_jd_CheckCK => ccwav_QLScript2_jd_CheckCK}/2021-12-17-12-00-01.log (100%) rename qqrobot/qinglong/test_data/log/{shufflewzc_faker2_jd_bean_change => ccwav_QLScript2_jd_bean_change}/2021-12-13-09-30-00.log (100%) diff --git a/qqrobot/qinglong/expired_test.go b/qqrobot/qinglong/expired_test.go index d0df8049e..a109e826d 100644 --- a/qqrobot/qinglong/expired_test.go +++ b/qqrobot/qinglong/expired_test.go @@ -13,7 +13,7 @@ func Test_parseCookieExpired(t *testing.T) { return result } - logPath := getPath("log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log") + logPath := getPath("log/ccwav_QLScript2_jd_CheckCK/2021-12-17-12-00-01.log") info := QueryCookieInfo("pin_1") assert.Equal(t, "pin_1 状态正常!", getResult(info, logPath)) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index c68eb7a75..b70245269 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -70,7 +70,7 @@ func QuerySummary(info *JdCookieInfo) string { return "" } - summaryDir := getPath("log/shufflewzc_faker2_jd_bean_change") + summaryDir := getPath("log/ccwav_QLScript2_jd_bean_change") logFiles, err := ioutil.ReadDir(summaryDir) if err != nil { logger.Errorf("read log dir failed, err=%v", err) @@ -103,7 +103,7 @@ func QueryCookieExpired(info *JdCookieInfo) string { return "" } - checkCookieDir := getPath("log/shufflewzc_faker2_jd_CheckCK") + checkCookieDir := getPath("log/ccwav_QLScript2_jd_CheckCK") logFiles, err := ioutil.ReadDir(checkCookieDir) if err != nil { logger.Errorf("read log dir failed, err=%v", err) diff --git a/qqrobot/qinglong/summary_test.go b/qqrobot/qinglong/summary_test.go index eabdb0f80..374a43b52 100644 --- a/qqrobot/qinglong/summary_test.go +++ b/qqrobot/qinglong/summary_test.go @@ -10,7 +10,7 @@ import ( // 2021/12/13 20:48 by fzls func Test_parseSummary(t *testing.T) { - logPath := getPath("log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log") + logPath := getPath("log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log") info := QueryCookieInfo("pin_1") assert.Contains(t, parseSummary(info, logPath), "测试账号-1") diff --git a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log b/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_CheckCK/2021-12-17-12-00-01.log similarity index 100% rename from qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_CheckCK/2021-12-17-12-00-01.log rename to qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_CheckCK/2021-12-17-12-00-01.log diff --git a/qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log b/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log similarity index 100% rename from qqrobot/qinglong/test_data/log/shufflewzc_faker2_jd_bean_change/2021-12-13-09-30-00.log rename to qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log From 57d1be0ee505c79a9cc7bcc1c7d589264abd4063 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 6 Feb 2022 18:39:22 +0800 Subject: [PATCH 277/612] db/leveldb: register `message.RedBagMessageType` Fixes #1323 --- db/leveldb/leveldb.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/db/leveldb/leveldb.go b/db/leveldb/leveldb.go index 9955c1f88..02369320c 100644 --- a/db/leveldb/leveldb.go +++ b/db/leveldb/leveldb.go @@ -3,9 +3,11 @@ package leveldb import ( "bytes" "encoding/gob" - "github.com/Mrs4s/MiraiGo/utils" "path" + "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/MiraiGo/utils" + "github.com/Mrs4s/MiraiGo/binary" "github.com/pkg/errors" "github.com/syndtr/goleveldb/leveldb" @@ -35,6 +37,7 @@ func init() { gob.Register(db.StoredGroupMessage{}) gob.Register(db.StoredPrivateMessage{}) gob.Register(db.StoredGuildChannelMessage{}) + gob.Register(message.RedBagMessageType(0)) db.Register("leveldb", func(node yaml.Node) db.Database { conf := new(config.LevelDBConfig) From 82b0c69b1b75c060cab67fd83760438ec34659c0 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 6 Feb 2022 22:18:47 +0800 Subject: [PATCH 278/612] config: remove old env config Fixes #1219 --- docs/config.md | 10 ++++++++ internal/param/param.go | 35 -------------------------- modules/config/config.go | 47 +++-------------------------------- modules/config/config_test.go | 30 ++++++++++++++++++++++ server/http.go | 45 +-------------------------------- server/websocket.go | 44 -------------------------------- 6 files changed, 45 insertions(+), 166 deletions(-) create mode 100644 modules/config/config_test.go diff --git a/docs/config.md b/docs/config.md index d3a2df226..93a299bee 100644 --- a/docs/config.md +++ b/docs/config.md @@ -167,6 +167,16 @@ database: # 数据库相关设置 > 注5:关于MIME扫描, 详见[MIME](file.md#MIME) +### 环境变量 + +go-cqhttp 配置文件可以使用占位符来读取**环境变量**的值。 + +```yaml +account: # 账号相关 + uin: ${CQ_UIN} # 读取环境变量 CQ_UIN + password: ${CQ_PASSWORD} # 读取环境变量 CQ_UIN +``` + ## 在线状态 | 状态 | 值 | diff --git a/internal/param/param.go b/internal/param/param.go index 0b709678b..c38bf1cb2 100644 --- a/internal/param/param.go +++ b/internal/param/param.go @@ -3,7 +3,6 @@ package param import ( "math" - "reflect" "regexp" "strings" "sync" @@ -94,37 +93,3 @@ func Base64DecodeString(s string) ([]byte, error) { n, err := e.Decode(dst, utils.S2B(s)) return dst[:n], err } - -// SetAtDefault 在变量 variable 为默认值 defaultValue 的时候修改为 value -func SetAtDefault(variable, value, defaultValue interface{}) { - v := reflect.ValueOf(variable) - v2 := reflect.ValueOf(value) - if v.Kind() != reflect.Ptr || v.IsNil() { - return - } - v = v.Elem() - if v.Interface() != defaultValue { - return - } - if v.Kind() != v2.Kind() { - return - } - v.Set(v2) -} - -// SetExcludeDefault 在目标值 value 不为默认值 defaultValue 时修改 variable 为 value -func SetExcludeDefault(variable, value, defaultValue interface{}) { - v := reflect.ValueOf(variable) - v2 := reflect.ValueOf(value) - if v.Kind() != reflect.Ptr || v.IsNil() { - return - } - v = v.Elem() - if reflect.Indirect(v2).Interface() != defaultValue { - return - } - if v.Kind() != v2.Kind() { - return - } - v.Set(v2) -} diff --git a/modules/config/config.go b/modules/config/config.go index 89ee1c9e6..e9b049dca 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -7,14 +7,11 @@ import ( "fmt" "os" "regexp" - "strconv" "strings" "sync" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" - - "github.com/Mrs4s/go-cqhttp/internal/param" ) // defaultConfig 默认配置文件 @@ -74,9 +71,8 @@ type Config struct { // Server 的简介和初始配置 type Server struct { - Brief string - Default string - ParseEnv func() (string, *yaml.Node) + Brief string + Default string } // LevelDBConfig leveldb 相关配置 @@ -93,52 +89,17 @@ type MongoDBConfig struct { // Parse 从默认配置文件路径中获取 func Parse(path string) *Config { - fromEnv := os.Getenv("GCQ_UIN") != "" - file, err := os.ReadFile(path) config := &Config{} if err == nil { err = yaml.NewDecoder(strings.NewReader(expand(string(file), os.Getenv))).Decode(config) - if err != nil && !fromEnv { + if err != nil { log.Fatal("配置文件不合法!", err) } - } else if !fromEnv { + } else { generateConfig() os.Exit(0) } - if fromEnv { - // type convert tools - toInt64 := func(str string) int64 { - i, _ := strconv.ParseInt(str, 10, 64) - return i - } - - // load config from environment variable - param.SetAtDefault(&config.Account.Uin, toInt64(os.Getenv("GCQ_UIN")), int64(0)) - param.SetAtDefault(&config.Account.Password, os.Getenv("GCQ_PWD"), "") - param.SetAtDefault(&config.Account.Status, int32(toInt64(os.Getenv("GCQ_STATUS"))), int32(0)) - param.SetAtDefault(&config.Account.ReLogin.Disabled, !param.EnsureBool(os.Getenv("GCQ_RELOGIN_DISABLED"), true), false) - param.SetAtDefault(&config.Account.ReLogin.Delay, uint(toInt64(os.Getenv("GCQ_RELOGIN_DELAY"))), uint(0)) - param.SetAtDefault(&config.Account.ReLogin.MaxTimes, uint(toInt64(os.Getenv("GCQ_RELOGIN_MAX_TIMES"))), uint(0)) - dbConf := &LevelDBConfig{Enable: param.EnsureBool(os.Getenv("GCQ_LEVELDB"), true)} - if config.Database == nil { - config.Database = make(map[string]yaml.Node) - } - config.Database["leveldb"] = func() yaml.Node { - n := &yaml.Node{} - _ = n.Encode(dbConf) - return *n - }() - - for _, s := range serverconfs { - if s.ParseEnv != nil { - name, node := s.ParseEnv() - if node != nil { - config.Servers = append(config.Servers, map[string]yaml.Node{name: *node}) - } - } - } - } return config } diff --git a/modules/config/config_test.go b/modules/config/config_test.go new file mode 100644 index 000000000..295a45083 --- /dev/null +++ b/modules/config/config_test.go @@ -0,0 +1,30 @@ +package config + +import ( + "strings" + "testing" +) + +func Test_expand(t *testing.T) { + tests := []struct { + src string + mapping func(string) string + expected string + }{ + { + src: "foo: ${bar}", + mapping: strings.ToUpper, + expected: "foo: BAR", + }, + { + src: "$123", + mapping: strings.ToUpper, + expected: "$123", + }, + } + for i, tt := range tests { + if got := expand(tt.src, tt.mapping); got != tt.expected { + t.Errorf("testcase %d failed, expected %v but got %v", i, tt.expected, got) + } + } +} diff --git a/server/http.go b/server/http.go index 1df3cb545..dcde4f95e 100644 --- a/server/http.go +++ b/server/http.go @@ -24,7 +24,6 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -97,50 +96,8 @@ const httpDefault = ` # retries-interval: 1000 # 重试时间,单位毫秒,0 时立即 ` -func nilParseUint(s string, base int, bitSize int) *uint64 { - pu, err := strconv.ParseUint(s, base, bitSize) - if err != nil { - return nil - } - return &pu -} - -func readEnvConfig() (string, *yaml.Node) { - if s, ok := os.LookupEnv("GCQ_HTTP_PORT"); !ok || s == "" { - return "", nil - } - - // type convert tools - toInt64 := func(str string) int64 { - i, _ := strconv.ParseInt(str, 10, 64) - return i - } - accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") - node := &yaml.Node{} - httpConf := &HTTPServer{ - Host: "0.0.0.0", - Port: 5700, - MiddleWares: MiddleWares{ - AccessToken: accessTokenEnv, - }, - } - param.SetExcludeDefault(&httpConf.Disabled, param.EnsureBool(os.Getenv("GCQ_HTTP_DISABLE"), false), false) - param.SetExcludeDefault(&httpConf.Host, os.Getenv("GCQ_HTTP_HOST"), "") - param.SetExcludeDefault(&httpConf.Port, int(toInt64(os.Getenv("GCQ_HTTP_PORT"))), 0) - if os.Getenv("GCQ_HTTP_POST_URL") != "" { - httpConf.Post = append(httpConf.Post, httpServerPost{ - os.Getenv("GCQ_HTTP_POST_URL"), - os.Getenv("GCQ_HTTP_POST_SECRET"), - nilParseUint(os.Getenv("GCQ_HTTP_POST_MAXRETRIES"), 10, 64), - nilParseUint(os.Getenv("GCQ_HTTP_POST_RETRIESINTERVAL"), 10, 64), - }) - } - _ = node.Encode(httpConf) - return "http", node -} - func init() { - config.AddServer(&config.Server{Brief: "HTTP通信", Default: httpDefault, ParseEnv: readEnvConfig}) + config.AddServer(&config.Server{Brief: "HTTP通信", Default: httpDefault}) } var joinQuery = regexp.MustCompile(`\[(.+?),(.+?)]\.0`) diff --git a/server/websocket.go b/server/websocket.go index 964b926cd..20d3a73ba 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "os" "runtime/debug" "strconv" "strings" @@ -20,7 +19,6 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -123,52 +121,10 @@ func init() { config.AddServer(&config.Server{ Brief: "正向 Websocket 通信", Default: wsDefault, - ParseEnv: func() (string, *yaml.Node) { - if os.Getenv("GCQ_WS_PORT") != "" { - // type convert tools - toInt64 := func(str string) int64 { - i, _ := strconv.ParseInt(str, 10, 64) - return i - } - accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") - node := &yaml.Node{} - wsServerConf := &WebsocketServer{ - Host: "0.0.0.0", - Port: 6700, - MiddleWares: MiddleWares{ - AccessToken: accessTokenEnv, - }, - } - param.SetExcludeDefault(&wsServerConf.Disabled, param.EnsureBool(os.Getenv("GCQ_WS_DISABLE"), false), false) - param.SetExcludeDefault(&wsServerConf.Host, os.Getenv("GCQ_WS_HOST"), "") - param.SetExcludeDefault(&wsServerConf.Port, int(toInt64(os.Getenv("GCQ_WS_PORT"))), 0) - _ = node.Encode(wsServerConf) - return "ws", node - } - return "", nil - }, }) config.AddServer(&config.Server{ Brief: "反向 Websocket 通信", Default: wsReverseDefault, - ParseEnv: func() (string, *yaml.Node) { - if os.Getenv("GCQ_RWS_API") != "" || os.Getenv("GCQ_RWS_EVENT") != "" || os.Getenv("GCQ_RWS_UNIVERSAL") != "" { - accessTokenEnv := os.Getenv("GCQ_ACCESS_TOKEN") - node := &yaml.Node{} - rwsConf := &WebsocketReverse{ - MiddleWares: MiddleWares{ - AccessToken: accessTokenEnv, - }, - } - param.SetExcludeDefault(&rwsConf.Disabled, param.EnsureBool(os.Getenv("GCQ_RWS_DISABLE"), false), false) - param.SetExcludeDefault(&rwsConf.API, os.Getenv("GCQ_RWS_API"), "") - param.SetExcludeDefault(&rwsConf.Event, os.Getenv("GCQ_RWS_EVENT"), "") - param.SetExcludeDefault(&rwsConf.Universal, os.Getenv("GCQ_RWS_UNIVERSAL"), "") - _ = node.Encode(rwsConf) - return "ws-reverse", node - } - return "", nil - }, }) } From d33f17e727f60b92d312cd91559a92c6769f703f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Mon, 7 Feb 2022 19:49:24 +0800 Subject: [PATCH 279/612] fix: uploadForwardElement upload image error (#1354) --- coolq/api.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 6005c108b..2e9677dc6 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -796,16 +796,17 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F var wg sync.WaitGroup resolveElement := func(elems []message.IMessageElement) []message.IMessageElement { for i, elem := range elems { - switch elem.(type) { + iescape := i + switch o := elem.(type) { case *LocalImageElement, *LocalVideoElement: wg.Add(1) lazyUpload = append(lazyUpload, func() { defer wg.Done() - gm, err := bot.uploadMedia(elem, groupID, true) + gm, err := bot.uploadMedia(o, groupID, true) if err != nil { - log.Warnf("警告: 群 %d %s上传失败: %v", groupID, elem.Type().String(), err) + log.Warnf("警告: 群 %d %s上传失败: %v", groupID, o.Type().String(), err) } else { - elems[i] = gm + elems[iescape] = gm } }) } @@ -864,7 +865,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F } } } - content := bot.ConvertObjectMessage(e.Get("data.content"), MessageSourceGroup) + content := bot.ConvertObjectMessage(c, MessageSourceGroup) if uin != 0 && name != "" && len(content) > 0 { return &message.ForwardNode{ SenderId: uin, From b70db344a63323d63e327b34c63df27d116c5048 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 8 Feb 2022 21:52:54 +0800 Subject: [PATCH 280/612] .github: update golint.yml --- .github/workflows/golint.yml | 1 + coolq/cqcode.go | 2 +- db/leveldb/leveldb.go | 2 -- server/http.go | 12 ------------ 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index 624634e4d..5a9ef49c8 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -37,4 +37,5 @@ jobs: if: ${{ github.event.pull_request }} uses: reviewdog/action-suggester@v1 with: + github_token: ${{ secrets.GITHUB_TOKEN }} tool_name: golangci-lint diff --git a/coolq/cqcode.go b/coolq/cqcode.go index eb6c8fd50..80d591360 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -414,7 +414,7 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { case *message.RedBagElement: m = global.MSG{ "type": "redbag", - "data": global.MSG{"title": o.Title, "type": o.MsgType}, + "data": global.MSG{"title": o.Title, "type": int(o.MsgType)}, } case *message.ForwardElement: m = global.MSG{ diff --git a/db/leveldb/leveldb.go b/db/leveldb/leveldb.go index 02369320c..cfe7716e1 100644 --- a/db/leveldb/leveldb.go +++ b/db/leveldb/leveldb.go @@ -5,7 +5,6 @@ import ( "encoding/gob" "path" - "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/binary" @@ -37,7 +36,6 @@ func init() { gob.Register(db.StoredGroupMessage{}) gob.Register(db.StoredPrivateMessage{}) gob.Register(db.StoredGuildChannelMessage{}) - gob.Register(message.RedBagMessageType(0)) db.Register("leveldb", func(node yaml.Node) db.Database { conf := new(config.LevelDBConfig) diff --git a/server/http.go b/server/http.go index dcde4f95e..7d696c529 100644 --- a/server/http.go +++ b/server/http.go @@ -2,7 +2,6 @@ package server import ( "bytes" - "context" "crypto/hmac" "crypto/sha1" "encoding/hex" @@ -360,14 +359,3 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { c.bot.CQHandleQuickOperation(gjson.Parse(e.JSONString()), gjson.ParseBytes(r)) } } - -func (s *httpServer) ShutDown() { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - if err := s.HTTP.Shutdown(ctx); err != nil { - log.Fatal("http Server Shutdown:", err) - } - <-ctx.Done() - log.Println("timeout of 5 seconds.") - log.Println("http Server exiting") -} From a4992c3f79f8bb068a64a7691ce5871bc0f5ae53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=9F=E5=B0=8F=E7=99=BDCore?= <42798150+Core2002@users.noreply.github.com> Date: Tue, 8 Feb 2022 23:54:36 +0800 Subject: [PATCH 281/612] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=88=9B=E5=BB=BA=E7=9A=84=E6=89=B9=E5=A4=84=E7=90=86?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=97=A0=E6=B3=95=E6=89=A7=E8=A1=8C=E7=9A=84?= =?UTF-8?q?Bug=20(#1355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复了自动创建的批处理文件无法执行的Bug * 调整代码 --- global/terminal/double_click_windows.go | 6 +++++- go.sum | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/global/terminal/double_click_windows.go b/global/terminal/double_click_windows.go index 2ac3186ed..1fa746cf0 100644 --- a/global/terminal/double_click_windows.go +++ b/global/terminal/double_click_windows.go @@ -5,6 +5,7 @@ package terminal import ( "os" + "path/filepath" "syscall" "unsafe" @@ -44,7 +45,10 @@ func NoMoreDoubleClick() error { return errors.Errorf("打开go-cqhttp.bat失败: %v", err) } _ = f.Truncate(0) - _, err = f.WriteString("%Created by go-cqhttp. DO NOT EDIT ME!%\nstart cmd /K go-cqhttp.exe") + + ex, _ := os.Executable() + exPath := filepath.Base(ex) + _, err = f.WriteString("%Created by go-cqhttp. DO NOT EDIT ME!%\nstart cmd /K \"" + exPath + "\"") if err != nil { return errors.Errorf("写入go-cqhttp.bat失败: %v", err) } diff --git a/go.sum b/go.sum index 62a10c1dd..597fdb73b 100644 --- a/go.sum +++ b/go.sum @@ -29,6 +29,7 @@ github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98/go.mod h1:E5TBHc60dsWt github.com/gocq/rs v1.0.1 h1:ng7nhXmnx3SnfM0DOqmbP6GmQp1xGwRG9XmBiLFDWuM= github.com/gocq/rs v1.0.1/go.mod h1:8oaQnRvqn1fMh8i5zsetgQo03OUXksJV1k+dpmExxcY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -91,6 +92,7 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -120,6 +122,7 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -135,8 +138,10 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -153,6 +158,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 8eefcc8cc867c9f74c095bc1e49b6ebae9cfd529 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 9 Feb 2022 13:41:19 +0800 Subject: [PATCH 282/612] config: impl env placeholder with default value Fixes #1358 --- modules/config/config.go | 23 +++++++++++++++++------ modules/config/config_test.go | 5 +++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/modules/config/config.go b/modules/config/config.go index e9b049dca..77a0b9930 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -152,12 +152,23 @@ func generateConfig() { // os.ExpandEnv 字符 $ 无法逃逸 // https://github.com/golang/go/issues/43482 func expand(s string, mapping func(string) string) string { - r := regexp.MustCompile(`\${([a-zA-Z_]+[a-zA-Z0-9_]*)}`) - re := r.FindAllStringSubmatch(s, -1) - for _, i := range re { - if len(i) == 2 { - s = strings.ReplaceAll(s, i[0], mapping(i[1])) + r := regexp.MustCompile(`\${([a-zA-Z_]+[a-zA-Z0-9_:]*)}`) + return r.ReplaceAllStringFunc(s, func(s string) string { + s = strings.Trim(s, "${}") + // todo: use strings.Cut once go1.18 is released + placeholder, default_, ok := cut(s, ":") + m := mapping(placeholder) + if ok && m == "" { + return default_ } + return m + }) + +} + +func cut(s, sep string) (before, after string, found bool) { + if i := strings.Index(s, sep); i >= 0 { + return s[:i], s[i+len(sep):], true } - return s + return s, "", false } diff --git a/modules/config/config_test.go b/modules/config/config_test.go index 295a45083..8e6f5da70 100644 --- a/modules/config/config_test.go +++ b/modules/config/config_test.go @@ -21,6 +21,11 @@ func Test_expand(t *testing.T) { mapping: strings.ToUpper, expected: "$123", }, + { + src: "foo: ${bar:123456}", + mapping: func(s string) string { return "" }, + expected: "foo: 123456", + }, } for i, tt := range tests { if got := expand(tt.src, tt.mapping); got != tt.expected { From e1937e9f153a5ded2d4395c120ad6ffe21e4e9bd Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 9 Feb 2022 15:01:38 +0800 Subject: [PATCH 283/612] docs: document default env placeholder --- docs/config.md | 2 +- modules/config/config.go | 8 ++++---- modules/config/config_test.go | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/docs/config.md b/docs/config.md index 93a299bee..3bd136900 100644 --- a/docs/config.md +++ b/docs/config.md @@ -174,7 +174,7 @@ go-cqhttp 配置文件可以使用占位符来读取**环境变量**的值。 ```yaml account: # 账号相关 uin: ${CQ_UIN} # 读取环境变量 CQ_UIN - password: ${CQ_PASSWORD} # 读取环境变量 CQ_UIN + password: ${CQ_PWD:123456} # 当 CQ_PWD 为空时使用默认值 123456 ``` ## 在线状态 diff --git a/modules/config/config.go b/modules/config/config.go index 77a0b9930..c9ccc9deb 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -152,14 +152,14 @@ func generateConfig() { // os.ExpandEnv 字符 $ 无法逃逸 // https://github.com/golang/go/issues/43482 func expand(s string, mapping func(string) string) string { - r := regexp.MustCompile(`\${([a-zA-Z_]+[a-zA-Z0-9_:]*)}`) + r := regexp.MustCompile(`\${([a-zA-Z_]+[a-zA-Z0-9_:/.]*)}`) return r.ReplaceAllStringFunc(s, func(s string) string { s = strings.Trim(s, "${}") // todo: use strings.Cut once go1.18 is released - placeholder, default_, ok := cut(s, ":") - m := mapping(placeholder) + before, after, ok := cut(s, ":") + m := mapping(before) if ok && m == "" { - return default_ + return after } return m }) diff --git a/modules/config/config_test.go b/modules/config/config_test.go index 8e6f5da70..ae44b8e68 100644 --- a/modules/config/config_test.go +++ b/modules/config/config_test.go @@ -6,6 +6,9 @@ import ( ) func Test_expand(t *testing.T) { + nullStringMapping := func(_ string) string { + return "" + } tests := []struct { src string mapping func(string) string @@ -23,9 +26,19 @@ func Test_expand(t *testing.T) { }, { src: "foo: ${bar:123456}", - mapping: func(s string) string { return "" }, + mapping: nullStringMapping, expected: "foo: 123456", }, + { + src: "foo: ${bar:127.0.0.1:5700}", + mapping: nullStringMapping, + expected: "foo: 127.0.0.1:5700", + }, + { + src: "foo: ${bar:ws//localhost:9999/ws}", + mapping: nullStringMapping, + expected: "foo: ws//localhost:9999/ws", + }, } for i, tt := range tests { if got := expand(tt.src, tt.mapping); got != tt.expected { From 0145879f37a6bd00568b46550f2b16e9563f60a4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Feb 2022 07:02:21 +0000 Subject: [PATCH 284/612] ci(chore): Fix stylings --- modules/config/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/config/config.go b/modules/config/config.go index c9ccc9deb..84c4a3e2b 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -163,7 +163,6 @@ func expand(s string, mapping func(string) string) string { } return m }) - } func cut(s, sep string) (before, after string, found bool) { From 4b3ae1c77938036da2090e2965dbdb55ba241e7a Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 9 Feb 2022 17:31:39 +0800 Subject: [PATCH 285/612] fix #1351 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1bc39f8a3..852d52aff 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747 + github.com/Mrs4s/MiraiGo v0.0.0-20220209092529-5d071b034c17 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 diff --git a/go.sum b/go.sum index 597fdb73b..ba4622513 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747 h1:i9IcyQOVc3eYHXAXgTAMsEdAcGkxpbjBEvVb87z50ZA= -github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747/go.mod h1:rtKLkhMEi2YjsrXaNztT4uagUOPBxf6a+TNREkG097I= +github.com/Mrs4s/MiraiGo v0.0.0-20220209092529-5d071b034c17 h1:OQVnlWt5if0TOFoNGDjILazBjVTncSlPxGXabD+4MvE= +github.com/Mrs4s/MiraiGo v0.0.0-20220209092529-5d071b034c17/go.mod h1:rtKLkhMEi2YjsrXaNztT4uagUOPBxf6a+TNREkG097I= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpSNVp5mnuloRMd3Rj2Lfg5biZ3emv//c= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From 1a78a4809a412ddda63970bdc13789b2e2313892 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 9 Feb 2022 22:33:53 +0800 Subject: [PATCH 286/612] server/http: delete tab in default config Fixes #1362 --- docs/config.md | 7 ------- server/http.go | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/docs/config.md b/docs/config.md index 3bd136900..d352b61eb 100644 --- a/docs/config.md +++ b/docs/config.md @@ -79,13 +79,6 @@ default-middlewares: &default frequency: 1 # 令牌回复频率, 单位秒 bucket: 1 # 令牌桶大小 -database: # 数据库相关设置 - leveldb: - # 是否启用内置leveldb数据库 - # 启用将会增加10-20MB的内存占用和一定的磁盘空间 - # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 - enable: true - # 连接服务列表 servers: # HTTP 通信设置 diff --git a/server/http.go b/server/http.go index 7d696c529..0a73db8f3 100644 --- a/server/http.go +++ b/server/http.go @@ -87,11 +87,11 @@ const httpDefault = ` post: # 反向HTTP POST地址列表 #- url: '' # 地址 # secret: '' # 密钥 - # max-retries: 3 # 最大重试,0 时禁用 + # max-retries: 3 # 最大重试,0 时禁用 # retries-interval: 1500 # 重试时间,单位毫秒,0 时立即 #- url: http://127.0.0.1:5701/ # 地址 # secret: '' # 密钥 - # max-retries: 10 # 最大重试,0 时禁用 + # max-retries: 10 # 最大重试,0 时禁用 # retries-interval: 1000 # 重试时间,单位毫秒,0 时立即 ` From 115a5f1da75db26e02d5efe346c35f34ddde3aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=83=E6=A9=98=20=E9=9B=AB=E9=9C=9E?= Date: Wed, 9 Feb 2022 23:55:39 +0800 Subject: [PATCH 287/612] fix: examine errors that have been ignored in the 'CQ:at' parser (#1363) #1332 --- coolq/cqcode.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 80d591360..7f95b1fc5 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -954,7 +954,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou if qq == "all" { return message.AtAll(), nil } - t, _ := strconv.ParseInt(qq, 10, 64) + t, err := strconv.ParseInt(qq, 10, 64) + if err != nil { + return nil, err + } name := strings.TrimSpace(d["name"]) if len(name) > 0 { name = "@" + name From cf9fa71646747754cd445b94ba45b58bb33cadd3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 11 Feb 2022 14:17:54 +0800 Subject: [PATCH 288/612] coolq: fix private reply id Fixes #1368 --- coolq/cqcode.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 7f95b1fc5..c30de3730 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -114,7 +114,7 @@ func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []glob if reply != nil && source.SourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) rid := int64(source.PrimaryID) - if rid == 0 { + if source.SourceType == MessageSourcePrivate { rid = replyElem.Sender } if replyElem.GroupID != 0 { @@ -280,7 +280,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. if reply != nil && source.SourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) rid := int64(source.PrimaryID) - if rid == 0 { + if source.SourceType == MessageSourcePrivate { rid = replyElem.Sender } if replyElem.GroupID != 0 { From baecc2f8e6b3f4889185da1d6bcb0b251b89f4b5 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 12 Feb 2022 16:18:54 +0800 Subject: [PATCH 289/612] dep: update MiraiGo For #1368 --- db/leveldb/leveldb.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/db/leveldb/leveldb.go b/db/leveldb/leveldb.go index cfe7716e1..2d86dbb55 100644 --- a/db/leveldb/leveldb.go +++ b/db/leveldb/leveldb.go @@ -50,7 +50,7 @@ func init() { func (ldb *LevelDBImpl) Open() error { p := path.Join("data", "leveldb-v2") d, err := leveldb.OpenFile(p, &opt.Options{ - WriteBuffer: 128 * opt.KiB, + WriteBuffer: 32 * opt.KiB, }) if err != nil { return errors.Wrap(err, "open leveldb error") diff --git a/go.mod b/go.mod index 852d52aff..0d4896d60 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220209092529-5d071b034c17 + github.com/Mrs4s/MiraiGo v0.0.0-20220212081637-6692c4acb8eb github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 diff --git a/go.sum b/go.sum index ba4622513..9c788da0e 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220209092529-5d071b034c17 h1:OQVnlWt5if0TOFoNGDjILazBjVTncSlPxGXabD+4MvE= -github.com/Mrs4s/MiraiGo v0.0.0-20220209092529-5d071b034c17/go.mod h1:rtKLkhMEi2YjsrXaNztT4uagUOPBxf6a+TNREkG097I= +github.com/Mrs4s/MiraiGo v0.0.0-20220212081637-6692c4acb8eb h1:nc96MkGgqa2iW8bMzFMj2mdT9LUJYgsdez30zwo8qxg= +github.com/Mrs4s/MiraiGo v0.0.0-20220212081637-6692c4acb8eb/go.mod h1:rtKLkhMEi2YjsrXaNztT4uagUOPBxf6a+TNREkG097I= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpSNVp5mnuloRMd3Rj2Lfg5biZ3emv//c= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From ba808fff1d35258ce49f3bb4b2b0ef0a522d7ba4 Mon Sep 17 00:00:00 2001 From: Lin <767763591@qq.com> Date: Sat, 12 Feb 2022 16:48:06 +0800 Subject: [PATCH 290/612] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=9A=E9=80=82?= =?UTF-8?q?=E9=85=8D=E8=87=AA=E5=AE=9A=E4=B9=89QR=E5=B0=BA=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: wdvxdr --- cmd/gocq/login.go | 39 ++++++++++++++++++++++++++++++++------- go.mod | 5 ----- go.sum | 12 ------------ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 80ac10bcf..bc191461a 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -3,13 +3,14 @@ package gocq import ( "bufio" "bytes" + "image" + "image/png" "os" "strings" "time" - qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go" "github.com/Mrs4s/MiraiGo/client" - "github.com/gocq/qrcode" + "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -53,12 +54,36 @@ func commonLogin() error { return loginResponseProcessor(res) } -func qrcodeLogin() error { - rsp, err := cli.FetchQRCode() +func printQRCode(imgData []byte) { + const ( + black = "\033[48;5;0m \033[0m" + white = "\033[48;5;7m \033[0m" + ) + img, err := png.Decode(bytes.NewReader(imgData)) if err != nil { - return err + log.Panic(err) + } + data := img.(*image.Gray).Pix + bound := img.Bounds().Max.X + buf := make([]byte, 0, (bound*4+1)*(bound)) + i := 0 + for y := 0; y < bound; y++ { + i = y * bound + for x := 0; x < bound; x++ { + if data[i] != 255 { + buf = append(buf, white...) + } else { + buf = append(buf, black...) + } + i++ + } + buf = append(buf, '\n') } - fi, err := qrcode.Decode(bytes.NewReader(rsp.ImageData)) + _, _ = colorable.NewColorableStdout().Write(buf) +} + +func qrcodeLogin() error { + rsp, err := cli.FetchQRCodeCustomSize(1, 2, 1) if err != nil { return err } @@ -70,7 +95,7 @@ func qrcodeLogin() error { log.Infof("请使用手机QQ扫描二维码 (qrcode.png) : ") } time.Sleep(time.Second) - qrcodeTerminal.New2(qrcodeTerminal.ConsoleColors.BrightBlack, qrcodeTerminal.ConsoleColors.BrightWhite, qrcodeTerminal.QRCodeRecoveryLevels.Low).Get(fi.Content).Print() + printQRCode(rsp.ImageData) s, err := cli.QueryQRCodeStatus(rsp.Sig) if err != nil { return err diff --git a/go.mod b/go.mod index 0d4896d60..43309d3b3 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,12 @@ module github.com/Mrs4s/go-cqhttp go 1.17 require ( - github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 github.com/Mrs4s/MiraiGo v0.0.0-20220212081637-6692c4acb8eb github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 - github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/klauspost/compress v1.13.6 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible @@ -34,17 +32,14 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect - github.com/gocq/rs v1.0.1 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/google/go-cmp v0.5.5 // indirect - github.com/google/uuid v1.1.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/pierrec/lz4/v4 v4.1.11 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect - github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect diff --git a/go.sum b/go.sum index 9c788da0e..f8678e5f0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= -github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Mrs4s/MiraiGo v0.0.0-20220212081637-6692c4acb8eb h1:nc96MkGgqa2iW8bMzFMj2mdT9LUJYgsdez30zwo8qxg= @@ -8,8 +6,6 @@ github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpS github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= -github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= -github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -24,10 +20,6 @@ github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0Fn github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98 h1:NJDZEa7gibUa0w4tie8qKeQFKdeKFUbecWyQDPdRx40= -github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98/go.mod h1:E5TBHc60dsWtOL7sbXCb3P9i4xrj2J7Zm5sEJftIc1w= -github.com/gocq/rs v1.0.1 h1:ng7nhXmnx3SnfM0DOqmbP6GmQp1xGwRG9XmBiLFDWuM= -github.com/gocq/rs v1.0.1/go.mod h1:8oaQnRvqn1fMh8i5zsetgQo03OUXksJV1k+dpmExxcY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -36,8 +28,6 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= -github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= @@ -82,8 +72,6 @@ github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= -github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= From 325bd427341db837f7782d2b9a3602e97addf644 Mon Sep 17 00:00:00 2001 From: ishkong Date: Sun, 13 Feb 2022 16:07:49 +0800 Subject: [PATCH 291/612] =?UTF-8?q?=F0=9F=93=9D=20Replace=20broken=20links?= =?UTF-8?q?=20in=20documentation=20(#1365)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/cqhttp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cqhttp.md b/docs/cqhttp.md index e861219c1..71d7d201a 100644 --- a/docs/cqhttp.md +++ b/docs/cqhttp.md @@ -244,7 +244,7 @@ Type: `node` | `seq` | message | 具体消息 | 用于自定义消息 | 特殊说明: **需要使用单独的API `/send_group_forward_msg` 发送,并且由于消息段较为复杂,仅支持Array形式入参。 如果引用消息和自定义消息同时出现,实际查看顺序将取消息段顺序. -另外按 [CQHTTP](https://git.io/JtxtN) 文档说明, `data` 应全为字符串, 但由于需要接收`message` 类型的消息, 所以 *仅限此Type的content字段* 支持Array套娃** +另外按 [Onebot v11](https://github.com/botuniverse/onebot-11/blob/master/message/array.md) 文档说明, `data` 应全为字符串, 但由于需要接收`message` 类型的消息, 所以 *仅限此Type的content字段* 支持Array套娃** 示例: From c609fd72f5b1e46c2a35dcd54330f6715b2cb839 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 13 Feb 2022 17:28:08 +0800 Subject: [PATCH 292/612] dep: update deps & drop github.com/klauspost/compress --- coolq/cqcode.go | 11 +---- go.mod | 12 ++--- go.sum | 71 ++++++++++++++++++++------- internal/selfupdate/update_others.go | 3 +- internal/selfupdate/update_windows.go | 3 +- 5 files changed, 61 insertions(+), 39 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index c30de3730..46f725283 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/md5" "encoding/hex" - xml2 "encoding/xml" "errors" "fmt" "io" @@ -1044,7 +1043,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou }, nil } xml := fmt.Sprintf(``, - XMLEscape(d["title"]), d["url"], d["image"], d["audio"], XMLEscape(d["title"]), XMLEscape(d["content"])) + utils.XmlEscape(d["title"]), d["url"], d["image"], d["audio"], utils.XmlEscape(d["title"]), utils.XmlEscape(d["content"])) return &message.ServiceElement{ Id: 60, Content: xml, @@ -1147,14 +1146,6 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou } } -// XMLEscape 将字符串c转义为XML字符串 -func XMLEscape(c string) string { - buf := global.NewBuffer() - defer global.PutBuffer(buf) - _ = xml2.EscapeText(buf, utils.S2B(c)) - return buf.String() -} - /*CQCodeEscapeText 将字符串raw中部分字符转义 & -> & diff --git a/go.mod b/go.mod index 43309d3b3..80bf553ff 100644 --- a/go.mod +++ b/go.mod @@ -4,23 +4,22 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220212081637-6692c4acb8eb + github.com/Mrs4s/MiraiGo v0.0.0-20220213090811-8a518ee96c6b github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 - github.com/klauspost/compress v1.13.6 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/mattn/go-colorable v0.1.12 github.com/pkg/errors v0.9.1 github.com/segmentio/asm v1.1.3 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 - github.com/syndtr/goleveldb v1.0.0 - github.com/tidwall/gjson v1.12.1 + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + github.com/tidwall/gjson v1.14.0 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 - go.mongodb.org/mongo-driver v1.8.1 + go.mongodb.org/mongo-driver v1.8.3 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 @@ -32,9 +31,10 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect - github.com/golang/snappy v0.0.1 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.5 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/klauspost/compress v1.13.6 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/pierrec/lz4/v4 v4.1.11 // indirect diff --git a/go.sum b/go.sum index f8678e5f0..71f6147b3 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220212081637-6692c4acb8eb h1:nc96MkGgqa2iW8bMzFMj2mdT9LUJYgsdez30zwo8qxg= -github.com/Mrs4s/MiraiGo v0.0.0-20220212081637-6692c4acb8eb/go.mod h1:rtKLkhMEi2YjsrXaNztT4uagUOPBxf6a+TNREkG097I= +github.com/Mrs4s/MiraiGo v0.0.0-20220213090811-8a518ee96c6b h1:RC+/HdXawl3FWYS7uqFdORHFTO3BNOQT0XQE6zJEdK8= +github.com/Mrs4s/MiraiGo v0.0.0-20220213090811-8a518ee96c6b/go.mod h1:BNKTKQjbsmv2Coh1u//NfsxsuOq2ByVN2ZbSSMwjQBc= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpSNVp5mnuloRMd3Rj2Lfg5biZ3emv//c= github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= @@ -12,6 +12,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= @@ -21,14 +23,22 @@ github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmx github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -53,11 +63,15 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pierrec/lz4/v4 v4.1.11 h1:LVs17FAZJFOjgmJXl9Tf13WfLUvZq7/RjfEJrnwZ9OE= github.com/pierrec/lz4/v4 v4.1.11/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -78,11 +92,11 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo= -github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= +github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -98,17 +112,20 @@ github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyh github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -go.mongodb.org/mongo-driver v1.8.1 h1:OZE4Wni/SJlrcmSIBRYNzunX5TKxjrTS4jKSnA99oKU= -go.mongodb.org/mongo-driver v1.8.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4= +go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= @@ -120,8 +137,15 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cO golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -137,6 +161,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -144,19 +170,26 @@ golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13W golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/selfupdate/update_others.go b/internal/selfupdate/update_others.go index b18bb6bbe..1bd535dd7 100644 --- a/internal/selfupdate/update_others.go +++ b/internal/selfupdate/update_others.go @@ -6,12 +6,11 @@ package selfupdate import ( "archive/tar" "bytes" + "compress/gzip" "crypto/sha256" "errors" "io" "net/http" - - "github.com/klauspost/compress/gzip" ) // update go-cqhttp自我更新 diff --git a/internal/selfupdate/update_windows.go b/internal/selfupdate/update_windows.go index 235b48f96..cb32e1a58 100644 --- a/internal/selfupdate/update_windows.go +++ b/internal/selfupdate/update_windows.go @@ -1,13 +1,12 @@ package selfupdate import ( + "archive/zip" "bytes" "crypto/sha256" "errors" "io" "net/http" - - "github.com/klauspost/compress/zip" ) // update go-cqhttp自我更新 From da9bad44e2e6a74c07c7dd887d1e111260832924 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 14 Feb 2022 00:54:13 +0800 Subject: [PATCH 293/612] dep: update MiraiGo reduce binary size about 550KiB --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 80bf553ff..20fcd9a4b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220213090811-8a518ee96c6b + github.com/Mrs4s/MiraiGo v0.0.0-20220213165136-d53bf8503e18 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 @@ -27,7 +27,7 @@ require ( ) require ( - github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 // indirect + github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect diff --git a/go.sum b/go.sum index 71f6147b3..9efb487e0 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220213090811-8a518ee96c6b h1:RC+/HdXawl3FWYS7uqFdORHFTO3BNOQT0XQE6zJEdK8= -github.com/Mrs4s/MiraiGo v0.0.0-20220213090811-8a518ee96c6b/go.mod h1:BNKTKQjbsmv2Coh1u//NfsxsuOq2ByVN2ZbSSMwjQBc= -github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 h1:hnaAkKz4t+xpSNVp5mnuloRMd3Rj2Lfg5biZ3emv//c= -github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/Mrs4s/MiraiGo v0.0.0-20220213165136-d53bf8503e18 h1:eM2KdNvC9DffdhBUSMjSCzdedQp2PmnNkiI+TAM1iDQ= +github.com/Mrs4s/MiraiGo v0.0.0-20220213165136-d53bf8503e18/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= +github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 h1:8CK7Hg+CRGTFhpjvp5V+7wd8/TkuZ6fSuztLVV3bwoQ= +github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From f2e26d0e134c5ebb431f7d946f1536c2e60b5cd0 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 14 Feb 2022 16:30:47 +0800 Subject: [PATCH 294/612] dep: update MiraiGo Fixes #1095 --- go.mod | 2 +- go.sum | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 20fcd9a4b..5a94b4f5b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220213165136-d53bf8503e18 + github.com/Mrs4s/MiraiGo v0.0.0-20220214082717-0e68a1e7b715 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 diff --git a/go.sum b/go.sum index 9efb487e0..7d2a47437 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220213165136-d53bf8503e18 h1:eM2KdNvC9DffdhBUSMjSCzdedQp2PmnNkiI+TAM1iDQ= -github.com/Mrs4s/MiraiGo v0.0.0-20220213165136-d53bf8503e18/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= +github.com/Mrs4s/MiraiGo v0.0.0-20220214082717-0e68a1e7b715 h1:6tSKbzugDl1V+htbVtTpiuVTpINGGXIRD8PBB64Sjoc= +github.com/Mrs4s/MiraiGo v0.0.0-20220214082717-0e68a1e7b715/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 h1:8CK7Hg+CRGTFhpjvp5V+7wd8/TkuZ6fSuztLVV3bwoQ= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= @@ -29,7 +29,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -94,7 +93,6 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -127,7 +125,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -150,10 +147,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -179,8 +174,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 9054d4cee89b4102fd3e0ad620d68169e3343b70 Mon Sep 17 00:00:00 2001 From: wdvxdr1123 <34832863+wdvxdr1123@users.noreply.github.com> Date: Tue, 15 Feb 2022 22:24:27 +0800 Subject: [PATCH 295/612] db/leveldb: impl index read/write drop `encoding/gob` (#1370) Two benefit below: * shrink go-cqhttp binary size about 200KiB * shrink database file from 2.8M to 1.56M compared with v2 database Also provide a tool to migrate v2 database: https://github.com/RomiChan/gocq-leveldb-migrate --- db/leveldb/const.go | 25 ++++++ db/leveldb/database_gen.go | 177 +++++++++++++++++++++++++++++++++++++ db/leveldb/leveldb.go | 131 +++++++++++---------------- db/leveldb/mkrw.go | 129 +++++++++++++++++++++++++++ db/leveldb/reader.go | 154 ++++++++++++++++++++++++++++++++ db/leveldb/writer.go | 149 +++++++++++++++++++++++++++++++ modules/config/config.go | 5 -- 7 files changed, 687 insertions(+), 83 deletions(-) create mode 100644 db/leveldb/const.go create mode 100644 db/leveldb/database_gen.go create mode 100644 db/leveldb/mkrw.go create mode 100644 db/leveldb/reader.go create mode 100644 db/leveldb/writer.go diff --git a/db/leveldb/const.go b/db/leveldb/const.go new file mode 100644 index 000000000..1e6303134 --- /dev/null +++ b/db/leveldb/const.go @@ -0,0 +1,25 @@ +package leveldb + +const dataVersion = 1 + +const ( + group = 0x0 + private = 0x1 + guildChannel = 0x2 +) + +type coder byte + +const ( + coderNil coder = iota + coderInt + coderUint + coderInt32 + coderUint32 + coderInt64 + coderUint64 + coderString + coderMSG // global.MSG + coderArrayMSG // []global.MSG + coderStruct // struct{} +) diff --git a/db/leveldb/database_gen.go b/db/leveldb/database_gen.go new file mode 100644 index 000000000..929aeac1d --- /dev/null +++ b/db/leveldb/database_gen.go @@ -0,0 +1,177 @@ +// Code generated by mkrw.go; DO NOT EDIT. + +package leveldb + +import "github.com/Mrs4s/go-cqhttp/db" + +func (w *writer) writeStoredGroupMessage(x *db.StoredGroupMessage) { + if x == nil { + w.nil() + return + } + w.coder(coderStruct) + w.string(x.ID) + w.int32(x.GlobalID) + w.writeStoredMessageAttribute(x.Attribute) + w.string(x.SubType) + w.writeQuotedInfo(x.QuotedInfo) + w.int64(x.GroupCode) + w.string(x.AnonymousID) + w.arrayMsg(x.Content) +} + +func (r *reader) readStoredGroupMessage() *db.StoredGroupMessage { + coder := r.coder() + if coder == coderNil { + return nil + } + x := &db.StoredGroupMessage{} + x.ID = r.string() + x.GlobalID = r.int32() + x.Attribute = r.readStoredMessageAttribute() + x.SubType = r.string() + x.QuotedInfo = r.readQuotedInfo() + x.GroupCode = r.int64() + x.AnonymousID = r.string() + x.Content = r.arrayMsg() + return x +} + +func (w *writer) writeStoredPrivateMessage(x *db.StoredPrivateMessage) { + if x == nil { + w.nil() + return + } + w.coder(coderStruct) + w.string(x.ID) + w.int32(x.GlobalID) + w.writeStoredMessageAttribute(x.Attribute) + w.string(x.SubType) + w.writeQuotedInfo(x.QuotedInfo) + w.int64(x.SessionUin) + w.int64(x.TargetUin) + w.arrayMsg(x.Content) +} + +func (r *reader) readStoredPrivateMessage() *db.StoredPrivateMessage { + coder := r.coder() + if coder == coderNil { + return nil + } + x := &db.StoredPrivateMessage{} + x.ID = r.string() + x.GlobalID = r.int32() + x.Attribute = r.readStoredMessageAttribute() + x.SubType = r.string() + x.QuotedInfo = r.readQuotedInfo() + x.SessionUin = r.int64() + x.TargetUin = r.int64() + x.Content = r.arrayMsg() + return x +} + +func (w *writer) writeStoredGuildChannelMessage(x *db.StoredGuildChannelMessage) { + if x == nil { + w.nil() + return + } + w.coder(coderStruct) + w.string(x.ID) + w.writeStoredGuildMessageAttribute(x.Attribute) + w.uint64(x.GuildID) + w.uint64(x.ChannelID) + w.writeQuotedInfo(x.QuotedInfo) + w.arrayMsg(x.Content) +} + +func (r *reader) readStoredGuildChannelMessage() *db.StoredGuildChannelMessage { + coder := r.coder() + if coder == coderNil { + return nil + } + x := &db.StoredGuildChannelMessage{} + x.ID = r.string() + x.Attribute = r.readStoredGuildMessageAttribute() + x.GuildID = r.uint64() + x.ChannelID = r.uint64() + x.QuotedInfo = r.readQuotedInfo() + x.Content = r.arrayMsg() + return x +} + +func (w *writer) writeStoredMessageAttribute(x *db.StoredMessageAttribute) { + if x == nil { + w.nil() + return + } + w.coder(coderStruct) + w.int32(x.MessageSeq) + w.int32(x.InternalID) + w.int64(x.SenderUin) + w.string(x.SenderName) + w.int64(x.Timestamp) +} + +func (r *reader) readStoredMessageAttribute() *db.StoredMessageAttribute { + coder := r.coder() + if coder == coderNil { + return nil + } + x := &db.StoredMessageAttribute{} + x.MessageSeq = r.int32() + x.InternalID = r.int32() + x.SenderUin = r.int64() + x.SenderName = r.string() + x.Timestamp = r.int64() + return x +} + +func (w *writer) writeStoredGuildMessageAttribute(x *db.StoredGuildMessageAttribute) { + if x == nil { + w.nil() + return + } + w.coder(coderStruct) + w.uint64(x.MessageSeq) + w.uint64(x.InternalID) + w.uint64(x.SenderTinyID) + w.string(x.SenderName) + w.int64(x.Timestamp) +} + +func (r *reader) readStoredGuildMessageAttribute() *db.StoredGuildMessageAttribute { + coder := r.coder() + if coder == coderNil { + return nil + } + x := &db.StoredGuildMessageAttribute{} + x.MessageSeq = r.uint64() + x.InternalID = r.uint64() + x.SenderTinyID = r.uint64() + x.SenderName = r.string() + x.Timestamp = r.int64() + return x +} + +func (w *writer) writeQuotedInfo(x *db.QuotedInfo) { + if x == nil { + w.nil() + return + } + w.coder(coderStruct) + w.string(x.PrevID) + w.int32(x.PrevGlobalID) + w.arrayMsg(x.QuotedContent) +} + +func (r *reader) readQuotedInfo() *db.QuotedInfo { + coder := r.coder() + if coder == coderNil { + return nil + } + x := &db.QuotedInfo{} + x.PrevID = r.string() + x.PrevGlobalID = r.int32() + x.QuotedContent = r.arrayMsg() + return x +} diff --git a/db/leveldb/leveldb.go b/db/leveldb/leveldb.go index 2d86dbb55..7853e1b13 100644 --- a/db/leveldb/leveldb.go +++ b/db/leveldb/leveldb.go @@ -1,54 +1,40 @@ package leveldb import ( - "bytes" - "encoding/gob" "path" - "github.com/Mrs4s/MiraiGo/utils" - "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/MiraiGo/utils" "github.com/pkg/errors" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/opt" "gopkg.in/yaml.v3" "github.com/Mrs4s/go-cqhttp/db" - "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/modules/config" ) -type LevelDBImpl struct { +type database struct { db *leveldb.DB } -const ( - group byte = 0x0 - private byte = 0x1 - guildChannel byte = 0x2 -) +// config leveldb 相关配置 +type config struct { + Enable bool `yaml:"enable"` +} func init() { - gob.Register(db.StoredMessageAttribute{}) - gob.Register(db.StoredGuildMessageAttribute{}) - gob.Register(db.QuotedInfo{}) - gob.Register(global.MSG{}) - gob.Register(db.StoredGroupMessage{}) - gob.Register(db.StoredPrivateMessage{}) - gob.Register(db.StoredGuildChannelMessage{}) - db.Register("leveldb", func(node yaml.Node) db.Database { - conf := new(config.LevelDBConfig) + conf := new(config) _ = node.Decode(conf) if !conf.Enable { return nil } - return &LevelDBImpl{} + return &database{} }) } -func (ldb *LevelDBImpl) Open() error { - p := path.Join("data", "leveldb-v2") +func (ldb *database) Open() error { + p := path.Join("data", "leveldb-v3") d, err := leveldb.OpenFile(p, &opt.Options{ WriteBuffer: 32 * opt.KiB, }) @@ -59,31 +45,31 @@ func (ldb *LevelDBImpl) Open() error { return nil } -func (ldb *LevelDBImpl) GetMessageByGlobalID(id int32) (db.StoredMessage, error) { +func (ldb *database) GetMessageByGlobalID(id int32) (_ db.StoredMessage, err error) { v, err := ldb.db.Get(binary.ToBytes(id), nil) - if err != nil { + if err != nil || len(v) == 0 { return nil, errors.Wrap(err, "get value error") } - r := binary.NewReader(v) - switch r.ReadByte() { - case group: - g := &db.StoredGroupMessage{} - if err = gob.NewDecoder(bytes.NewReader(r.ReadAvailable())).Decode(g); err != nil { - return nil, errors.Wrap(err, "decode message error") + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("%v", r) } - return g, nil + }() + r, err := newReader(utils.B2S(v)) + if err != nil { + return nil, err + } + switch r.uvarint() { + case group: + return r.readStoredGroupMessage(), nil case private: - p := &db.StoredPrivateMessage{} - if err = gob.NewDecoder(bytes.NewReader(r.ReadAvailable())).Decode(p); err != nil { - return nil, errors.Wrap(err, "decode message error") - } - return p, nil + return r.readStoredPrivateMessage(), nil default: return nil, errors.New("unknown message flag") } } -func (ldb *LevelDBImpl) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, error) { +func (ldb *database) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, error) { i, err := ldb.GetMessageByGlobalID(id) if err != nil { return nil, err @@ -95,7 +81,7 @@ func (ldb *LevelDBImpl) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMess return g, nil } -func (ldb *LevelDBImpl) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessage, error) { +func (ldb *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessage, error) { i, err := ldb.GetMessageByGlobalID(id) if err != nil { return nil, err @@ -107,59 +93,48 @@ func (ldb *LevelDBImpl) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivate return p, nil } -func (ldb *LevelDBImpl) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { +func (ldb *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { v, err := ldb.db.Get([]byte(id), nil) if err != nil { return nil, errors.Wrap(err, "get value error") } - r := binary.NewReader(v) - switch r.ReadByte() { - case guildChannel: - g := &db.StoredGuildChannelMessage{} - if err = gob.NewDecoder(bytes.NewReader(r.ReadAvailable())).Decode(g); err != nil { - return nil, errors.Wrap(err, "decode message error") + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("%v", r) } - return g, nil + }() + r, err := newReader(utils.B2S(v)) + if err != nil { + return nil, err + } + switch r.uvarint() { + case guildChannel: + return r.readStoredGuildChannelMessage(), nil default: return nil, errors.New("unknown message flag") } } -func (ldb *LevelDBImpl) InsertGroupMessage(msg *db.StoredGroupMessage) error { - buf := global.NewBuffer() - defer global.PutBuffer(buf) - if err := gob.NewEncoder(buf).Encode(msg); err != nil { - return errors.Wrap(err, "encode message error") - } - err := ldb.db.Put(binary.ToBytes(msg.GlobalID), binary.NewWriterF(func(w *binary.Writer) { - w.WriteByte(group) - w.Write(buf.Bytes()) - }), nil) +func (ldb *database) InsertGroupMessage(msg *db.StoredGroupMessage) error { + w := newWriter() + w.uvarint(group) + w.writeStoredGroupMessage(msg) + err := ldb.db.Put(binary.ToBytes(msg.GlobalID), w.bytes(), nil) return errors.Wrap(err, "put data error") } -func (ldb *LevelDBImpl) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { - buf := global.NewBuffer() - defer global.PutBuffer(buf) - if err := gob.NewEncoder(buf).Encode(msg); err != nil { - return errors.Wrap(err, "encode message error") - } - err := ldb.db.Put(binary.ToBytes(msg.GlobalID), binary.NewWriterF(func(w *binary.Writer) { - w.WriteByte(private) - w.Write(buf.Bytes()) - }), nil) +func (ldb *database) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { + w := newWriter() + w.uvarint(private) + w.writeStoredPrivateMessage(msg) + err := ldb.db.Put(binary.ToBytes(msg.GlobalID), w.bytes(), nil) return errors.Wrap(err, "put data error") } -func (ldb *LevelDBImpl) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) error { - buf := global.NewBuffer() - defer global.PutBuffer(buf) - if err := gob.NewEncoder(buf).Encode(msg); err != nil { - return errors.Wrap(err, "encode message error") - } - err := ldb.db.Put(utils.S2B(msg.ID), binary.NewWriterF(func(w *binary.Writer) { - w.WriteByte(guildChannel) - w.Write(buf.Bytes()) - }), nil) +func (ldb *database) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) error { + w := newWriter() + w.uvarint(guildChannel) + w.writeStoredGuildChannelMessage(msg) + err := ldb.db.Put(utils.S2B(msg.ID), w.bytes(), nil) return errors.Wrap(err, "put data error") } diff --git a/db/leveldb/mkrw.go b/db/leveldb/mkrw.go new file mode 100644 index 000000000..d7a7489cd --- /dev/null +++ b/db/leveldb/mkrw.go @@ -0,0 +1,129 @@ +//go:build ignore + +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "os" +) + +var output bytes.Buffer + +func fprintf(format string, args ...interface{}) { + _, _ = fmt.Fprintf(&output, format, args...) +} + +func main() { + f, _ := parser.ParseFile(token.NewFileSet(), "./../database.go", nil, 0) + fprintf("// Code generated by mkrw.go; DO NOT EDIT.\n\n") + fprintf("package leveldb\n\n") + fprintf("import \"github.com/Mrs4s/go-cqhttp/db\"\n\n") + ast.Inspect(f, func(node ast.Node) bool { + switch node := node.(type) { + case *ast.FuncDecl: + return false + case *ast.TypeSpec: + if !node.Name.IsExported() { + return false + } + x, ok := node.Type.(*ast.StructType) + if !ok { + return false + } + if x.Fields != nil && x.Fields.List != nil { + mkWrite(node) + mkRead(node) + } + } + return true + }) + out, err := format.Source(output.Bytes()) + if err != nil { + fmt.Println(string(output.Bytes())) + panic(err) + } + os.WriteFile("database_gen.go", out, 0o644) +} + +func typeName(typ ast.Expr) string { + switch typ := typ.(type) { + case *ast.Ident: + return typ.Name + case *ast.ArrayType: + if typ.Len != nil { + panic("unexpected array type") + } + return "[]" + typeName(typ.Elt) + case *ast.SelectorExpr: + return typeName(typ.X) + "." + typ.Sel.Name + } + panic("unexpected type") +} + +func mkWrite(node *ast.TypeSpec) { + typename := node.Name.String() + structType := node.Type.(*ast.StructType) + fprintf("func (w *writer) write%s(x *db.%s) {\n", typename, typename) + fprintf("if x == nil {\n") + fprintf("w.nil()\n") + fprintf("return\n") + fprintf("}\n") + fprintf("w.coder(coderStruct)\n") + for _, field := range structType.Fields.List { + switch typ := field.Type.(type) { + case *ast.Ident: + for _, name := range field.Names { + fprintf("w.%s(x.%s)\n", typ.Name, name.Name) + } + case *ast.ArrayType: + if typeName(typ) != "[]global.MSG" { + panic("unexpected array type") + } + for _, name := range field.Names { + fprintf("w.arrayMsg(x.%s)\n", name.Name) + } + case *ast.StarExpr: + for _, name := range field.Names { + fprintf("w.write%s(x.%s)\n", typeName(typ.X), name.Name) + } + } + } + fprintf("}\n\n") +} + +func mkRead(node *ast.TypeSpec) { + typename := node.Name.String() + structType := node.Type.(*ast.StructType) + fprintf(`func (r *reader) read%s() *db.%s { + coder := r.coder() + if coder == coderNil { + return nil + }`+"\n", typename, typename) + fprintf("x := &db.%s{}\n", typename) + for _, field := range structType.Fields.List { + switch typ := field.Type.(type) { + case *ast.Ident: + for _, name := range field.Names { + fprintf("x.%s = r.%s()\n", name.Name, typ.Name) + } + case *ast.ArrayType: + if typeName(typ) != "[]global.MSG" { + panic("unexpected array type") + } + for _, name := range field.Names { + fprintf("x.%s = r.arrayMsg()\n", name.Name) + } + case *ast.StarExpr: + for _, name := range field.Names { + fprintf("x.%s = r.read%s()\n", name.Name, typeName(typ.X)) + } + } + } + fprintf("return x\n") + fprintf("}\n\n") +} diff --git a/db/leveldb/reader.go b/db/leveldb/reader.go new file mode 100644 index 000000000..ebc14cd8b --- /dev/null +++ b/db/leveldb/reader.go @@ -0,0 +1,154 @@ +package leveldb + +import ( + "encoding/binary" + "io" + "strconv" + "strings" + + "github.com/pkg/errors" + + "github.com/Mrs4s/go-cqhttp/global" +) + +type intReader struct { + data string + *strings.Reader +} + +func newIntReader(s string) intReader { + return intReader{ + data: s, + Reader: strings.NewReader(s), + } +} + +func (r *intReader) varint() int64 { + i, _ := binary.ReadVarint(r) + return i +} + +func (r *intReader) uvarint() uint64 { + i, _ := binary.ReadUvarint(r) + return i +} + +// reader implements the index read. +// data format is the same as the writer's +type reader struct { + data intReader + strings intReader + stringIndex map[uint64]string +} + +func (r *reader) coder() coder { o, _ := r.data.ReadByte(); return coder(o) } +func (r *reader) varint() int64 { return r.data.varint() } +func (r *reader) uvarint() uint64 { return r.data.uvarint() } + +func (r *reader) sync(c coder) { + if coder := r.coder(); coder != c { + panic("db/leveldb: bad sync expected " + strconv.Itoa(int(c)) + " but got " + strconv.Itoa(int(coder))) + } +} + +func (r *reader) int() int { + return int(r.varint()) +} + +func (r *reader) uint() uint { + return uint(r.uvarint()) +} + +func (r *reader) int32() int32 { + return int32(r.varint()) +} + +func (r *reader) uint32() uint32 { + return uint32(r.uvarint()) +} + +func (r *reader) int64() int64 { + return r.varint() +} + +func (r *reader) uint64() uint64 { + return r.uvarint() +} + +func (r *reader) string() string { + off := r.data.uvarint() + if s, ok := r.stringIndex[off]; ok { + return s + } + _, _ = r.strings.Seek(int64(off), io.SeekStart) + l := int64(r.strings.uvarint()) + whence, _ := r.strings.Seek(0, io.SeekCurrent) + s := r.strings.data[whence : whence+l] + r.stringIndex[off] = s + return s +} + +func (r *reader) msg() global.MSG { + length := r.uvarint() + msg := make(global.MSG, length) + for i := uint64(0); i < length; i++ { + s := r.string() + msg[s] = r.obj() + } + return msg +} + +func (r *reader) arrayMsg() []global.MSG { + length := r.uvarint() + msgs := make([]global.MSG, length) + for i := range msgs { + msgs[i] = r.msg() + } + return msgs +} + +func (r *reader) obj() interface{} { + switch coder := r.coder(); coder { + case coderNil: + return nil + case coderInt: + return int(r.varint()) + case coderUint: + return uint(r.uvarint()) + case coderInt32: + return int32(r.varint()) + case coderUint32: + return uint32(r.uvarint()) + case coderInt64: + return r.varint() + case coderUint64: + return r.uvarint() + case coderString: + return r.string() + case coderMSG: + return r.msg() + case coderArrayMSG: + return r.arrayMsg() + default: + panic("db/leveldb: invalid coder " + strconv.Itoa(int(coder))) + } +} + +func newReader(data string) (*reader, error) { + in := newIntReader(data) + v := in.uvarint() + if v != dataVersion { + return nil, errors.Errorf("db/leveldb: invalid data version %d", v) + } + sl := int64(in.uvarint()) + dl := int64(in.uvarint()) + whence, _ := in.Seek(0, io.SeekCurrent) + sData := data[whence : whence+sl] + dData := data[whence+sl : whence+sl+dl] + r := reader{ + data: newIntReader(dData), + strings: newIntReader(sData), + stringIndex: make(map[uint64]string), + } + return &r, nil +} diff --git a/db/leveldb/writer.go b/db/leveldb/writer.go new file mode 100644 index 000000000..c4add4438 --- /dev/null +++ b/db/leveldb/writer.go @@ -0,0 +1,149 @@ +package leveldb + +import ( + "bytes" + "io" + + "github.com/Mrs4s/go-cqhttp/global" +) + +type intWriter struct { + bytes.Buffer +} + +func (w *intWriter) varint(x int64) { + w.uvarint(uint64(x)<<1 ^ uint64(x>>63)) +} + +func (w *intWriter) uvarint(x uint64) { + for x >= 0x80 { + w.WriteByte(byte(x) | 0x80) + x >>= 7 + } + w.WriteByte(byte(x)) +} + +// writer implements the index write. +// data format(use uvarint to encode integers): +// | version | string data length | index data length | string data | index data | +// for string data part, each string is encoded as: +// | string length | string | +// for index data part, each value is encoded as: +// | coder | value | +// * coder is the identifier of value's type. +// * specially for string, it's value is the offset in string data part. +type writer struct { + data intWriter + strings intWriter + stringIndex map[string]uint64 +} + +func newWriter() *writer { + return &writer{ + stringIndex: make(map[string]uint64), + } +} + +func (w *writer) coder(o coder) { w.data.WriteByte(byte(o)) } +func (w *writer) varint(x int64) { w.data.varint(x) } +func (w *writer) uvarint(x uint64) { w.data.uvarint(x) } +func (w *writer) nil() { w.coder(coderNil) } + +func (w *writer) int(i int) { + w.varint(int64(i)) +} + +func (w *writer) uint(i uint) { + w.uvarint(uint64(i)) +} + +func (w *writer) int32(i int32) { + w.varint(int64(i)) +} + +func (w *writer) uint32(i uint32) { + w.uvarint(uint64(i)) +} + +func (w *writer) int64(i int64) { + w.varint(i) +} + +func (w *writer) uint64(i uint64) { + w.uvarint(i) +} + +func (w *writer) string(s string) { + off, ok := w.stringIndex[s] + if !ok { + // not found write to string data part + // | string length | string | + off = uint64(w.strings.Len()) + w.strings.uvarint(uint64(len(s))) + _, _ = w.strings.WriteString(s) + w.stringIndex[s] = off + } + // write offset to index data part + w.uvarint(off) +} + +func (w *writer) msg(m global.MSG) { + w.uvarint(uint64(len(m))) + for s, obj := range m { + w.string(s) + w.obj(obj) + } +} + +func (w *writer) arrayMsg(a []global.MSG) { + w.uvarint(uint64(len(a))) + for _, v := range a { + w.msg(v) + } +} + +func (w *writer) obj(o interface{}) { + switch x := o.(type) { + case nil: + w.nil() + case int: + w.coder(coderInt) + w.int(x) + case int32: + w.coder(coderInt32) + w.int32(x) + case int64: + w.coder(coderInt64) + w.int64(x) + case uint: + w.coder(coderUint) + w.uint(x) + case uint32: + w.coder(coderUint32) + w.uint32(x) + case uint64: + w.coder(coderUint64) + w.uint64(x) + case string: + w.coder(coderString) + w.string(x) + case global.MSG: + w.coder(coderMSG) + w.msg(x) + case []global.MSG: + w.coder(coderArrayMSG) + w.arrayMsg(x) + default: + panic("unsupported type") + } +} + +func (w *writer) bytes() []byte { + var out intWriter + out.uvarint(dataVersion) + out.uvarint(uint64(w.strings.Len())) + out.uvarint(uint64(w.data.Len())) + _, _ = io.Copy(&out, &w.strings) + _, _ = io.Copy(&out, &w.data) + return out.Bytes() +} diff --git a/modules/config/config.go b/modules/config/config.go index 84c4a3e2b..1386aa66f 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -75,11 +75,6 @@ type Server struct { Default string } -// LevelDBConfig leveldb 相关配置 -type LevelDBConfig struct { - Enable bool `yaml:"enable"` -} - // MongoDBConfig mongodb 相关配置 type MongoDBConfig struct { Enable bool `yaml:"enable"` From 197ca5a3ead385835644117a67659701d8e233be Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 15 Feb 2022 22:52:49 +0800 Subject: [PATCH 296/612] disable pprof module by default --- main.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 09cd2f0d0..0cd60791f 100644 --- a/main.go +++ b/main.go @@ -3,10 +3,12 @@ package main import ( "github.com/Mrs4s/go-cqhttp/cmd/gocq" - _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb - _ "github.com/Mrs4s/go-cqhttp/modules/mime" // mime检查模块 - _ "github.com/Mrs4s/go-cqhttp/modules/pprof" // pprof 性能分析 - _ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块 + _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb + _ "github.com/Mrs4s/go-cqhttp/modules/mime" // mime检查模块 + _ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块 + // 其他模块 + // _ "github.com/Mrs4s/go-cqhttp/db/mongodb" // mongodb 数据库支持 + // _ "github.com/Mrs4s/go-cqhttp/modules/pprof" // pprof 性能分析 ) func main() { From 2a66896d43b87c8700c36dd5209bb36a16959fb1 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 16 Feb 2022 01:13:02 +0800 Subject: [PATCH 297/612] coolq: new package cqcode move cqcode escape&unescape to new package --- coolq/api.go | 3 +- coolq/converter.go | 71 +++++++++---------- coolq/cqcode.go | 124 ++++------------------------------ coolq/cqcode/escape.go | 97 ++++++++++++++++++++++++++ coolq/cqcode_test.go | 6 +- db/leveldb/reader.go | 35 ++-------- db/leveldb/writer.go | 30 ++------ go.mod | 2 - go.sum | 11 +-- internal/selfupdate/update.go | 27 ++++++-- server/http.go | 5 +- 11 files changed, 192 insertions(+), 219 deletions(-) create mode 100644 coolq/cqcode/escape.go diff --git a/coolq/api.go b/coolq/api.go index 2e9677dc6..b9cf35273 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1984,8 +1984,7 @@ func OK(data interface{}) global.MSG { // Failed 生成失败返回值 func Failed(code int, msg ...string) global.MSG { - m := "" - w := "" + m, w := "", "" if len(msg) > 0 { m = msg[0] } diff --git a/coolq/converter.go b/coolq/converter.go index 44bc17cc5..154f3540a 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -13,38 +13,32 @@ import ( ) func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG { + sex := "unknown" + if m.Gender == 1 { // unknown = 0xff + sex = "female" + } else if m.Gender == 0 { + sex = "male" + } + role := "member" + switch m.Permission { // nolint:exhaustive + case client.Owner: + role = "owner" + case client.Administrator: + role = "admin" + } return global.MSG{ - "group_id": groupID, - "user_id": m.Uin, - "nickname": m.Nickname, - "card": m.CardName, - "sex": func() string { - if m.Gender == 1 { - return "female" - } else if m.Gender == 0 { - return "male" - } - // unknown = 0xff - return "unknown" - }(), + "group_id": groupID, + "user_id": m.Uin, + "nickname": m.Nickname, + "card": m.CardName, + "sex": sex, "age": 0, "area": "", "join_time": m.JoinTime, "last_sent_time": m.LastSpeakTime, "shut_up_timestamp": m.ShutUpTimestamp, "level": strconv.FormatInt(int64(m.Level), 10), - "role": func() string { - switch m.Permission { - case client.Owner: - return "owner" - case client.Administrator: - return "admin" - case client.Member: - return "member" - default: - return "member" - } - }(), + "role": role, "unfriendly": false, "title": m.SpecialTitle, "title_expire_time": m.SpecialTitleExpireTime, @@ -71,6 +65,10 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { PrimaryID: uint64(m.GroupCode), } cqm := ToStringMessage(m.Elements, source, true) + postType := "message" + if m.Sender.Uin == bot.Client.Uin { + postType = "message_sent" + } gm := global.MSG{ "anonymous": nil, "font": 0, @@ -78,14 +76,9 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { "message": ToFormattedMessage(m.Elements, source, false), "message_type": "group", "message_seq": m.Id, - "post_type": func() string { - if m.Sender.Uin == bot.Client.Uin { - return "message_sent" - } - return "message" - }(), - "raw_message": cqm, - "self_id": bot.Client.Uin, + "post_type": postType, + "raw_message": cqm, + "self_id": bot.Client.Uin, "sender": global.MSG{ "age": 0, "area": "", @@ -122,16 +115,14 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { } } ms := gm["sender"].(global.MSG) - switch mem.Permission { + role := "member" + switch mem.Permission { // nolint:exhaustive case client.Owner: - ms["role"] = "owner" + role = "owner" case client.Administrator: - ms["role"] = "admin" - case client.Member: - ms["role"] = "member" - default: - ms["role"] = "member" + role = "admin" } + ms["role"] = role ms["nickname"] = mem.Nickname ms["card"] = mem.CardName ms["title"] = mem.SpecialTitle diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 46f725283..c2c77a194 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -22,6 +22,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" + "github.com/Mrs4s/go-cqhttp/coolq/cqcode" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" @@ -289,7 +290,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]", db.ToGlobalID(rid, replyElem.ReplySeq), replyElem.ReplySeq, replyElem.Sender, replyElem.Time, - CQCodeEscapeValue(ToStringMessage(replyElem.Elements, source))) + cqcode.EscapeValue(ToStringMessage(replyElem.Elements, source))) } else { write("[CQ:reply,id=%d]", db.ToGlobalID(rid, replyElem.ReplySeq)) } @@ -304,7 +305,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. } } case *message.TextElement: - sb.WriteString(CQCodeEscapeText(o.Content)) + sb.WriteString(cqcode.EscapeText(o.Content)) case *message.AtElement: if o.Target == 0 { write("[CQ:at,qq=all]") @@ -321,13 +322,13 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. if ur { write(`[CQ:record,file=%s]`, o.Name) } else { - write(`[CQ:record,file=%s,url=%s]`, o.Name, CQCodeEscapeValue(o.Url)) + write(`[CQ:record,file=%s,url=%s]`, o.Name, cqcode.EscapeValue(o.Url)) } case *message.ShortVideoElement: if ur { write(`[CQ:video,file=%s]`, o.Name) } else { - write(`[CQ:video,file=%s,url=%s]`, o.Name, CQCodeEscapeValue(o.Url)) + write(`[CQ:video,file=%s,url=%s]`, o.Name, cqcode.EscapeValue(o.Url)) } case *message.GroupImageElement: var arg string @@ -340,7 +341,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. if ur { write("[CQ:image,file=%s%s]", hex.EncodeToString(o.Md5)+".image", arg) } else { - write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url), arg) + write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url), arg) } case *message.FriendImageElement: var arg string @@ -350,22 +351,22 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. if ur { write("[CQ:image,file=%s%s]", hex.EncodeToString(o.Md5)+".image", arg) } else { - write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url), arg) + write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url), arg) } case *message.GuildImageElement: - write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url)) + write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url)) case *message.DiceElement: write("[CQ:dice,value=%v]", o.Value) case *message.MarketFaceElement: sb.WriteString(o.Name) case *message.ServiceElement: if isOk := strings.Contains(o.Content, " 0 { if base.SplitURL { - for _, txt := range param.SplitURL(CQCodeUnescapeText(raw[:i])) { + for _, txt := range param.SplitURL(cqcode.UnescapeText(raw[:i])) { r = append(r, message.NewText(txt)) } } else { - r = append(r, message.NewText(CQCodeUnescapeText(raw[:i]))) + r = append(r, message.NewText(cqcode.UnescapeText(raw[:i]))) } } @@ -658,7 +659,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, sourceType MessageSourceType) if i+1 > len(raw) { return } - d[key] = CQCodeUnescapeValue(raw[:i]) + d[key] = cqcode.UnescapeValue(raw[:i]) raw = raw[i:] i = 0 } @@ -1060,7 +1061,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou return message.NewDice(int32(i)), nil case "xml": resID := d["resid"] - template := CQCodeEscapeValue(d["data"]) + template := cqcode.EscapeValue(d["data"]) i, _ := strconv.ParseInt(resID, 10, 64) msg := message.NewRichXml(template, i) return msg, nil @@ -1146,101 +1147,6 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou } } -/*CQCodeEscapeText 将字符串raw中部分字符转义 - -& -> & - -[ -> [ - -] -> ] - -*/ -func CQCodeEscapeText(s string) string { - count := strings.Count(s, "&") - count += strings.Count(s, "[") - count += strings.Count(s, "]") - if count == 0 { - return s - } - - // Apply replacements to buffer. - var b strings.Builder - b.Grow(len(s) + count*4) - start := 0 - for i := 0; i < count; i++ { - j := start - for index, r := range s[start:] { - if r == '&' || r == '[' || r == ']' { - j += index - break - } - } - b.WriteString(s[start:j]) - switch s[j] { - case '&': - b.WriteString("&") - case '[': - b.WriteString("[") - case ']': - b.WriteString("]") - } - start = j + 1 - } - b.WriteString(s[start:]) - return b.String() -} - -/*CQCodeEscapeValue 将字符串value中部分字符转义 - -, -> , - -& -> & - -[ -> [ - -] -> ] - -*/ -func CQCodeEscapeValue(value string) string { - ret := CQCodeEscapeText(value) - ret = strings.ReplaceAll(ret, ",", ",") - return ret -} - -/*CQCodeUnescapeText 将字符串content中部分字符反转义 - -& -> & - -[ -> [ - -] -> ] - -*/ -func CQCodeUnescapeText(content string) string { - ret := content - ret = strings.ReplaceAll(ret, "[", "[") - ret = strings.ReplaceAll(ret, "]", "]") - ret = strings.ReplaceAll(ret, "&", "&") - return ret -} - -/*CQCodeUnescapeValue 将字符串content中部分字符反转义 - -, -> , - -& -> & - -[ -> [ - -] -> ] - -*/ -func CQCodeUnescapeValue(content string) string { - ret := strings.ReplaceAll(content, ",", ",") - ret = CQCodeUnescapeText(ret) - return ret -} - // makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用 func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceType MessageSourceType) (message.IMessageElement, error) { f := d["file"] diff --git a/coolq/cqcode/escape.go b/coolq/cqcode/escape.go new file mode 100644 index 000000000..a9f0cdf40 --- /dev/null +++ b/coolq/cqcode/escape.go @@ -0,0 +1,97 @@ +// Package cqcode provides CQCode util functions. +package cqcode + +import "strings" + +/*EscapeText 将字符串raw中部分字符转义 + +& -> & + +[ -> [ + +] -> ] + +*/ +func EscapeText(s string) string { + count := strings.Count(s, "&") + count += strings.Count(s, "[") + count += strings.Count(s, "]") + if count == 0 { + return s + } + + // Apply replacements to buffer. + var b strings.Builder + b.Grow(len(s) + count*4) + start := 0 + for i := 0; i < count; i++ { + j := start + for index, r := range s[start:] { + if r == '&' || r == '[' || r == ']' { + j += index + break + } + } + b.WriteString(s[start:j]) + switch s[j] { + case '&': + b.WriteString("&") + case '[': + b.WriteString("[") + case ']': + b.WriteString("]") + } + start = j + 1 + } + b.WriteString(s[start:]) + return b.String() +} + +/*EscapeValue 将字符串value中部分字符转义 + +, -> , + +& -> & + +[ -> [ + +] -> ] + +*/ +func EscapeValue(value string) string { + ret := EscapeText(value) + return strings.ReplaceAll(ret, ",", ",") +} + +/*UnescapeText 将字符串content中部分字符反转义 + +& -> & + +[ -> [ + +] -> ] + +*/ +func UnescapeText(content string) string { + ret := content + ret = strings.ReplaceAll(ret, "[", "[") + ret = strings.ReplaceAll(ret, "]", "]") + ret = strings.ReplaceAll(ret, "&", "&") + return ret +} + +/*UnescapeValue 将字符串content中部分字符反转义 + +, -> , + +& -> & + +[ -> [ + +] -> ] + +*/ +func UnescapeValue(content string) string { + ret := strings.ReplaceAll(content, ",", ",") + return UnescapeText(ret) +} diff --git a/coolq/cqcode_test.go b/coolq/cqcode_test.go index 338ca8f0a..0c8bf7ed0 100644 --- a/coolq/cqcode_test.go +++ b/coolq/cqcode_test.go @@ -8,6 +8,8 @@ import ( "github.com/Mrs4s/MiraiGo/utils" "github.com/stretchr/testify/assert" "github.com/tidwall/gjson" + + "github.com/Mrs4s/go-cqhttp/coolq/cqcode" ) var bot = CQBot{} @@ -41,7 +43,7 @@ const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&98765 func BenchmarkCQCodeEscapeText(b *testing.B) { for i := 0; i < b.N; i++ { ret := bText - CQCodeEscapeText(ret) + cqcode.EscapeText(ret) } } @@ -61,6 +63,6 @@ func TestCQCodeEscapeText(t *testing.T) { ret = strings.ReplaceAll(ret, "&", "&") ret = strings.ReplaceAll(ret, "[", "[") ret = strings.ReplaceAll(ret, "]", "]") - assert.Equal(t, ret, CQCodeEscapeText(rs)) + assert.Equal(t, ret, cqcode.EscapeText(rs)) } } diff --git a/db/leveldb/reader.go b/db/leveldb/reader.go index ebc14cd8b..e89103640 100644 --- a/db/leveldb/reader.go +++ b/db/leveldb/reader.go @@ -44,36 +44,13 @@ type reader struct { func (r *reader) coder() coder { o, _ := r.data.ReadByte(); return coder(o) } func (r *reader) varint() int64 { return r.data.varint() } func (r *reader) uvarint() uint64 { return r.data.uvarint() } +func (r *reader) int32() int32 { return int32(r.varint()) } +func (r *reader) int64() int64 { return r.varint() } +func (r *reader) uint64() uint64 { return r.uvarint() } -func (r *reader) sync(c coder) { - if coder := r.coder(); coder != c { - panic("db/leveldb: bad sync expected " + strconv.Itoa(int(c)) + " but got " + strconv.Itoa(int(coder))) - } -} - -func (r *reader) int() int { - return int(r.varint()) -} - -func (r *reader) uint() uint { - return uint(r.uvarint()) -} - -func (r *reader) int32() int32 { - return int32(r.varint()) -} - -func (r *reader) uint32() uint32 { - return uint32(r.uvarint()) -} - -func (r *reader) int64() int64 { - return r.varint() -} - -func (r *reader) uint64() uint64 { - return r.uvarint() -} +// func (r *reader) uint32() uint32 { return uint32(r.uvarint()) } +// func (r *reader) int() int { return int(r.varint()) } +// func (r *reader) uint() uint { return uint(r.uvarint()) } func (r *reader) string() string { off := r.data.uvarint() diff --git a/db/leveldb/writer.go b/db/leveldb/writer.go index c4add4438..a8fe70755 100644 --- a/db/leveldb/writer.go +++ b/db/leveldb/writer.go @@ -48,30 +48,12 @@ func (w *writer) coder(o coder) { w.data.WriteByte(byte(o)) } func (w *writer) varint(x int64) { w.data.varint(x) } func (w *writer) uvarint(x uint64) { w.data.uvarint(x) } func (w *writer) nil() { w.coder(coderNil) } - -func (w *writer) int(i int) { - w.varint(int64(i)) -} - -func (w *writer) uint(i uint) { - w.uvarint(uint64(i)) -} - -func (w *writer) int32(i int32) { - w.varint(int64(i)) -} - -func (w *writer) uint32(i uint32) { - w.uvarint(uint64(i)) -} - -func (w *writer) int64(i int64) { - w.varint(i) -} - -func (w *writer) uint64(i uint64) { - w.uvarint(i) -} +func (w *writer) int(i int) { w.varint(int64(i)) } +func (w *writer) uint(i uint) { w.uvarint(uint64(i)) } +func (w *writer) int32(i int32) { w.varint(int64(i)) } +func (w *writer) uint32(i uint32) { w.uvarint(uint64(i)) } +func (w *writer) int64(i int64) { w.varint(i) } +func (w *writer) uint64(i uint64) { w.uvarint(i) } func (w *writer) string(s string) { off, ok := w.stringIndex[s] diff --git a/go.mod b/go.mod index 5a94b4f5b..6b0ef76d6 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,8 @@ require ( github.com/Microsoft/go-winio v0.5.1 github.com/Mrs4s/MiraiGo v0.0.0-20220214082717-0e68a1e7b715 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc - github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 - github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/mattn/go-colorable v0.1.12 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 7d2a47437..20d014bf7 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,6 @@ github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMm github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -29,6 +27,7 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -41,8 +40,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -93,6 +90,7 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -125,6 +123,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -147,8 +146,10 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -174,6 +175,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/selfupdate/update.go b/internal/selfupdate/update.go index 2a05f606e..5aa52a3a7 100644 --- a/internal/selfupdate/update.go +++ b/internal/selfupdate/update.go @@ -7,13 +7,12 @@ import ( "fmt" "hash" "io" + "math" "os" "path/filepath" "runtime" "strings" - "github.com/dustin/go-humanize" - "github.com/kardianos/osext" "github.com/sirupsen/logrus" "github.com/tidwall/gjson" @@ -147,13 +146,33 @@ func (wc *writeSumCounter) Write(p []byte) (int, error) { wc.total += uint64(n) wc.hash.Write(p) fmt.Printf("\r ") - fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.total)) + fmt.Printf("\rDownloading... %s complete", humanBytes(wc.total)) return n, nil } +func logn(n, b float64) float64 { + return math.Log(n) / math.Log(b) +} + +func humanBytes(s uint64) string { + sizes := []string{"B", "kB", "MB", "GB"} // GB对于go-cqhttp来说已经够用了 + if s < 10 { + return fmt.Sprintf("%d B", s) + } + e := math.Floor(logn(float64(s), 1000)) + suffix := sizes[int(e)] + val := math.Floor(float64(s)/math.Pow(1000, e)*10+0.5) / 10 + f := "%.0f %s" + if val < 10 { + f = "%.1f %s" + } + return fmt.Sprintf(f, val, suffix) +} + // FromStream copy form getlantern/go-update func fromStream(updateWith io.Reader) (err error, errRecover error) { - updatePath, err := osext.Executable() + updatePath, err := os.Executable() + updatePath = filepath.Clean(updatePath) if err != nil { return } diff --git a/server/http.go b/server/http.go index 0a73db8f3..b289eef8e 100644 --- a/server/http.go +++ b/server/http.go @@ -51,7 +51,6 @@ type httpServerPost struct { } type httpServer struct { - HTTP *http.Server api *api.Caller accessToken string } @@ -259,11 +258,11 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) { go func() { log.Infof("CQ HTTP 服务器已启动: %v", addr) - s.HTTP = &http.Server{ + server := &http.Server{ Addr: addr, Handler: s, } - if err := s.HTTP.ListenAndServe(); err != nil && err != http.ErrServerClosed { + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Error(err) log.Infof("HTTP 服务启动失败, 请检查端口是否被占用.") log.Warnf("将在五秒后退出.") From 83266850880c2ea5540d0bf820972d6eaa900ed9 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Feb 2022 15:09:57 +0800 Subject: [PATCH 298/612] coolq: only truncate replySeq to uint16 on private message --- coolq/cqcode.go | 39 ++++++++++++++++++++------------------- db/database.go | 24 ++++++++---------------- go.mod | 2 +- go.sum | 11 ++--------- 4 files changed, 31 insertions(+), 45 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index c2c77a194..dfd83e156 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -103,6 +103,20 @@ func (e *PokeElement) Type() message.ElementType { return message.At } +func replyID(r *message.ReplyElement, source MessageSource) int32 { + id := int64(source.PrimaryID) + seq := r.ReplySeq + if source.SourceType == MessageSourcePrivate { + // 私聊似乎腾讯服务器有bug? + seq = int32(uint16(seq)) + id = r.Sender + } + if r.GroupID != 0 { + id = r.GroupID + } + return db.ToGlobalID(id, seq) +} + // ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报 func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []global.MSG) { r = make([]global.MSG, 0, len(e)) @@ -113,18 +127,12 @@ func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []glob }) if reply != nil && source.SourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) - rid := int64(source.PrimaryID) - if source.SourceType == MessageSourcePrivate { - rid = replyElem.Sender - } - if replyElem.GroupID != 0 { - rid = replyElem.GroupID - } + id := replyID(replyElem, source) if base.ExtraReplyData { r = append(r, global.MSG{ "type": "reply", "data": map[string]string{ - "id": strconv.FormatInt(int64(db.ToGlobalID(rid, replyElem.ReplySeq)), 10), + "id": strconv.FormatInt(int64(id), 10), "seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10), "qq": strconv.FormatInt(replyElem.Sender, 10), "time": strconv.FormatInt(int64(replyElem.Time), 10), @@ -134,7 +142,7 @@ func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []glob } else { r = append(r, global.MSG{ "type": "reply", - "data": map[string]string{"id": strconv.FormatInt(int64(db.ToGlobalID(rid, replyElem.ReplySeq)), 10)}, + "data": map[string]string{"id": strconv.FormatInt(int64(id), 10)}, }) } } @@ -279,20 +287,13 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. }) if reply != nil && source.SourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) - rid := int64(source.PrimaryID) - if source.SourceType == MessageSourcePrivate { - rid = replyElem.Sender - } - if replyElem.GroupID != 0 { - rid = replyElem.GroupID - } + id := replyID(replyElem, source) if base.ExtraReplyData { write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]", - db.ToGlobalID(rid, replyElem.ReplySeq), - replyElem.ReplySeq, replyElem.Sender, replyElem.Time, + id, replyElem.ReplySeq, replyElem.Sender, replyElem.Time, cqcode.EscapeValue(ToStringMessage(replyElem.Elements, source))) } else { - write("[CQ:reply,id=%d]", db.ToGlobalID(rid, replyElem.ReplySeq)) + write("[CQ:reply,id=%d]", id) } } for i, elem := range e { diff --git a/db/database.go b/db/database.go index 6495f8236..c19da97f7 100644 --- a/db/database.go +++ b/db/database.go @@ -103,22 +103,14 @@ func ToGlobalID(code int64, msgID int32) int32 { return int32(crc32.ChecksumIEEE([]byte(fmt.Sprintf("%d-%d", code, msgID)))) } -func (m *StoredGroupMessage) GetID() string { return m.ID } - -func (m *StoredGroupMessage) GetType() string { return "group" } - -func (m *StoredGroupMessage) GetGlobalID() int32 { return m.GlobalID } - +func (m *StoredGroupMessage) GetID() string { return m.ID } +func (m *StoredGroupMessage) GetType() string { return "group" } +func (m *StoredGroupMessage) GetGlobalID() int32 { return m.GlobalID } func (m *StoredGroupMessage) GetAttribute() *StoredMessageAttribute { return m.Attribute } +func (m *StoredGroupMessage) GetContent() []global.MSG { return m.Content } -func (m *StoredGroupMessage) GetContent() []global.MSG { return m.Content } - -func (m *StoredPrivateMessage) GetID() string { return m.ID } - -func (m *StoredPrivateMessage) GetType() string { return "private" } - -func (m *StoredPrivateMessage) GetGlobalID() int32 { return m.GlobalID } - +func (m *StoredPrivateMessage) GetID() string { return m.ID } +func (m *StoredPrivateMessage) GetType() string { return "private" } +func (m *StoredPrivateMessage) GetGlobalID() int32 { return m.GlobalID } func (m *StoredPrivateMessage) GetAttribute() *StoredMessageAttribute { return m.Attribute } - -func (m *StoredPrivateMessage) GetContent() []global.MSG { return m.Content } +func (m *StoredPrivateMessage) GetContent() []global.MSG { return m.Content } diff --git a/go.mod b/go.mod index 6b0ef76d6..6459e606b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220214082717-0e68a1e7b715 + github.com/Mrs4s/MiraiGo v0.0.0-20220218065747-0578942d86ab github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 20d014bf7..bb1a78cca 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220214082717-0e68a1e7b715 h1:6tSKbzugDl1V+htbVtTpiuVTpINGGXIRD8PBB64Sjoc= -github.com/Mrs4s/MiraiGo v0.0.0-20220214082717-0e68a1e7b715/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= +github.com/Mrs4s/MiraiGo v0.0.0-20220218065747-0578942d86ab h1:kYJNHTBfkmEByDVIOvdCT5NMH9xbx915MOmpKGGXLWs= +github.com/Mrs4s/MiraiGo v0.0.0-20220218065747-0578942d86ab/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 h1:8CK7Hg+CRGTFhpjvp5V+7wd8/TkuZ6fSuztLVV3bwoQ= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= @@ -27,7 +27,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -90,7 +89,6 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -123,7 +121,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -146,10 +143,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -175,8 +170,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 421e5bfc230219e6a04eba2ed8a06e119453b7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 18 Feb 2022 17:23:50 +0800 Subject: [PATCH 299/612] =?UTF-8?q?feat:=20=E9=89=B4=E4=BA=8Envjdc?= =?UTF-8?q?=E7=BB=8F=E5=B8=B8=E4=B8=8D=E5=8F=AF=E7=94=A8=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E6=89=8B=E5=8A=A8=E6=9B=B4=E6=96=B0=E5=90=8E=E4=B8=8D?= =?UTF-8?q?=E4=BC=9A=E6=9B=B4=E6=96=B0=E8=AF=A5=E6=97=B6=E9=97=B4=E6=88=B3?= =?UTF-8?q?=EF=BC=8C=E6=9A=82=E6=97=B6=E5=B1=8F=E8=94=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/env.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/qqrobot/qinglong/env.go b/qqrobot/qinglong/env.go index ff1c037a5..4c1f25510 100644 --- a/qqrobot/qinglong/env.go +++ b/qqrobot/qinglong/env.go @@ -6,7 +6,6 @@ import ( "fmt" "net/url" "os" - "strconv" "strings" "time" ) @@ -30,21 +29,22 @@ func (e *EnvDBEntry) CreateTime() int64 { // UpdateTime 更新unix时间戳 func (e *EnvDBEntry) UpdateTime() int64 { - // 先尝试解析nvjdc设置的更新时间:test@@1640780099690@@UID_xxxxxx - if strings.Contains(e.Remarks, "@@") { - for _, remarkPart := range strings.Split(e.Remarks, "@@") { - if len(remarkPart) != 13 { - continue - } - - millTimeStamp, err := strconv.ParseInt(remarkPart, 10, 64) - if err != nil { - continue - } - - return time.UnixMilli(millTimeStamp).Unix() - } - } + // re: 鉴于nvjdc经常不可用,避免手动更新后不会更新该时间戳,暂时屏蔽 + // // 先尝试解析nvjdc设置的更新时间:test@@1640780099690@@UID_xxxxxx + // if strings.Contains(e.Remarks, "@@") { + // for _, remarkPart := range strings.Split(e.Remarks, "@@") { + // if len(remarkPart) != 13 { + // continue + // } + // + // millTimeStamp, err := strconv.ParseInt(remarkPart, 10, 64) + // if err != nil { + // continue + // } + // + // return time.UnixMilli(millTimeStamp).Unix() + // } + // } // 否则,解析青龙设置的更新时间 // Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间) From a85f846a5f8e9c9dcfdbeffddf146063c9beaea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Fri, 18 Feb 2022 18:38:58 +0800 Subject: [PATCH 300/612] fix: get_msg returns nil on local image element (#1342) * fix: get_msg returns nil on local image element * fix: make lint happy --- coolq/cqcode.go | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index dfd83e156..b08dc252e 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -45,6 +45,7 @@ type PokeElement struct { type LocalImageElement struct { Stream io.ReadSeeker File string + Url string Flash bool EffectID int32 @@ -354,6 +355,20 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. } else { write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url), arg) } + case *LocalImageElement: + var arg string + if o.Flash { + arg = ",type=flash" + } + data, err := os.ReadFile(o.File) + if err == nil { + m := md5.Sum(data) + if ur { + write("[CQ:image,file=%s%s]", hex.EncodeToString(m[:])+".image", arg) + } else { + write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(m[:])+".image", CQCodeEscapeValue(o.Url), arg) + } + } case *message.GuildImageElement: write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url)) case *message.DiceElement: @@ -796,7 +811,13 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType Message case "text": r = append(r, message.NewText(data["text"].(string))) case "image": - e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, false, sourceType) + u, ok := data["url"] + d := make(map[string]string, 2) + if ok { + d["url"] = u.(string) + } + d["file"] = data["file"].(string) + e, err := bot.makeImageOrVideoElem(d, false, sourceType) if err != nil { log.Warnf("make image elem error: %v", err) continue @@ -1151,6 +1172,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou // makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用 func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceType MessageSourceType) (message.IMessageElement, error) { f := d["file"] + u, ok := d["url"] + if !ok { + u = "" + } if strings.HasPrefix(f, "http") { hash := md5.Sum([]byte(f)) cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash[:])+".cache") @@ -1173,7 +1198,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if video { return &LocalVideoElement{File: cacheFile}, nil } - return &LocalImageElement{File: cacheFile}, nil + return &LocalImageElement{File: cacheFile, Url: f}, nil } if strings.HasPrefix(f, "file") { fu, err := url.Parse(f) @@ -1199,14 +1224,14 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if info.Size() == 0 || info.Size() >= maxImageSize { return nil, errors.New("invalid image size") } - return &LocalImageElement{File: fu.Path}, nil + return &LocalImageElement{File: fu.Path, Url: f}, nil } if strings.HasPrefix(f, "base64") && !video { b, err := param.Base64DecodeString(strings.TrimPrefix(f, "base64://")) if err != nil { return nil, err } - return &LocalImageElement{Stream: bytes.NewReader(b)}, nil + return &LocalImageElement{Stream: bytes.NewReader(b), Url: f}, nil } rawPath := path.Join(global.ImagePath, f) if video { @@ -1255,7 +1280,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return nil, errors.New("invalid image") } if path.Ext(rawPath) != ".image" { - return &LocalImageElement{File: rawPath}, nil + return &LocalImageElement{File: rawPath, Url: u}, nil } b, err := os.ReadFile(rawPath) if err != nil { From 75bed6aabc24b083cc5afe05b711298ff8fd840e Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Feb 2022 19:11:43 +0800 Subject: [PATCH 301/612] coolq: clean readImageCache --- cmd/gocq/main.go | 2 +- coolq/cqcode.go | 45 +++++++++++++++++---------------------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 2ce945b3a..2b5952c98 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -81,7 +81,7 @@ func Main() { mkCacheDir := func(path string, _type string) { if !global.PathExists(path) { - if err := os.MkdirAll(path, 0o755); err != nil { + if err := os.MkdirAll(path, 0o644); err != nil { log.Fatalf("创建%s缓存文件夹失败: %v", _type, err) } } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index b08dc252e..1aa9b94db 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -45,7 +45,7 @@ type PokeElement struct { type LocalImageElement struct { Stream io.ReadSeeker File string - Url string + URL string Flash bool EffectID int32 @@ -366,7 +366,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. if ur { write("[CQ:image,file=%s%s]", hex.EncodeToString(m[:])+".image", arg) } else { - write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(m[:])+".image", CQCodeEscapeValue(o.Url), arg) + write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(m[:])+".image", cqcode.EscapeValue(o.URL), arg) } } case *message.GuildImageElement: @@ -1198,7 +1198,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if video { return &LocalVideoElement{File: cacheFile}, nil } - return &LocalImageElement{File: cacheFile, Url: f}, nil + return &LocalImageElement{File: cacheFile, URL: f}, nil } if strings.HasPrefix(f, "file") { fu, err := url.Parse(f) @@ -1224,14 +1224,14 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if info.Size() == 0 || info.Size() >= maxImageSize { return nil, errors.New("invalid image size") } - return &LocalImageElement{File: fu.Path, Url: f}, nil + return &LocalImageElement{File: fu.Path, URL: f}, nil } if strings.HasPrefix(f, "base64") && !video { b, err := param.Base64DecodeString(strings.TrimPrefix(f, "base64://")) if err != nil { return nil, err } - return &LocalImageElement{Stream: bytes.NewReader(b), Url: f}, nil + return &LocalImageElement{Stream: bytes.NewReader(b), URL: f}, nil } rawPath := path.Join(global.ImagePath, f) if video { @@ -1280,7 +1280,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return nil, errors.New("invalid image") } if path.Ext(rawPath) != ".image" { - return &LocalImageElement{File: rawPath, Url: u}, nil + return &LocalImageElement{File: rawPath, URL: u}, nil } b, err := os.ReadFile(rawPath) if err != nil { @@ -1299,38 +1299,27 @@ func (bot *CQBot) readImageCache(b []byte, sourceType MessageSourceType) (messag size := r.ReadInt32() r.ReadString() imageURL := r.ReadString() - if size == 0 { - if imageURL != "" { - return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType) - } - return nil, errors.New("img size is 0") - } - if len(hash) != 16 { - return nil, errors.New("invalid hash") + if size == 0 && imageURL != "" { + return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType) } var rsp message.IMessageElement - if sourceType == MessageSourceGroup { + switch sourceType { // nolint:exhaustive + case MessageSourceGroup: rsp, err = bot.Client.QueryGroupImage(int64(rand.Uint32()), hash, size) - goto ok - } - if sourceType == MessageSourceGuildChannel { + case MessageSourceGuildChannel: if len(bot.Client.GuildService.Guilds) == 0 { err = errors.New("cannot query guild image: not any joined guild") - goto ok + break } guild := bot.Client.GuildService.Guilds[0] rsp, err = bot.Client.GuildService.QueryImage(guild.GuildId, guild.Channels[0].ChannelId, hash, uint64(size)) - goto ok + default: + rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size) } - rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size) -ok: - if err != nil { - if imageURL != "" { - return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType) - } - return nil, err + if err != nil && imageURL != "" { + return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType) } - return rsp, nil + return rsp, err } func (bot *CQBot) readVideoCache(b []byte) message.IMessageElement { From e6904d8dde3960f6f4ba06a2859220746cccf839 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 19 Feb 2022 18:55:28 +0800 Subject: [PATCH 302/612] internal/btree: implement file lock For #1366 --- internal/btree/btree.go | 18 ++++++++++++ internal/btree/file_lock_unix.go | 45 +++++++++++++++++++++++++++++ internal/btree/file_lock_windows.go | 28 ++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 internal/btree/file_lock_unix.go create mode 100644 internal/btree/file_lock_windows.go diff --git a/internal/btree/btree.go b/internal/btree/btree.go index 7b3b49cb7..81adcb902 100644 --- a/internal/btree/btree.go +++ b/internal/btree/btree.go @@ -18,6 +18,10 @@ const ( tableStructSize = int(unsafe.Sizeof(table{})) ) +type fileLock interface { + release() error +} + type item struct { hash [hashSize]byte offset int64 @@ -48,6 +52,7 @@ type DB struct { alloc int64 cache [cacheSlots]cache + flock fileLock inAllocator bool deleteLarger bool fqueue [freeQueueLen]chunk @@ -108,6 +113,10 @@ func (d *DB) flushSuper() { // Open opens an existed btree file func Open(name string) (*DB, error) { + lock, err := newFileLock(name + ".lock") + if err != nil { + return nil, errors.New("文件被其他进程占用") + } btree := new(DB) fd, err := os.OpenFile(name, os.O_RDWR, 0o644) if err != nil { @@ -120,17 +129,23 @@ func Open(name string) (*DB, error) { btree.top = super.top btree.freeTop = super.freeTop btree.alloc = super.alloc + btree.flock = lock return btree, errors.Wrap(err, "btree read meta info failed") } // Create creates a database func Create(name string) (*DB, error) { + lock, err := newFileLock(name + ".lock") + if err != nil { + return nil, errors.New("文件被其他进程占用") + } btree := new(DB) fd, err := os.OpenFile(name, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0o644) if err != nil { return nil, errors.Wrap(err, "btree open file failed") } + btree.flock = lock btree.fd = fd btree.alloc = int64(superSize) btree.flushSuper() @@ -140,6 +155,9 @@ func Create(name string) (*DB, error) { // Close closes the database func (d *DB) Close() error { _ = d.fd.Sync() + if err := d.flock.release(); err != nil { + return err + } err := d.fd.Close() for i := 0; i < cacheSlots; i++ { d.cache[i] = cache{} diff --git a/internal/btree/file_lock_unix.go b/internal/btree/file_lock_unix.go new file mode 100644 index 000000000..897428437 --- /dev/null +++ b/internal/btree/file_lock_unix.go @@ -0,0 +1,45 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd + +package btree + +import ( + "os" + "syscall" +) + +type unixFileLock struct { + f *os.File +} + +func (fl *unixFileLock) release() error { + if err := setFileLock(fl.f, false); err != nil { + return err + } + return fl.f.Close() +} + +func newFileLock(path string) (fl fileLock, err error) { + flag := os.O_RDWR + f, err := os.OpenFile(path, flag, 0) + if os.IsNotExist(err) { + f, err = os.OpenFile(path, flag|os.O_CREATE, 0644) + } + if err != nil { + return + } + err = setFileLock(f, true) + if err != nil { + f.Close() + return + } + fl = &unixFileLock{f: f} + return +} + +func setFileLock(f *os.File, lock bool) error { + how := syscall.LOCK_UN + if lock { + how = syscall.LOCK_EX + } + return syscall.Flock(int(f.Fd()), how|syscall.LOCK_NB) +} diff --git a/internal/btree/file_lock_windows.go b/internal/btree/file_lock_windows.go new file mode 100644 index 000000000..becdfaded --- /dev/null +++ b/internal/btree/file_lock_windows.go @@ -0,0 +1,28 @@ +package btree + +import "syscall" + +type windowsFileLock struct { + fd syscall.Handle +} + +func (fl *windowsFileLock) release() error { + return syscall.Close(fl.fd) +} + +func newFileLock(path string) (fileLock, error) { + pathp, err := syscall.UTF16PtrFromString(path) + if err != nil { + return nil, err + } + + const access uint32 = syscall.GENERIC_READ | syscall.GENERIC_WRITE + fd, err := syscall.CreateFile(pathp, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0) + if err == syscall.ERROR_FILE_NOT_FOUND { + fd, err = syscall.CreateFile(pathp, access, 0, nil, syscall.OPEN_ALWAYS, syscall.FILE_ATTRIBUTE_NORMAL, 0) + } + if err != nil { + return nil, err + } + return &windowsFileLock{fd: fd}, nil +} From ee749a45fceb39911966ef0993a3b1dd49273d33 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 19 Feb 2022 10:56:44 +0000 Subject: [PATCH 303/612] ci(chore): Fix stylings --- internal/btree/temp.1353328705.db.lock | 0 internal/btree/temp.1925824040.db.lock | 0 internal/btree/temp.2099670424.db.lock | 0 internal/btree/temp.2437681929.db.lock | 0 internal/btree/temp.315143417.db.lock | 0 internal/btree/temp.3409689178.db.lock | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 internal/btree/temp.1353328705.db.lock create mode 100644 internal/btree/temp.1925824040.db.lock create mode 100644 internal/btree/temp.2099670424.db.lock create mode 100644 internal/btree/temp.2437681929.db.lock create mode 100644 internal/btree/temp.315143417.db.lock create mode 100644 internal/btree/temp.3409689178.db.lock diff --git a/internal/btree/temp.1353328705.db.lock b/internal/btree/temp.1353328705.db.lock new file mode 100644 index 000000000..e69de29bb diff --git a/internal/btree/temp.1925824040.db.lock b/internal/btree/temp.1925824040.db.lock new file mode 100644 index 000000000..e69de29bb diff --git a/internal/btree/temp.2099670424.db.lock b/internal/btree/temp.2099670424.db.lock new file mode 100644 index 000000000..e69de29bb diff --git a/internal/btree/temp.2437681929.db.lock b/internal/btree/temp.2437681929.db.lock new file mode 100644 index 000000000..e69de29bb diff --git a/internal/btree/temp.315143417.db.lock b/internal/btree/temp.315143417.db.lock new file mode 100644 index 000000000..e69de29bb diff --git a/internal/btree/temp.3409689178.db.lock b/internal/btree/temp.3409689178.db.lock new file mode 100644 index 000000000..e69de29bb From 31cdd337677fc607e6cf8adca203541cb7379aab Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 19 Feb 2022 20:19:17 +0800 Subject: [PATCH 304/612] internal/btree: remove lock file after testing --- .gitignore | 2 ++ internal/btree/btree_test.go | 11 ++++++++--- internal/btree/temp.1353328705.db.lock | 0 internal/btree/temp.1925824040.db.lock | 0 internal/btree/temp.2099670424.db.lock | 0 internal/btree/temp.2437681929.db.lock | 0 internal/btree/temp.315143417.db.lock | 0 internal/btree/temp.3409689178.db.lock | 0 8 files changed, 10 insertions(+), 3 deletions(-) delete mode 100644 internal/btree/temp.1353328705.db.lock delete mode 100644 internal/btree/temp.1925824040.db.lock delete mode 100644 internal/btree/temp.2099670424.db.lock delete mode 100644 internal/btree/temp.2437681929.db.lock delete mode 100644 internal/btree/temp.315143417.db.lock delete mode 100644 internal/btree/temp.3409689178.db.lock diff --git a/.gitignore b/.gitignore index d2ee21bf5..43a051cdf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ session.token device.json data/ logs/ +internal/btree/*.lock +internal/btree/*.db \ No newline at end of file diff --git a/internal/btree/btree_test.go b/internal/btree/btree_test.go index 5d16c5410..464c7ac76 100644 --- a/internal/btree/btree_test.go +++ b/internal/btree/btree_test.go @@ -16,16 +16,21 @@ func tempfile(t *testing.T) string { return temp.Name() } +func removedb(name string) { + os.Remove(name) + os.Remove(name + ".lock") +} + func TestCreate(t *testing.T) { f := tempfile(t) _, err := Create(f) assert2.NoError(t, err) - defer os.Remove(f) + defer removedb(f) } func TestBtree(t *testing.T) { f := tempfile(t) - defer os.Remove(f) + defer removedb(f) bt, err := Create(f) assert := assert2.New(t) assert.NoError(err) @@ -73,7 +78,7 @@ func testForeach(t *testing.T, elemSize int) { expected[i] = utils.RandomString(20) } f := tempfile(t) - defer os.Remove(f) + defer removedb(f) bt, err := Create(f) assert2.NoError(t, err) for _, v := range expected { diff --git a/internal/btree/temp.1353328705.db.lock b/internal/btree/temp.1353328705.db.lock deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/btree/temp.1925824040.db.lock b/internal/btree/temp.1925824040.db.lock deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/btree/temp.2099670424.db.lock b/internal/btree/temp.2099670424.db.lock deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/btree/temp.2437681929.db.lock b/internal/btree/temp.2437681929.db.lock deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/btree/temp.315143417.db.lock b/internal/btree/temp.315143417.db.lock deleted file mode 100644 index e69de29bb..000000000 diff --git a/internal/btree/temp.3409689178.db.lock b/internal/btree/temp.3409689178.db.lock deleted file mode 100644 index e69de29bb..000000000 From 2709c5d448fd285c7169d04fdffeb1c0adecf22b Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 20 Feb 2022 23:36:29 +0800 Subject: [PATCH 305/612] dep: update MiraiGo & revert disabling multi-thread upload --- coolq/bot.go | 9 ++------- go.mod | 2 +- go.sum | 11 +++++++++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 099be8aa7..950f63f4c 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "os" - "path" "runtime/debug" "sync" "time" @@ -144,7 +143,7 @@ func (bot *CQBot) UploadLocalImageAsGroup(groupCode int64, img *LocalImageElemen if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { return nil, errors.New("image type error: " + mime) } - i, err = bot.Client.UploadGroupImage(groupCode, img.Stream) + i, err = bot.Client.UploadGroupImage(groupCode, img.Stream, 4) if i != nil { i.Flash = img.Flash i.EffectID = img.EffectID @@ -159,11 +158,7 @@ func (bot *CQBot) UploadLocalVideo(target int64, v *LocalVideoElement) (*message return nil, err } defer func() { _ = video.Close() }() - hash, _ := utils.ComputeMd5AndLength(io.MultiReader(video, v.thumb)) - cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash)+".cache") - _, _ = video.Seek(0, io.SeekStart) - _, _ = v.thumb.Seek(0, io.SeekStart) - return bot.Client.UploadGroupShortVideo(target, video, v.thumb, cacheFile) + return bot.Client.UploadGroupShortVideo(target, video, v.thumb, 4) } // UploadLocalImageAsPrivate 上传本地图片至私聊 diff --git a/go.mod b/go.mod index 6459e606b..4f70cc18d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220218065747-0578942d86ab + github.com/Mrs4s/MiraiGo v0.0.0-20220220152607-5e8a5126989d github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index bb1a78cca..8ffb3232b 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220218065747-0578942d86ab h1:kYJNHTBfkmEByDVIOvdCT5NMH9xbx915MOmpKGGXLWs= -github.com/Mrs4s/MiraiGo v0.0.0-20220218065747-0578942d86ab/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= +github.com/Mrs4s/MiraiGo v0.0.0-20220220152607-5e8a5126989d h1:Og/CXvf7cNq+GFpIe3Tjll500qD8L8wunTXC6bQRUxI= +github.com/Mrs4s/MiraiGo v0.0.0-20220220152607-5e8a5126989d/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 h1:8CK7Hg+CRGTFhpjvp5V+7wd8/TkuZ6fSuztLVV3bwoQ= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= @@ -27,6 +27,7 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -89,6 +90,7 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -121,6 +123,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -143,8 +146,10 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -170,6 +175,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From b833193926a8ab98bccbe6f7898585011d0d6d3e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 20 Feb 2022 15:37:25 +0000 Subject: [PATCH 306/612] ci(chore): Fix stylings --- internal/param/param.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/param/param.go b/internal/param/param.go index c38bf1cb2..99e0e7660 100644 --- a/internal/param/param.go +++ b/internal/param/param.go @@ -30,7 +30,7 @@ func EnsureBool(p interface{}, defaultVal bool) bool { if !j.Exists() { return defaultVal } - switch j.Type { // nolint + switch j.Type { case gjson.True: return true case gjson.False: From 19230b15113ae08e2fc26fdcde8d4379fee0555c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 21 Feb 2022 17:49:02 +0800 Subject: [PATCH 307/612] coolq: move MessageSource to MiraiGo/message --- coolq/api.go | 40 ++++++++++++++++---------------- coolq/bot.go | 43 ++++++++++++++++------------------- coolq/converter.go | 6 ++--- coolq/cqcode.go | 54 ++++++++++++++------------------------------ coolq/cqcode_test.go | 7 +++--- coolq/event.go | 32 +++++++++++++------------- go.mod | 2 +- go.sum | 4 ++-- 8 files changed, 82 insertions(+), 106 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index b9cf35273..35a5d1d81 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -712,7 +712,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b var elem []message.IMessageElement if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, MessageSourceGroup) + elem = bot.ConvertObjectMessage(m, message.SourceGroup) } else { str := m.String() if str == "" { @@ -722,7 +722,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, MessageSourceGroup) + elem = bot.ConvertStringMessage(str, message.SourceGroup) } } fixAt(elem) @@ -766,7 +766,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R var elem []message.IMessageElement if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, MessageSourceGuildChannel) + elem = bot.ConvertObjectMessage(m, message.SourceGuildChannel) } else { str := m.String() if str == "" { @@ -776,7 +776,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, MessageSourceGuildChannel) + elem = bot.ConvertStringMessage(str, message.SourceGuildChannel) } } fixAt(elem) @@ -833,7 +833,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F } return int32(msgTime) }(), - Message: resolveElement(bot.ConvertContentMessage(m.Content, MessageSourceGroup)), + Message: resolveElement(bot.ConvertContentMessage(m.Content, message.SourceGroup)), } } log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str) @@ -865,7 +865,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F } } } - content := bot.ConvertObjectMessage(c, MessageSourceGroup) + content := bot.ConvertObjectMessage(c, message.SourceGroup) if uin != 0 && name != "" && len(content) > 0 { return &message.ForwardNode{ SenderId: uin, @@ -932,7 +932,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) global.MSG { var elem []message.IMessageElement if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, MessageSourcePrivate) + elem = bot.ConvertObjectMessage(m, message.SourcePrivate) } else { str := m.String() if str == "" { @@ -941,7 +941,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Resu if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, MessageSourcePrivate) + elem = bot.ConvertStringMessage(str, message.SourcePrivate) } } mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem}) @@ -1525,7 +1525,7 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG { r := make([]global.MSG, len(nodes)) for i, n := range nodes { bot.checkMedia(n.Message, 0) - content := ToFormattedMessage(n.Message, MessageSource{SourceType: MessageSourceGroup}, false) + content := ToFormattedMessage(n.Message, message.Source{SourceType: message.SourceGroup}, false) if len(n.Message) == 1 { if forward, ok := n.Message[0].(*message.ForwardMessage); ok { content = transformNodes(forward.Nodes) @@ -1573,9 +1573,9 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { switch o := msg.(type) { case *db.StoredGroupMessage: m["group_id"] = o.GroupCode - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, MessageSourceGroup), MessageSource{SourceType: MessageSourceGroup, PrimaryID: uint64(o.GroupCode)}, false) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourceGroup), message.Source{SourceType: message.SourceGroup, PrimaryID: o.GroupCode}, false) case *db.StoredPrivateMessage: - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, MessageSourcePrivate), MessageSource{SourceType: MessageSourcePrivate}, false) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourcePrivate), message.Source{SourceType: message.SourcePrivate}, false) } return OK(m) } @@ -1591,21 +1591,21 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { m := global.MSG{ "message_id": messageID, "message_source": func() string { - if source.SourceType == MessageSourceGuildDirect { + if source.SourceType == message.SourceGuildDirect { return "direct" } return "channel" }(), "message_seq": seq, - "guild_id": fU64(source.PrimaryID), + "guild_id": fU64(uint64(source.PrimaryID)), "reactions": []int{}, } // nolint: exhaustive switch source.SourceType { - case MessageSourceGuildChannel: - m["channel_id"] = fU64(source.SubID) + case message.SourceGuildChannel: + m["channel_id"] = fU64(uint64(source.SecondaryID)) if noCache { - pull, err := bot.Client.GuildService.PullGuildChannelMessage(source.PrimaryID, source.SubID, seq, seq) + pull, err := bot.Client.GuildService.PullGuildChannelMessage(uint64(source.PrimaryID), uint64(source.SecondaryID), seq, seq) if err != nil { log.Warnf("获取消息时出现错误: %v", err) return Failed(100, "API_ERROR", err.Error()) @@ -1635,11 +1635,11 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "tiny_id": fU64(channelMsgByDB.Attribute.SenderTinyID), "nickname": channelMsgByDB.Attribute.SenderName, } - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, MessageSourceGuildChannel), *source) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), *source) } - case MessageSourceGuildDirect: + case message.SourceGuildDirect: // todo(mrs4s): 支持 direct 消息 - m["tiny_id"] = fU64(source.SubID) + m["tiny_id"] = fU64(uint64(source.SecondaryID)) } return OK(m) } @@ -1737,7 +1737,7 @@ func (bot *CQBot) CQCanSendRecord() global.MSG { // @route(ocr_image,".ocr_image") // @rename(image_id->image) func (bot *CQBot) CQOcrImage(imageID string) global.MSG { - img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, MessageSourceGroup) + img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, message.SourceGroup) if err != nil { log.Warnf("load image error: %v", err) return Failed(100, "LOAD_FILE_ERROR", err.Error()) diff --git a/coolq/bot.go b/coolq/bot.go index 950f63f4c..931c99b40 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io" "os" "runtime/debug" "sync" @@ -152,13 +151,13 @@ func (bot *CQBot) UploadLocalImageAsGroup(groupCode int64, img *LocalImageElemen } // UploadLocalVideo 上传本地短视频至群聊 -func (bot *CQBot) UploadLocalVideo(target int64, v *LocalVideoElement) (*message.ShortVideoElement, error) { +func (bot *CQBot) UploadLocalVideo(target message.Source, v *LocalVideoElement) (*message.ShortVideoElement, error) { video, err := os.Open(v.File) if err != nil { return nil, err } defer func() { _ = video.Close() }() - return bot.Client.UploadGroupShortVideo(target, video, v.thumb, 4) + return bot.Client.UploadShortVideo(target, video, v.thumb, 4) } // UploadLocalImageAsPrivate 上传本地图片至私聊 @@ -197,18 +196,6 @@ func (bot *CQBot) UploadLocalImageAsGuildChannel(guildID, channelID uint64, img return bot.Client.GuildService.UploadGuildImage(guildID, channelID, img.Stream) } -func (bot *CQBot) uploadGuildVideo(i *LocalVideoElement, guildID, channelID uint64) (*message.ShortVideoElement, error) { - video, err := os.Open(i.File) - if err != nil { - return nil, err - } - defer func() { _ = video.Close() }() - _, _ = video.Seek(0, io.SeekStart) - _, _ = i.thumb.Seek(0, io.SeekStart) - n, err := bot.Client.UploadGuildShortVideo(guildID, channelID, video, i.thumb) - return n, err -} - // SendGroupMessage 发送群消息 func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int32 { newElem := make([]message.IMessageElement, 0, len(m.Elements)) @@ -368,7 +355,11 @@ func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message. e = n case *LocalVideoElement: - n, err := bot.uploadGuildVideo(i, guildID, channelID) + n, err := bot.UploadLocalVideo(message.Source{ + SourceType: message.SourceGuildChannel, + PrimaryID: int64(guildID), + SecondaryID: int64(channelID), + }, i) if err != nil { log.Warnf("警告: 频道 %d 消息%s上传失败: %v", channelID, e.Type().String(), err) continue @@ -518,7 +509,7 @@ func (bot *CQBot) InsertTempMessage(target int64, m *message.TempMessage) int32 // InsertGuildChannelMessage 频道消息入数据库 func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) string { - id := encodeGuildMessageID(m.GuildId, m.ChannelId, m.Id, MessageSourceGuildChannel) + id := encodeGuildMessageID(m.GuildId, m.ChannelId, m.Id, message.SourceGuildChannel) msg := &db.StoredGuildChannelMessage{ ID: id, Attribute: &db.StoredGuildMessageAttribute{ @@ -597,7 +588,11 @@ func (bot *CQBot) uploadMedia(raw message.IMessageElement, target int64, group b } return bot.Client.UploadPrivatePtt(target, bytes.NewReader(m.Data)) case *LocalVideoElement: - return bot.UploadLocalVideo(target, m) + source := message.Source{ + SourceType: message.SourceGroup, + PrimaryID: target, + } + return bot.UploadLocalVideo(source, m) } return nil, errors.New("unsupported message element type") } @@ -613,7 +608,7 @@ func encodeMessageID(target int64, seq int32) string { // encodeGuildMessageID 将频道信息编码为字符串 // 当信息来源为 Channel 时 primaryID 为 guildID , subID 为 channelID // 当信息来源为 Direct 时 primaryID 为 guildID , subID 为 tinyID -func encodeGuildMessageID(primaryID, subID, seq uint64, source MessageSourceType) string { +func encodeGuildMessageID(primaryID, subID, seq uint64, source message.SourceType) string { return base64.StdEncoding.EncodeToString(binary.NewWriterF(func(w *binary.Writer) { w.WriteByte(byte(source)) w.WriteUInt64(primaryID) @@ -622,16 +617,16 @@ func encodeGuildMessageID(primaryID, subID, seq uint64, source MessageSourceType })) } -func decodeGuildMessageID(id string) (source *MessageSource, seq uint64) { +func decodeGuildMessageID(id string) (source *message.Source, seq uint64) { b, _ := base64.StdEncoding.DecodeString(id) if len(b) < 25 { return } r := binary.NewReader(b) - source = &MessageSource{ - SourceType: MessageSourceType(r.ReadByte()), - PrimaryID: uint64(r.ReadInt64()), - SubID: uint64(r.ReadInt64()), + source = &message.Source{ + SourceType: message.SourceType(r.ReadByte()), + PrimaryID: r.ReadInt64(), + SecondaryID: r.ReadInt64(), } seq = uint64(r.ReadInt64()) return diff --git a/coolq/converter.go b/coolq/converter.go index 154f3540a..fbe3d4c5b 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -60,9 +60,9 @@ func convertGuildMemberInfo(m []*client.GuildMemberInfo) (r []global.MSG) { } func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { - source := MessageSource{ - SourceType: MessageSourceGroup, - PrimaryID: uint64(m.GroupCode), + source := message.Source{ + SourceType: message.SourceGroup, + PrimaryID: m.GroupCode, } cqm := ToStringMessage(m.Elements, source, true) postType := "message" diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 1aa9b94db..5434d8df3 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -63,26 +63,6 @@ type LocalVideoElement struct { thumb io.ReadSeeker } -// MessageSource 消息来源 -// 如果为私聊或者群聊, PrimaryID 将代表群号/QQ号 -// 如果为频道, PrimaryID 为 GuildID, SubID 为 ChannelID -type MessageSource struct { - SourceType MessageSourceType - PrimaryID uint64 - SubID uint64 -} - -// MessageSourceType 消息来源类型 -type MessageSourceType byte - -// MessageSourceType 常量 -const ( - MessageSourcePrivate MessageSourceType = 1 << iota - MessageSourceGroup - MessageSourceGuildChannel - MessageSourceGuildDirect -) - const ( maxImageSize = 1024 * 1024 * 30 // 30MB maxVideoSize = 1024 * 1024 * 100 // 100MB @@ -104,10 +84,10 @@ func (e *PokeElement) Type() message.ElementType { return message.At } -func replyID(r *message.ReplyElement, source MessageSource) int32 { +func replyID(r *message.ReplyElement, source message.Source) int32 { id := int64(source.PrimaryID) seq := r.ReplySeq - if source.SourceType == MessageSourcePrivate { + if source.SourceType == message.SourcePrivate { // 私聊似乎腾讯服务器有bug? seq = int32(uint16(seq)) id = r.Sender @@ -119,14 +99,14 @@ func replyID(r *message.ReplyElement, source MessageSource) int32 { } // ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报 -func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []global.MSG) { +func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []global.MSG) { r = make([]global.MSG, 0, len(e)) m := &message.SendingMessage{Elements: e} reply := m.FirstOrNil(func(e message.IMessageElement) bool { _, ok := e.(*message.ReplyElement) return ok }) - if reply != nil && source.SourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { + if reply != nil && source.SourceType&(message.SourceGroup|message.SourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) id := replyID(replyElem, source) if base.ExtraReplyData { @@ -270,7 +250,7 @@ func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []glob } // ToStringMessage 将消息元素数组转为字符串以用于消息上报 -func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw ...bool) (r string) { +func ToStringMessage(e []message.IMessageElement, source message.Source, isRaw ...bool) (r string) { sb := global.NewBuffer() sb.Reset() write := func(format string, a ...interface{}) { @@ -286,7 +266,7 @@ func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw .. _, ok := e.(*message.ReplyElement) return ok }) - if reply != nil && source.SourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { + if reply != nil && source.SourceType&(message.SourceGroup|message.SourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) id := replyID(replyElem, source) if base.ExtraReplyData { @@ -511,7 +491,7 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { } // ConvertStringMessage 将消息字符串转为消息元素数组 -func (bot *CQBot) ConvertStringMessage(raw string, sourceType MessageSourceType) (r []message.IMessageElement) { +func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType) (r []message.IMessageElement) { var t, key string d := map[string]string{} @@ -684,11 +664,11 @@ func (bot *CQBot) ConvertStringMessage(raw string, sourceType MessageSourceType) } // ConvertObjectMessage 将消息JSON对象转为消息元素数组 -func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType MessageSourceType) (r []message.IMessageElement) { +func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType message.SourceType) (r []message.IMessageElement) { d := make(map[string]string) convertElem := func(e gjson.Result) { t := e.Get("type").Str - if t == "reply" && sourceType&(MessageSourceGroup|MessageSourcePrivate) != 0 { + if t == "reply" && sourceType&(message.SourceGroup|message.SourcePrivate) != 0 { if len(r) > 0 { if _, ok := r[0].(*message.ReplyElement); ok { log.Warnf("警告: 一条信息只能包含一个 Reply 元素.") @@ -804,7 +784,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType MessageSourceT } // ConvertContentMessage 将数据库用的 content 转换为消息元素数组 -func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType MessageSourceType) (r []message.IMessageElement) { +func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message.SourceType) (r []message.IMessageElement) { for _, c := range content { data := c["data"].(global.MSG) switch c["type"] { @@ -883,7 +863,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType Message // 返回 interface{} 存在三种类型 // // message.IMessageElement []message.IMessageElement nil -func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSourceType) (m interface{}, err error) { +func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.SourceType) (m interface{}, err error) { switch t { case "text": if base.SplitURL { @@ -1117,7 +1097,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou if err != nil { return nil, errors.New("send cardimage faild") } - return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, sourceType == MessageSourceGroup) + return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, sourceType == message.SourceGroup) case "video": file, err := bot.makeImageOrVideoElem(d, true, sourceType) if err != nil { @@ -1170,7 +1150,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSou } // makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用 -func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceType MessageSourceType) (message.IMessageElement, error) { +func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceType message.SourceType) (message.IMessageElement, error) { f := d["file"] u, ok := d["url"] if !ok { @@ -1254,7 +1234,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return bot.readVideoCache(b), nil } // 目前频道内上传的图片均无法被查询到, 需要单独处理 - if sourceType == MessageSourceGuildChannel { + if sourceType == message.SourceGuildChannel { cacheFile := path.Join(global.ImagePath, "guild-images", f) if global.PathExists(cacheFile) { return &LocalImageElement{File: cacheFile}, nil @@ -1289,7 +1269,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return bot.readImageCache(b, sourceType) } -func (bot *CQBot) readImageCache(b []byte, sourceType MessageSourceType) (message.IMessageElement, error) { +func (bot *CQBot) readImageCache(b []byte, sourceType message.SourceType) (message.IMessageElement, error) { var err error if len(b) < 20 { return nil, errors.New("invalid cache") @@ -1304,9 +1284,9 @@ func (bot *CQBot) readImageCache(b []byte, sourceType MessageSourceType) (messag } var rsp message.IMessageElement switch sourceType { // nolint:exhaustive - case MessageSourceGroup: + case message.SourceGroup: rsp, err = bot.Client.QueryGroupImage(int64(rand.Uint32()), hash, size) - case MessageSourceGuildChannel: + case message.SourceGuildChannel: if len(bot.Client.GuildService.Guilds) == 0 { err = errors.New("cannot query guild image: not any joined guild") break diff --git a/coolq/cqcode_test.go b/coolq/cqcode_test.go index 0c8bf7ed0..30c163692 100644 --- a/coolq/cqcode_test.go +++ b/coolq/cqcode_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" "github.com/stretchr/testify/assert" "github.com/tidwall/gjson" @@ -15,7 +16,7 @@ import ( var bot = CQBot{} func TestCQBot_ConvertStringMessage(t *testing.T) { - for _, v := range bot.ConvertStringMessage(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`, MessageSourcePrivate) { + for _, v := range bot.ConvertStringMessage(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`, message.SourcePrivate) { fmt.Println(v) } } @@ -27,14 +28,14 @@ var ( func BenchmarkCQBot_ConvertStringMessage(b *testing.B) { for i := 0; i < b.N; i++ { - bot.ConvertStringMessage(bench, MessageSourcePrivate) + bot.ConvertStringMessage(bench, message.SourcePrivate) } b.SetBytes(int64(len(bench))) } func BenchmarkCQBot_ConvertObjectMessage(b *testing.B) { for i := 0; i < b.N; i++ { - bot.ConvertObjectMessage(benchArray, MessageSourcePrivate) + bot.ConvertObjectMessage(benchArray, message.SourcePrivate) } } diff --git a/coolq/event.go b/coolq/event.go index d89707dbe..fd2f9413c 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -21,7 +21,7 @@ import ( ) // ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式 -func ToFormattedMessage(e []message.IMessageElement, source MessageSource, isRaw ...bool) (r interface{}) { +func ToFormattedMessage(e []message.IMessageElement, source message.Source, isRaw ...bool) (r interface{}) { if base.PostFormat == "string" { r = ToStringMessage(e, source, isRaw...) } else if base.PostFormat == "array" { @@ -32,9 +32,9 @@ func ToFormattedMessage(e []message.IMessageElement, source MessageSource, isRaw func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) { bot.checkMedia(m.Elements, m.Sender.Uin) - source := MessageSource{ - SourceType: MessageSourcePrivate, - PrimaryID: uint64(m.Sender.Uin), + source := message.Source{ + SourceType: message.SourcePrivate, + PrimaryID: m.Sender.Uin, } cqm := ToStringMessage(m.Elements, source, true) id := bot.InsertPrivateMessage(m) @@ -89,9 +89,9 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) return } } - source := MessageSource{ - SourceType: MessageSourceGroup, - PrimaryID: uint64(m.GroupCode), + source := message.Source{ + SourceType: message.SourceGroup, + PrimaryID: m.GroupCode, } cqm := ToStringMessage(m.Elements, source, true) id := bot.InsertGroupMessage(m) @@ -107,9 +107,9 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) { m := e.Message bot.checkMedia(m.Elements, m.Sender.Uin) - source := MessageSource{ - SourceType: MessageSourcePrivate, - PrimaryID: uint64(e.Session.Sender), + source := message.Source{ + SourceType: message.SourcePrivate, + PrimaryID: e.Session.Sender, } cqm := ToStringMessage(m.Elements, source, true) bot.tempSessionCache.Store(m.Sender.Uin, e.Session) @@ -149,10 +149,10 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC return } channel := guild.FindChannel(m.ChannelId) - source := MessageSource{ - SourceType: MessageSourceGuildChannel, - PrimaryID: m.GuildId, - SubID: m.ChannelId, + source := message.Source{ + SourceType: message.SourceGuildChannel, + PrimaryID: int64(m.GuildId), + SecondaryID: int64(m.ChannelId), } log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, ToStringMessage(m.Elements, source, true)) id := bot.InsertGuildChannelMessage(m) @@ -181,7 +181,7 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien if guild == nil { return } - msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId, MessageSourceGuildChannel) + msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId, message.SourceGuildChannel) str := fmt.Sprintf("频道 %v(%v) 消息 %v 表情贴片已更新: ", guild.GuildName, guild.GuildId, msgID) currentReactions := make([]global.MSG, len(e.CurrentReactions)) for i, r := range e.CurrentReactions { @@ -228,7 +228,7 @@ func (bot *CQBot) guildChannelMessageRecalledEvent(c *client.QQClient, e *client log.Errorf("处理频道撤回事件时出现错误: 获取操作者资料时出现错误 %v", err) return } - msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId, MessageSourceGuildChannel) + msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId, message.SourceGuildChannel) log.Infof("用户 %v(%v) 撤回了频道 %v(%v) 子频道 %v(%v) 的消息 %v", operator.Nickname, operator.TinyId, guild.GuildName, guild.GuildId, channel.ChannelName, channel.ChannelId, msgID) bot.dispatchEventMessage(global.MSG{ "post_type": "notice", diff --git a/go.mod b/go.mod index 4f70cc18d..2ee23ee42 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220220152607-5e8a5126989d + github.com/Mrs4s/MiraiGo v0.0.0-20220221093105-1e32793eef06 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 8ffb3232b..f8c754d9a 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220220152607-5e8a5126989d h1:Og/CXvf7cNq+GFpIe3Tjll500qD8L8wunTXC6bQRUxI= -github.com/Mrs4s/MiraiGo v0.0.0-20220220152607-5e8a5126989d/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= +github.com/Mrs4s/MiraiGo v0.0.0-20220221093105-1e32793eef06 h1:9V7AhNUPJ1vqLAoXjwLdpv06hJWs15UdgYbKaHww60s= +github.com/Mrs4s/MiraiGo v0.0.0-20220221093105-1e32793eef06/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 h1:8CK7Hg+CRGTFhpjvp5V+7wd8/TkuZ6fSuztLVV3bwoQ= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From c7f0aed1b771a458e868af68150cbafc1ad9a6df Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 21 Feb 2022 23:44:49 +0800 Subject: [PATCH 308/612] coolq: adapt new MiraiGo upload api --- coolq/api.go | 16 ++++- coolq/bot.go | 148 +++++++++++++++------------------------- coolq/cqcode.go | 16 ++--- go.mod | 2 +- go.sum | 4 +- internal/param/param.go | 2 +- 6 files changed, 80 insertions(+), 108 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 35a5d1d81..bcb7830ac 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -791,6 +791,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.ForwardElement { ts := time.Now().Add(-time.Minute * 5) fm := message.NewForwardMessage() + source := message.Source{SourceType: message.SourceGroup, PrimaryID: groupID} var lazyUpload []func() var wg sync.WaitGroup @@ -798,11 +799,22 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F for i, elem := range elems { iescape := i switch o := elem.(type) { - case *LocalImageElement, *LocalVideoElement: + case *LocalVideoElement: wg.Add(1) lazyUpload = append(lazyUpload, func() { defer wg.Done() - gm, err := bot.uploadMedia(o, groupID, true) + gm, err := bot.uploadLocalVideo(source, o) + if err != nil { + log.Warnf("警告: 群 %d %s上传失败: %v", groupID, o.Type().String(), err) + } else { + elems[iescape] = gm + } + }) + case *LocalImageElement: + wg.Add(1) + lazyUpload = append(lazyUpload, func() { + defer wg.Done() + gm, err := bot.uploadLocalImage(source, o) if err != nil { log.Warnf("警告: 群 %d %s上传失败: %v", groupID, o.Type().String(), err) } else { diff --git a/coolq/bot.go b/coolq/bot.go index 931c99b40..00a53c8c0 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -129,8 +129,8 @@ func (bot *CQBot) OnEventPush(f func(e *Event)) { bot.lock.Unlock() } -// UploadLocalImageAsGroup 上传本地图片至群聊 -func (bot *CQBot) UploadLocalImageAsGroup(groupCode int64, img *LocalImageElement) (i *message.GroupImageElement, err error) { +// uploadLocalImage 上传本地图片 +func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement) (i message.IMessageElement, err error) { if img.File != "" { f, err := os.Open(img.File) if err != nil { @@ -142,16 +142,19 @@ func (bot *CQBot) UploadLocalImageAsGroup(groupCode int64, img *LocalImageElemen if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { return nil, errors.New("image type error: " + mime) } - i, err = bot.Client.UploadGroupImage(groupCode, img.Stream, 4) - if i != nil { + i, err = bot.Client.UploadImage(target, img.Stream, 4) + switch i := i.(type) { + case *message.GroupImageElement: i.Flash = img.Flash i.EffectID = img.EffectID + case *message.FriendImageElement: + i.Flash = img.Flash } return } -// UploadLocalVideo 上传本地短视频至群聊 -func (bot *CQBot) UploadLocalVideo(target message.Source, v *LocalVideoElement) (*message.ShortVideoElement, error) { +// uploadLocalVideo 上传本地短视频至群聊 +func (bot *CQBot) uploadLocalVideo(target message.Source, v *LocalVideoElement) (*message.ShortVideoElement, error) { video, err := os.Open(v.File) if err != nil { return nil, err @@ -160,55 +163,52 @@ func (bot *CQBot) UploadLocalVideo(target message.Source, v *LocalVideoElement) return bot.Client.UploadShortVideo(target, video, v.thumb, 4) } -// UploadLocalImageAsPrivate 上传本地图片至私聊 -func (bot *CQBot) UploadLocalImageAsPrivate(userID int64, img *LocalImageElement) (i *message.FriendImageElement, err error) { - if img.File != "" { - f, err := os.Open(img.File) - if err != nil { - return nil, errors.Wrap(err, "open image error") +func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessageElement) []message.IMessageElement { + var j int + for _, m := range elements { + raw := m // upload failed will make m nil, so copy it + var err error + switch e := m.(type) { + case *LocalImageElement: + m, err = bot.uploadLocalImage(target, e) + case *message.VoiceElement: + if target.SourceType == message.SourceGuildChannel { + continue // todo + } + m, err = bot.Client.UploadVoice(target, bytes.NewReader(e.Data)) + case *LocalVideoElement: + m, err = bot.uploadLocalVideo(target, e) } - defer func() { _ = f.Close() }() - img.Stream = f - } - if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { - return nil, errors.New("image type error: " + mime) - } - i, err = bot.Client.UploadPrivateImage(userID, img.Stream) - if i != nil { - i.Flash = img.Flash - } - return -} - -// UploadLocalImageAsGuildChannel 上传本地图片至频道 -func (bot *CQBot) UploadLocalImageAsGuildChannel(guildID, channelID uint64, img *LocalImageElement) (*message.GuildImageElement, error) { - if img.File != "" { - f, err := os.Open(img.File) if err != nil { - return nil, errors.Wrap(err, "open image error") + var source string + switch target.SourceType { // nolint:exhaustive + case message.SourceGroup: + source = "群" + case message.SourcePrivate: + source = "私聊" + case message.SourceGuildChannel: + source = "频道" + } + log.Warnf("警告: %s %d %s上传失败: %v", source, target.PrimaryID, raw.Type().String(), err) + continue } - defer func() { _ = f.Close() }() - img.Stream = f - } - if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { - return nil, errors.New("image type error: " + mime) + elements[j] = m + j++ } - return bot.Client.GuildService.UploadGuildImage(guildID, channelID, img.Stream) + return elements[:j] } // SendGroupMessage 发送群消息 func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int32 { newElem := make([]message.IMessageElement, 0, len(m.Elements)) group := bot.Client.FindGroup(groupID) + source := message.Source{ + SourceType: message.SourceGroup, + PrimaryID: groupID, + } + m.Elements = bot.uploadMedia(source, m.Elements) for _, e := range m.Elements { switch i := e.(type) { - case *LocalImageElement, *message.VoiceElement, *LocalVideoElement: - i, err := bot.uploadMedia(i, groupID, true) - if err != nil { - log.Warnf("警告: 群 %d 消息%s上传失败: %v", groupID, e.Type().String(), err) - continue - } - e = i case *PokeElement: if group != nil { if mem := group.FindMember(i.Target); mem != nil { @@ -247,15 +247,13 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int // SendPrivateMessage 发送私聊消息 func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.SendingMessage) int32 { newElem := make([]message.IMessageElement, 0, len(m.Elements)) + source := message.Source{ + SourceType: message.SourcePrivate, + PrimaryID: target, + } + m.Elements = bot.uploadMedia(source, m.Elements) for _, e := range m.Elements { switch i := e.(type) { - case *LocalImageElement, *message.VoiceElement, *LocalVideoElement: - i, err := bot.uploadMedia(i, target, false) - if err != nil { - log.Warnf("警告: 私聊 %d 消息%s上传失败: %v", target, e.Type().String(), err) - continue - } - e = i case *PokeElement: bot.Client.SendFriendPoke(i.Target) return 0 @@ -344,33 +342,19 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen // SendGuildChannelMessage 发送频道消息 func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message.SendingMessage) string { newElem := make([]message.IMessageElement, 0, len(m.Elements)) + source := message.Source{ + SourceType: message.SourceGuildChannel, + PrimaryID: int64(guildID), + SecondaryID: int64(channelID), + } + m.Elements = bot.uploadMedia(source, m.Elements) for _, e := range m.Elements { switch i := e.(type) { - case *LocalImageElement: - n, err := bot.UploadLocalImageAsGuildChannel(guildID, channelID, i) - if err != nil { - log.Warnf("警告: 频道 %d 消息%s上传失败: %v", channelID, e.Type().String(), err) - continue - } - e = n - - case *LocalVideoElement: - n, err := bot.UploadLocalVideo(message.Source{ - SourceType: message.SourceGuildChannel, - PrimaryID: int64(guildID), - SecondaryID: int64(channelID), - }, i) - if err != nil { - log.Warnf("警告: 频道 %d 消息%s上传失败: %v", channelID, e.Type().String(), err) - continue - } - e = n - case *message.MusicShareElement: bot.Client.SendGuildMusicShare(guildID, channelID, i) return "-1" // todo: fix this - case *LocalVoiceElement, *PokeElement: + case *message.VoiceElement, *PokeElement: log.Warnf("警告: 频道暂不支持发送 %v 消息", i.Type().String()) continue } @@ -575,28 +559,6 @@ func formatMemberName(mem *client.GroupMemberInfo) string { return fmt.Sprintf("%s(%d)", mem.DisplayName(), mem.Uin) } -func (bot *CQBot) uploadMedia(raw message.IMessageElement, target int64, group bool) (message.IMessageElement, error) { - switch m := raw.(type) { - case *LocalImageElement: - if group { - return bot.UploadLocalImageAsGroup(target, m) - } - return bot.UploadLocalImageAsPrivate(target, m) - case *message.VoiceElement: - if group { - return bot.Client.UploadGroupPtt(target, bytes.NewReader(m.Data)) - } - return bot.Client.UploadPrivatePtt(target, bytes.NewReader(m.Data)) - case *LocalVideoElement: - source := message.Source{ - SourceType: message.SourceGroup, - PrimaryID: target, - } - return bot.UploadLocalVideo(source, m) - } - return nil, errors.New("unsupported message element type") -} - // encodeMessageID 临时先这样, 暂时用不上 func encodeMessageID(target int64, seq int32) string { return hex.EncodeToString(binary.NewWriterF(func(w *binary.Writer) { diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 5434d8df3..48c1cf131 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -51,12 +51,6 @@ type LocalImageElement struct { EffectID int32 } -// LocalVoiceElement 本地语音 -type LocalVoiceElement struct { - message.VoiceElement - Stream io.ReadSeeker -} - // LocalVideoElement 本地视频 type LocalVideoElement struct { File string @@ -85,7 +79,7 @@ func (e *PokeElement) Type() message.ElementType { } func replyID(r *message.ReplyElement, source message.Source) int32 { - id := int64(source.PrimaryID) + id := source.PrimaryID seq := r.ReplySeq if source.SourceType == message.SourcePrivate { // 私聊似乎腾讯服务器有bug? @@ -1321,9 +1315,13 @@ func (bot *CQBot) makeShowPic(elem message.IMessageElement, source string, brief if brief == "" { brief = "[分享]我看到一张很赞的图片,分享给你,快来看!" } - if _, ok := elem.(*LocalImageElement); ok { + if local, ok := elem.(*LocalImageElement); ok { r := rand.Uint32() - e, err := bot.uploadMedia(elem, int64(r), group) + typ := message.SourceGroup + if !group { + typ = message.SourcePrivate + } + e, err := bot.uploadLocalImage(message.Source{SourceType: typ, PrimaryID: int64(r)}, local) if err != nil { log.Warnf("警告: 图片上传失败: %v", err) return nil, err diff --git a/go.mod b/go.mod index 2ee23ee42..d4a5d5978 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220221093105-1e32793eef06 + github.com/Mrs4s/MiraiGo v0.0.0-20220221153544-6b5e7d35f04d github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index f8c754d9a..780331ee5 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220221093105-1e32793eef06 h1:9V7AhNUPJ1vqLAoXjwLdpv06hJWs15UdgYbKaHww60s= -github.com/Mrs4s/MiraiGo v0.0.0-20220221093105-1e32793eef06/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= +github.com/Mrs4s/MiraiGo v0.0.0-20220221153544-6b5e7d35f04d h1:qnots7MzDtJHOvHPRG60gehAw2pUPBGa0GKBsUQNjRM= +github.com/Mrs4s/MiraiGo v0.0.0-20220221153544-6b5e7d35f04d/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 h1:8CK7Hg+CRGTFhpjvp5V+7wd8/TkuZ6fSuztLVV3bwoQ= github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= diff --git a/internal/param/param.go b/internal/param/param.go index 99e0e7660..fd86b7462 100644 --- a/internal/param/param.go +++ b/internal/param/param.go @@ -30,7 +30,7 @@ func EnsureBool(p interface{}, defaultVal bool) bool { if !j.Exists() { return defaultVal } - switch j.Type { + switch j.Type { // nolint: exhaustive case gjson.True: return true case gjson.False: From d1f143ebf7601c04a630481db818887d436ee371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=83=E6=A9=98=20=E9=9B=AB=E9=9C=9E?= Date: Tue, 22 Feb 2022 13:38:42 +0800 Subject: [PATCH 309/612] feat: set qq profile (#1389) * feat: set qq profile * refactor: support empty field --- coolq/api.go | 21 +++++++++++++++++++++ modules/api/api.go | 7 +++++++ 2 files changed, 28 insertions(+) diff --git a/coolq/api.go b/coolq/api.go index bcb7830ac..621a666fa 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1981,6 +1981,27 @@ func (bot *CQBot) CQMarkMessageAsRead(msgID int32) global.MSG { return OK(nil) } +// CQSetQQProfile 设置 QQ 资料 +// +// @route(set_qq_profile) +func (bot *CQBot) CQSetQQProfile(nickname, company, email, college, personalNote gjson.Result) global.MSG { + u := client.NewProfileDetailUpdate() + + fi := func(f gjson.Result, do func(value string) client.ProfileDetailUpdate) { + if f.Exists() { + do(f.String()) + } + } + + fi(nickname, u.Nick) + fi(company, u.Company) + fi(email, u.Email) + fi(college, u.College) + fi(personalNote, u.PersonalNote) + bot.Client.UpdateProfile(u) + return OK(nil) +} + // CQReloadEventFilter 重载事件过滤器 // // @route(reload_event_filter) diff --git a/modules/api/api.go b/modules/api/api.go index 5ece47689..e936269ac 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -304,6 +304,13 @@ func (c *Caller) call(action string, p Getter) global.MSG { p2 := p.Get("role_id").Uint() p3 := p.Get("users") return c.bot.CQSetGuildMemberRole(p0, p1, p2, p3) + case "set_qq_profile": + p0 := p.Get("nickname") + p1 := p.Get("company") + p2 := p.Get("email") + p3 := p.Get("college") + p4 := p.Get("personal_note") + return c.bot.CQSetQQProfile(p0, p1, p2, p3, p4) case "update_guild_role": p0 := p.Get("guild_id").Uint() p1 := p.Get("role_id").Uint() From 987daad785839a9cb0425a27edc90a0a695f5c84 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 22 Feb 2022 14:45:50 +0800 Subject: [PATCH 310/612] coolq: allow upload media concurrently for normal message --- coolq/api.go | 29 +++++---------- coolq/bot.go | 103 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 82 insertions(+), 50 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 621a666fa..6cbdde642 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -12,7 +12,6 @@ import ( "runtime" "strconv" "strings" - "sync" "time" "github.com/segmentio/asm/base64" @@ -793,32 +792,27 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F fm := message.NewForwardMessage() source := message.Source{SourceType: message.SourceGroup, PrimaryID: groupID} - var lazyUpload []func() - var wg sync.WaitGroup + var w worker resolveElement := func(elems []message.IMessageElement) []message.IMessageElement { for i, elem := range elems { - iescape := i + p := &elems[i] switch o := elem.(type) { case *LocalVideoElement: - wg.Add(1) - lazyUpload = append(lazyUpload, func() { - defer wg.Done() + w.do(func() { gm, err := bot.uploadLocalVideo(source, o) if err != nil { - log.Warnf("警告: 群 %d %s上传失败: %v", groupID, o.Type().String(), err) + log.Warnf(uploadFailedTemplate, "群", groupID, "视频", err) } else { - elems[iescape] = gm + *p = gm } }) case *LocalImageElement: - wg.Add(1) - lazyUpload = append(lazyUpload, func() { - defer wg.Done() + w.do(func() { gm, err := bot.uploadLocalImage(source, o) if err != nil { - log.Warnf("警告: 群 %d %s上传失败: %v", groupID, o.Type().String(), err) + log.Warnf(uploadFailedTemplate, "群", groupID, "图片", err) } else { - elems[iescape] = gm + *p = gm } }) } @@ -903,12 +897,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F fm.AddNode(node) } } - - for _, upload := range lazyUpload { - go upload() - } - wg.Wait() - + w.wait() return bot.Client.UploadGroupForwardMessage(groupID, fm) } diff --git a/coolq/bot.go b/coolq/bot.go index 00a53c8c0..529b5fd30 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -129,6 +129,22 @@ func (bot *CQBot) OnEventPush(f func(e *Event)) { bot.lock.Unlock() } +type worker struct { + wg sync.WaitGroup +} + +func (w *worker) do(f func()) { + w.wg.Add(1) + go func() { + defer w.wg.Done() + f() + }() +} + +func (w *worker) wait() { + w.wg.Wait() +} + // uploadLocalImage 上传本地图片 func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement) (i message.IMessageElement, err error) { if img.File != "" { @@ -163,39 +179,70 @@ func (bot *CQBot) uploadLocalVideo(target message.Source, v *LocalVideoElement) return bot.Client.UploadShortVideo(target, video, v.thumb, 4) } -func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessageElement) []message.IMessageElement { +func removeLocalElement(elements []message.IMessageElement) []message.IMessageElement { var j int - for _, m := range elements { - raw := m // upload failed will make m nil, so copy it - var err error + for i, e := range elements { + switch e.(type) { + case *LocalImageElement, *LocalVideoElement: + case *message.VoiceElement: // 未上传的语音消息, 也删除 + default: + if j < i { + elements[j] = e + } + j++ + } + } + return elements[:j] +} + +const uploadFailedTemplate = "警告: %s %d %s上传失败: %v" + +func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessageElement) []message.IMessageElement { + var w worker + var source string + switch target.SourceType { // nolint:exhaustive + case message.SourceGroup: + source = "群" + case message.SourcePrivate: + source = "私聊" + case message.SourceGuildChannel: + source = "频道" + } + + for i, m := range elements { + p := &elements[i] switch e := m.(type) { case *LocalImageElement: - m, err = bot.uploadLocalImage(target, e) + w.do(func() { + m, err := bot.uploadLocalImage(target, e) + if err != nil { + log.Warnf(uploadFailedTemplate, source, target.PrimaryID, "图片", err) + } else { + *p = m + } + }) case *message.VoiceElement: - if target.SourceType == message.SourceGuildChannel { - continue // todo - } - m, err = bot.Client.UploadVoice(target, bytes.NewReader(e.Data)) + w.do(func() { + m, err := bot.Client.UploadVoice(target, bytes.NewReader(e.Data)) + if err != nil { + log.Warnf(uploadFailedTemplate, source, target.PrimaryID, "语音", err) + } else { + *p = m + } + }) case *LocalVideoElement: - m, err = bot.uploadLocalVideo(target, e) - } - if err != nil { - var source string - switch target.SourceType { // nolint:exhaustive - case message.SourceGroup: - source = "群" - case message.SourcePrivate: - source = "私聊" - case message.SourceGuildChannel: - source = "频道" - } - log.Warnf("警告: %s %d %s上传失败: %v", source, target.PrimaryID, raw.Type().String(), err) - continue + w.do(func() { + m, err := bot.uploadLocalVideo(target, e) + if err != nil { + log.Warnf(uploadFailedTemplate, source, target.PrimaryID, "视频", err) + } else { + *p = m + } + }) } - elements[j] = m - j++ } - return elements[:j] + w.wait() + return removeLocalElement(elements) } // SendGroupMessage 发送群消息 @@ -514,10 +561,6 @@ func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) stri return msg.ID } -// Release 释放Bot实例 -func (bot *CQBot) Release() { -} - func (bot *CQBot) dispatchEventMessage(m global.MSG) { bot.lock.RLock() defer bot.lock.RUnlock() From dfcad8082bc1d81468ca0c332b22fe7744d90148 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 24 Feb 2022 01:11:27 +0800 Subject: [PATCH 311/612] feat: check reset working dir exists --- internal/base/flag.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/base/flag.go b/internal/base/flag.go index 27940538a..c6ff29746 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/Mrs4s/go-cqhttp/global" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" @@ -140,6 +141,9 @@ func ResetWorkingDir() { } } p, _ := filepath.Abs(os.Args[0]) + if !global.PathExists(p) { + log.Fatalf("重置工作目录时出现错误: 无法找到路径 %v", p) + } proc := exec.Command(p, args...) proc.Stdin = os.Stdin proc.Stdout = os.Stdout From d4c2b62e5e856e3c20fcf9e0797869bcdfda8022 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 24 Feb 2022 01:15:42 +0800 Subject: [PATCH 312/612] fix: import cycle --- internal/base/flag.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/base/flag.go b/internal/base/flag.go index c6ff29746..5e9bd7bbf 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/Mrs4s/go-cqhttp/global" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" @@ -141,7 +141,8 @@ func ResetWorkingDir() { } } p, _ := filepath.Abs(os.Args[0]) - if !global.PathExists(p) { + _, err := os.Stat(p) + if !(err == nil || errors.Is(err, os.ErrExist)) { log.Fatalf("重置工作目录时出现错误: 无法找到路径 %v", p) } proc := exec.Command(p, args...) @@ -149,7 +150,7 @@ func ResetWorkingDir() { proc.Stdout = os.Stdout proc.Stderr = os.Stderr proc.Dir = wd - err := proc.Run() + err = proc.Run() if err != nil { panic(err) } From 6687d226432ec32fd3c7e53a5c4c6579af916e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 24 Feb 2022 21:58:48 +0800 Subject: [PATCH 313/612] fix: uploadLocalImage SIGSEGV (#1392) * fix: uploadLocalImage SIGSEGV * fix: removeLocalElement nil ptr --- coolq/bot.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 529b5fd30..f3beca250 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -146,7 +146,7 @@ func (w *worker) wait() { } // uploadLocalImage 上传本地图片 -func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement) (i message.IMessageElement, err error) { +func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement) (message.IMessageElement, error) { if img.File != "" { f, err := os.Open(img.File) if err != nil { @@ -158,7 +158,10 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { return nil, errors.New("image type error: " + mime) } - i, err = bot.Client.UploadImage(target, img.Stream, 4) + i, err := bot.Client.UploadImage(target, img.Stream, 4) + if err != nil { + return nil, err + } switch i := i.(type) { case *message.GroupImageElement: i.Flash = img.Flash @@ -166,7 +169,7 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement case *message.FriendImageElement: i.Flash = img.Flash } - return + return i, err } // uploadLocalVideo 上传本地短视频至群聊 @@ -185,6 +188,7 @@ func removeLocalElement(elements []message.IMessageElement) []message.IMessageEl switch e.(type) { case *LocalImageElement, *LocalVideoElement: case *message.VoiceElement: // 未上传的语音消息, 也删除 + case nil: default: if j < i { elements[j] = e From 8d26e3aec4ce66d41ed422ef37ca407f6c92ba8d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 27 Feb 2022 21:59:00 +0800 Subject: [PATCH 314/612] dep: update MiraiGo --- go.mod | 4 ++-- go.sum | 15 ++++----------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index d4a5d5978..c22aa4e32 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220221153544-6b5e7d35f04d + github.com/Mrs4s/MiraiGo v0.0.0-20220227135520-9884d9b0de20 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 @@ -25,7 +25,7 @@ require ( ) require ( - github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 // indirect + github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect diff --git a/go.sum b/go.sum index 780331ee5..a0e74b2d8 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220221153544-6b5e7d35f04d h1:qnots7MzDtJHOvHPRG60gehAw2pUPBGa0GKBsUQNjRM= -github.com/Mrs4s/MiraiGo v0.0.0-20220221153544-6b5e7d35f04d/go.mod h1:T66Ua3SOfpJMx+DcfQxk95MeR8RmvAVmjYSkoQQ8nwI= -github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0 h1:8CK7Hg+CRGTFhpjvp5V+7wd8/TkuZ6fSuztLVV3bwoQ= -github.com/RomiChan/protobuf v0.0.0-20220213164748-44b69c8bdec0/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/Mrs4s/MiraiGo v0.0.0-20220227135520-9884d9b0de20 h1:ZbJOWQRQB64npjqGMc031/Ry3bKhtMvx7Rf4bx0t9xI= +github.com/Mrs4s/MiraiGo v0.0.0-20220227135520-9884d9b0de20/go.mod h1:23f5g1I7XT7mB3zq02B3rfuiDnE7PJYSfvb/skzDVhI= +github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 h1:1jRB6xuBKwfgZrg0bA7XJin0VeNwG9iJKx9RXwDobt4= +github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -27,7 +27,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -90,7 +89,6 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -123,7 +121,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -146,10 +143,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -175,8 +170,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From c00e07dec9663c378cc989314eaa0d37ff7ac992 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 1 Mar 2022 16:30:23 +0800 Subject: [PATCH 315/612] coolq: adapt generic event handle & update MiraiGo --- .github/workflows/ci.yml | 3 ++- cmd/gocq/main.go | 2 +- coolq/api.go | 2 +- coolq/bot.go | 53 ++++++++++++++++++++-------------------- go.mod | 4 +-- go.sum | 4 +-- 6 files changed, 35 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 801395151..5b36780c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,8 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v2.1.3 with: - go-version: 1.17 + stable: false + go-version: 1.18.0-rc1 - name: Cache downloaded module uses: actions/cache@v2 with: diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 2b5952c98..863074bb8 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -259,7 +259,7 @@ func Main() { } var times uint = 1 // 重试次数 var reLoginLock sync.Mutex - cli.OnDisconnected(func(q *client.QQClient, e *client.ClientDisconnectedEvent) { + cli.DisconnectedEvent.Subscribe(func(q *client.QQClient, e *client.ClientDisconnectedEvent) { reLoginLock.Lock() defer reLoginLock.Unlock() times = 1 diff --git a/coolq/api.go b/coolq/api.go index 6cbdde642..dba906f7f 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -156,7 +156,7 @@ func (bot *CQBot) CQGetGuildMembers(guildID uint64, nextToken string) global.MSG if !exists { return Failed(100, "NEXT_TOKEN_NOT_EXISTS") } - token = i.(*guildMemberPageToken) + token = i if token.guildID != guildID { return Failed(100, "GUILD_NOT_MATCH") } diff --git a/coolq/bot.go b/coolq/bot.go index f3beca250..bd69f05d4 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -32,7 +32,7 @@ type CQBot struct { friendReqCache sync.Map tempSessionCache sync.Map - nextTokenCache *utils.Cache + nextTokenCache *utils.Cache[*guildMemberPageToken] } // Event 事件 @@ -67,40 +67,40 @@ func (e *Event) JSONString() string { func NewQQBot(cli *client.QQClient) *CQBot { bot := &CQBot{ Client: cli, - nextTokenCache: utils.NewCache(time.Second * 10), + nextTokenCache: utils.NewCache[*guildMemberPageToken](time.Second * 10), } - bot.Client.OnPrivateMessage(bot.privateMessageEvent) - bot.Client.OnGroupMessage(bot.groupMessageEvent) + bot.Client.PrivateMessageEvent.Subscribe(bot.privateMessageEvent) + bot.Client.GroupMessageEvent.Subscribe(bot.groupMessageEvent) if base.ReportSelfMessage { - bot.Client.OnSelfPrivateMessage(bot.privateMessageEvent) - bot.Client.OnSelfGroupMessage(bot.groupMessageEvent) + bot.Client.SelfPrivateMessageEvent.Subscribe(bot.privateMessageEvent) + bot.Client.SelfGroupMessageEvent.Subscribe(bot.groupMessageEvent) } - bot.Client.OnTempMessage(bot.tempMessageEvent) + bot.Client.TempMessageEvent.Subscribe(bot.tempMessageEvent) bot.Client.GuildService.OnGuildChannelMessage(bot.guildChannelMessageEvent) bot.Client.GuildService.OnGuildMessageReactionsUpdated(bot.guildMessageReactionsUpdatedEvent) bot.Client.GuildService.OnGuildMessageRecalled(bot.guildChannelMessageRecalledEvent) bot.Client.GuildService.OnGuildChannelUpdated(bot.guildChannelUpdatedEvent) bot.Client.GuildService.OnGuildChannelCreated(bot.guildChannelCreatedEvent) bot.Client.GuildService.OnGuildChannelDestroyed(bot.guildChannelDestroyedEvent) - bot.Client.OnGroupMuted(bot.groupMutedEvent) - bot.Client.OnGroupMessageRecalled(bot.groupRecallEvent) - bot.Client.OnGroupNotify(bot.groupNotifyEvent) - bot.Client.OnFriendNotify(bot.friendNotifyEvent) - bot.Client.OnMemberSpecialTitleUpdated(bot.memberTitleUpdatedEvent) - bot.Client.OnFriendMessageRecalled(bot.friendRecallEvent) - bot.Client.OnReceivedOfflineFile(bot.offlineFileEvent) - bot.Client.OnJoinGroup(bot.joinGroupEvent) - bot.Client.OnLeaveGroup(bot.leaveGroupEvent) - bot.Client.OnGroupMemberJoined(bot.memberJoinEvent) - bot.Client.OnGroupMemberLeaved(bot.memberLeaveEvent) - bot.Client.OnGroupMemberPermissionChanged(bot.memberPermissionChangedEvent) - bot.Client.OnGroupMemberCardUpdated(bot.memberCardUpdatedEvent) - bot.Client.OnNewFriendRequest(bot.friendRequestEvent) - bot.Client.OnNewFriendAdded(bot.friendAddedEvent) - bot.Client.OnGroupInvited(bot.groupInvitedEvent) - bot.Client.OnUserWantJoinGroup(bot.groupJoinReqEvent) - bot.Client.OnOtherClientStatusChanged(bot.otherClientStatusChangedEvent) - bot.Client.OnGroupDigest(bot.groupEssenceMsg) + bot.Client.GroupMuteEvent.Subscribe(bot.groupMutedEvent) + bot.Client.GroupMessageRecalledEvent.Subscribe(bot.groupRecallEvent) + bot.Client.GroupNotifyEvent.Subscribe(bot.groupNotifyEvent) + bot.Client.FriendNotifyEvent.Subscribe(bot.friendNotifyEvent) + bot.Client.MemberSpecialTitleUpdatedEvent.Subscribe(bot.memberTitleUpdatedEvent) + bot.Client.FriendMessageRecalledEvent.Subscribe(bot.friendRecallEvent) + bot.Client.OfflineFileEvent.Subscribe(bot.offlineFileEvent) + bot.Client.GroupJoinEvent.Subscribe(bot.joinGroupEvent) + bot.Client.GroupLeaveEvent.Subscribe(bot.leaveGroupEvent) + bot.Client.GroupMemberJoinEvent.Subscribe(bot.memberJoinEvent) + bot.Client.GroupMemberLeaveEvent.Subscribe(bot.memberLeaveEvent) + bot.Client.GroupMemberPermissionChangedEvent.Subscribe(bot.memberPermissionChangedEvent) + bot.Client.MemberCardUpdatedEvent.Subscribe(bot.memberCardUpdatedEvent) + bot.Client.NewFriendRequestEvent.Subscribe(bot.friendRequestEvent) + bot.Client.NewFriendEvent.Subscribe(bot.friendAddedEvent) + bot.Client.GroupInvitedEvent.Subscribe(bot.groupInvitedEvent) + bot.Client.UserWantJoinGroupEvent.Subscribe(bot.groupJoinReqEvent) + bot.Client.OtherClientStatusChangedEvent.Subscribe(bot.otherClientStatusChangedEvent) + bot.Client.GroupDigestEvent.Subscribe(bot.groupEssenceMsg) go func() { if base.HeartbeatInterval == 0 { log.Warn("警告: 心跳功能已关闭,若非预期,请检查配置文件。") @@ -158,6 +158,7 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { return nil, errors.New("image type error: " + mime) } + // todo: enable multi-thread upload, now got error code 81 i, err := bot.Client.UploadImage(target, img.Stream, 4) if err != nil { return nil, err diff --git a/go.mod b/go.mod index c22aa4e32..70f00527c 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/Mrs4s/go-cqhttp -go 1.17 +go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220227135520-9884d9b0de20 + github.com/Mrs4s/MiraiGo v0.0.0-20220301082018-d9f803837f49 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index a0e74b2d8..ee3aa7d7c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220227135520-9884d9b0de20 h1:ZbJOWQRQB64npjqGMc031/Ry3bKhtMvx7Rf4bx0t9xI= -github.com/Mrs4s/MiraiGo v0.0.0-20220227135520-9884d9b0de20/go.mod h1:23f5g1I7XT7mB3zq02B3rfuiDnE7PJYSfvb/skzDVhI= +github.com/Mrs4s/MiraiGo v0.0.0-20220301082018-d9f803837f49 h1:IIEX0ue2VBA7kGOR1RpdQfPPKfeB4gWine47QXUyzTY= +github.com/Mrs4s/MiraiGo v0.0.0-20220301082018-d9f803837f49/go.mod h1:qJWkRO5vry/sUHthX5kh6go2llYIVAJ+Mq8p+N/FW+8= github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 h1:1jRB6xuBKwfgZrg0bA7XJin0VeNwG9iJKx9RXwDobt4= github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From a75f412b828ac26cb17a1e6d7522df7b6bb480a5 Mon Sep 17 00:00:00 2001 From: Sam <100737234+sam01102@users.noreply.github.com> Date: Wed, 2 Mar 2022 15:42:21 +0800 Subject: [PATCH 316/612] [update] Update 633 link (#1401) --- .github/ISSUE_TEMPLATE/bug-report.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 1cee16c7b..9849493c1 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -11,7 +11,7 @@ body: ## 感谢您愿意填写错误回报! ## 以下是一些注意事项,请务必阅读让我们能够更容易处理 - ### ❗ | 确定没有相同问题的ISSUE已被提出. (教程: https://github.com/Mrs4s/go-cqhttp/issues/633) + ### ❗ | 确定没有相同问题的ISSUE已被提出. (教程: https://forums.go-cqhttp.org/t/topic/141) ### 🌎| 请准确填写环境信息 ### ❔ | 打开DEBUG模式复现,并提供出现问题前后至少 10 秒的完整日志内容。请自行删除日志内存在的个人信息及敏感内容。 ### ⚠ | 如果涉及内存泄漏/CPU占用异常请打开DEBUG模式并下载pprof性能分析. @@ -24,7 +24,7 @@ body: attributes: label: 请确保您已阅读以上注意事项,并勾选下方的确认框。 options: - - label: "我已经仔细阅读上述教程和 [\"提问前需知\"](https://github.com/Mrs4s/go-cqhttp/issues/633)" + - label: "我已经仔细阅读上述教程和 [\"提问前需知\"](https://forums.go-cqhttp.org/t/topic/141)" required: true - label: "我已经使用 [dev分支版本](https://github.com/Mrs4s/go-cqhttp/actions/workflows/ci.yml) 测试过,问题依旧存在。" required: true From 4d8c55aca1d561b80857557a6311f08671497a4c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 2 Mar 2022 22:08:47 +0800 Subject: [PATCH 317/612] cmd/gocq: adapt MiraiGo Logger interface --- cmd/gocq/main.go | 49 ++++++++++++++++++++++++++++++------------------ go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 863074bb8..cb52a0cb2 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -130,7 +130,6 @@ func Main() { log.Info("当前版本:", base.Version) if base.Debug { log.SetLevel(log.DebugLevel) - log.SetReportCaller(true) log.Warnf("已开启Debug模式.") // log.Debugf("开发交流群: 192548878") } @@ -383,22 +382,36 @@ func newClient() *client.QQClient { } log.Infof("读取到 %v 个自定义地址.", len(addr)) } - c.OnLog(func(c *client.QQClient, e *client.LogEvent) { - switch e.Type { - case "INFO": - log.Info("Protocol -> " + e.Message) - case "ERROR": - log.Error("Protocol -> " + e.Message) - case "DEBUG": - log.Debug("Protocol -> " + e.Message) - case "DUMP": - if !global.PathExists(global.DumpsPath) { - _ = os.MkdirAll(global.DumpsPath, 0o755) - } - dumpFile := path.Join(global.DumpsPath, fmt.Sprintf("%v.dump", time.Now().Unix())) - log.Errorf("出现错误 %v. 详细信息已转储至文件 %v 请连同日志提交给开发者处理", e.Message, dumpFile) - _ = os.WriteFile(dumpFile, e.Dump, 0o644) - } - }) + c.SetLogger(protocolLogger{}) return c } + +type protocolLogger struct{} + +const fromProtocol = "Protocol -> " + +func (p protocolLogger) Info(format string, arg ...any) { + log.Infof(fromProtocol+format, arg...) +} + +func (p protocolLogger) Warning(format string, arg ...any) { + log.Warnf(fromProtocol+format, arg...) +} + +func (p protocolLogger) Debug(format string, arg ...any) { + log.Debugf(fromProtocol+format, arg...) +} + +func (p protocolLogger) Error(format string, arg ...any) { + log.Errorf(fromProtocol+format, arg...) +} + +func (p protocolLogger) Dump(data []byte, format string, arg ...any) { + if !global.PathExists(global.DumpsPath) { + _ = os.MkdirAll(global.DumpsPath, 0o755) + } + dumpFile := path.Join(global.DumpsPath, fmt.Sprintf("%v.dump", time.Now().Unix())) + message := fmt.Sprintf(format, arg...) + log.Errorf("出现错误 %v. 详细信息已转储至文件 %v 请连同日志提交给开发者处理", message, dumpFile) + _ = os.WriteFile(dumpFile, data, 0o644) +} diff --git a/go.mod b/go.mod index 70f00527c..03ed722ef 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220301082018-d9f803837f49 + github.com/Mrs4s/MiraiGo v0.0.0-20220302134146-348e317a3400 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index ee3aa7d7c..e4a9bed4d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220301082018-d9f803837f49 h1:IIEX0ue2VBA7kGOR1RpdQfPPKfeB4gWine47QXUyzTY= -github.com/Mrs4s/MiraiGo v0.0.0-20220301082018-d9f803837f49/go.mod h1:qJWkRO5vry/sUHthX5kh6go2llYIVAJ+Mq8p+N/FW+8= +github.com/Mrs4s/MiraiGo v0.0.0-20220302134146-348e317a3400 h1:5e0KDN118/RKItDmUcJyCkyyK/EGItYYJ5TVX/jb6xM= +github.com/Mrs4s/MiraiGo v0.0.0-20220302134146-348e317a3400/go.mod h1:qJWkRO5vry/sUHthX5kh6go2llYIVAJ+Mq8p+N/FW+8= github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 h1:1jRB6xuBKwfgZrg0bA7XJin0VeNwG9iJKx9RXwDobt4= github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From 152521893d147895f2e768045798e11506d50746 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 4 Mar 2022 17:56:33 +0800 Subject: [PATCH 318/612] dep: update go-silk --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 03ed722ef..8ba6cc29d 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tidwall/gjson v1.14.0 - github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 + github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea go.mongodb.org/mongo-driver v1.8.3 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 diff --git a/go.sum b/go.sum index e4a9bed4d..d60cf4632 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4= -github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= +github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea h1:sl1pYm1kHtIndckTY8YDt+QFt77vI0JnKHP0U8rZtKc= +github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= From d7fe481a8b05ee8337791ae3cd96ae62c5cdc6dd Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 13 Mar 2022 15:23:14 +0800 Subject: [PATCH 319/612] db,server: release config after init --- coolq/cqcode.go | 2 -- db/multidb.go | 1 + modules/servers/servers.go | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 48c1cf131..f727a9d84 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -928,8 +928,6 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So if !lawful { return nil, errors.New("audio type error: " + mt) } - } - if !global.IsAMRorSILK(data) { data, err = global.EncoderSilk(data) if err != nil { return nil, err diff --git a/db/multidb.go b/db/multidb.go index 6d3825ce6..68d0ab30f 100644 --- a/db/multidb.go +++ b/db/multidb.go @@ -45,6 +45,7 @@ func Open() error { return errors.Wrap(err, "open backend error") } } + base.Database = nil return nil } diff --git a/modules/servers/servers.go b/modules/servers/servers.go index 7a8ae2890..e1c28c48a 100644 --- a/modules/servers/servers.go +++ b/modules/servers/servers.go @@ -43,4 +43,5 @@ func Run(bot *coolq.CQBot) { for _, fn := range nocfgsvr { go fn(bot) } + base.Servers = nil } From 76295b0e89f84ce8448088b9fa39c09059ae2310 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 17 Mar 2022 16:59:47 +0800 Subject: [PATCH 320/612] dep: update MiraiGo --- db/mongodb/mongodb.go | 32 +++++++++++++++++++------------- go.mod | 2 +- go.sum | 4 ++-- modules/config/config.go | 15 +-------------- server/scf.go | 8 ++++---- 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/db/mongodb/mongodb.go b/db/mongodb/mongodb.go index 9c8449d7d..35ea702c7 100644 --- a/db/mongodb/mongodb.go +++ b/db/mongodb/mongodb.go @@ -10,15 +10,21 @@ import ( "gopkg.in/yaml.v3" "github.com/Mrs4s/go-cqhttp/db" - "github.com/Mrs4s/go-cqhttp/modules/config" ) -type MongoDBImpl struct { +type database struct { uri string db string mongo *mongo.Database } +// config mongodb 相关配置 +type config struct { + Enable bool `yaml:"enable"` + URI string `yaml:"uri"` + Database string `yaml:"database"` +} + const ( MongoGroupMessageCollection = "group-messages" MongoPrivateMessageCollection = "private-messages" @@ -26,8 +32,8 @@ const ( ) func init() { - db.Register("mongodb", func(node yaml.Node) db.Database { - conf := new(config.MongoDBConfig) + db.Register("database", func(node yaml.Node) db.Database { + conf := new(config) _ = node.Decode(conf) if conf.Database == "" { conf.Database = "gocq-database" @@ -35,11 +41,11 @@ func init() { if !conf.Enable { return nil } - return &MongoDBImpl{uri: conf.URI, db: conf.Database} + return &database{uri: conf.URI, db: conf.Database} }) } -func (m *MongoDBImpl) Open() error { +func (m *database) Open() error { cli, err := mongo.Connect(context.Background(), options.Client().ApplyURI(m.uri)) if err != nil { return errors.Wrap(err, "open mongo connection error") @@ -48,14 +54,14 @@ func (m *MongoDBImpl) Open() error { return nil } -func (m *MongoDBImpl) GetMessageByGlobalID(id int32) (db.StoredMessage, error) { +func (m *database) GetMessageByGlobalID(id int32) (db.StoredMessage, error) { if r, err := m.GetGroupMessageByGlobalID(id); err == nil { return r, nil } return m.GetPrivateMessageByGlobalID(id) } -func (m *MongoDBImpl) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, error) { +func (m *database) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, error) { coll := m.mongo.Collection(MongoGroupMessageCollection) var ret db.StoredGroupMessage if err := coll.FindOne(context.Background(), bson.D{{"globalId", id}}).Decode(&ret); err != nil { @@ -64,7 +70,7 @@ func (m *MongoDBImpl) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessag return &ret, nil } -func (m *MongoDBImpl) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessage, error) { +func (m *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessage, error) { coll := m.mongo.Collection(MongoPrivateMessageCollection) var ret db.StoredPrivateMessage if err := coll.FindOne(context.Background(), bson.D{{"globalId", id}}).Decode(&ret); err != nil { @@ -73,7 +79,7 @@ func (m *MongoDBImpl) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMe return &ret, nil } -func (m *MongoDBImpl) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { +func (m *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { coll := m.mongo.Collection(MongoGuildChannelMessageCollection) var ret db.StoredGuildChannelMessage if err := coll.FindOne(context.Background(), bson.D{{"_id", id}}).Decode(&ret); err != nil { @@ -82,19 +88,19 @@ func (m *MongoDBImpl) GetGuildChannelMessageByID(id string) (*db.StoredGuildChan return &ret, nil } -func (m *MongoDBImpl) InsertGroupMessage(msg *db.StoredGroupMessage) error { +func (m *database) InsertGroupMessage(msg *db.StoredGroupMessage) error { coll := m.mongo.Collection(MongoGroupMessageCollection) _, err := coll.UpdateOne(context.Background(), bson.D{{"_id", msg.ID}}, bson.D{{"$set", msg}}, options.Update().SetUpsert(true)) return errors.Wrap(err, "insert error") } -func (m *MongoDBImpl) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { +func (m *database) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { coll := m.mongo.Collection(MongoPrivateMessageCollection) _, err := coll.UpdateOne(context.Background(), bson.D{{"_id", msg.ID}}, bson.D{{"$set", msg}}, options.Update().SetUpsert(true)) return errors.Wrap(err, "insert error") } -func (m *MongoDBImpl) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) error { +func (m *database) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) error { coll := m.mongo.Collection(MongoGuildChannelMessageCollection) _, err := coll.UpdateOne(context.Background(), bson.D{{"_id", msg.ID}}, bson.D{{"$set", msg}}, options.Update().SetUpsert(true)) return errors.Wrap(err, "insert error") diff --git a/go.mod b/go.mod index 8ba6cc29d..28b155c82 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220302134146-348e317a3400 + github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index d60cf4632..1a7f972ed 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220302134146-348e317a3400 h1:5e0KDN118/RKItDmUcJyCkyyK/EGItYYJ5TVX/jb6xM= -github.com/Mrs4s/MiraiGo v0.0.0-20220302134146-348e317a3400/go.mod h1:qJWkRO5vry/sUHthX5kh6go2llYIVAJ+Mq8p+N/FW+8= +github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3 h1:U3UumMt052Ii1gGrkKM1MbX1uxCzxKhlfuNQ42LtIRQ= +github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3/go.mod h1:qJWkRO5vry/sUHthX5kh6go2llYIVAJ+Mq8p+N/FW+8= github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 h1:1jRB6xuBKwfgZrg0bA7XJin0VeNwG9iJKx9RXwDobt4= github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= diff --git a/modules/config/config.go b/modules/config/config.go index 1386aa66f..9c3c65edb 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -8,7 +8,6 @@ import ( "os" "regexp" "strings" - "sync" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" @@ -75,13 +74,6 @@ type Server struct { Default string } -// MongoDBConfig mongodb 相关配置 -type MongoDBConfig struct { - Enable bool `yaml:"enable"` - URI string `yaml:"uri"` - Database string `yaml:"database"` -} - // Parse 从默认配置文件路径中获取 func Parse(path string) *Config { file, err := os.ReadFile(path) @@ -98,16 +90,11 @@ func Parse(path string) *Config { return config } -var ( - serverconfs []*Server - mu sync.Mutex -) +var serverconfs []*Server // AddServer 添加该服务的简介和默认配置 func AddServer(s *Server) { - mu.Lock() serverconfs = append(serverconfs, s) - mu.Unlock() } // generateConfig 生成配置文件 diff --git a/server/scf.go b/server/scf.go index 29546ae41..f41b3597f 100644 --- a/server/scf.go +++ b/server/scf.go @@ -190,9 +190,9 @@ func (c *lambdaClient) next() *http.Request { if resp.StatusCode != http.StatusOK { return nil } - req := new(http.Request) - invoke := new(lambdaInvoke) - _ = json.NewDecoder(resp.Body).Decode(invoke) + var req http.Request + var invoke lambdaInvoke + _ = json.NewDecoder(resp.Body).Decode(&invoke) if invoke.HTTPMethod == "" { // 不是 api 网关 return nil } @@ -211,5 +211,5 @@ func (c *lambdaClient) next() *http.Request { query[k] = []string{v} } req.URL.RawQuery = query.Encode() - return req + return &req } From d48dc4fb3c73ea2b070d97dce941a04f526c4746 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 17 Mar 2022 17:58:07 +0800 Subject: [PATCH 321/612] ci: disable golangci-lint --- .github/workflows/ci.yml | 3 +-- .github/workflows/golint.yml | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b36780c5..05bc69a27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,8 +28,7 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v2.1.3 with: - stable: false - go-version: 1.18.0-rc1 + go-version: 1.18 - name: Cache downloaded module uses: actions/cache@v2 with: diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index 5a9ef49c8..e4f61e857 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -12,9 +12,10 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v2.1.3 with: - go-version: 1.17 + go-version: 1.18 - name: golangci-lint + if: ${{ false }} uses: golangci/golangci-lint-action@v2 with: version: latest From 779fa207041eb86edc0ab26af1640b173fecbcfc Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 17 Mar 2022 21:20:34 +0800 Subject: [PATCH 322/612] Dockerfile: update to go1.18 Fixes #1427 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d5ff7e567..2f2f1acd7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.17-alpine AS builder +FROM golang:1.18-alpine AS builder RUN go env -w GO111MODULE=auto \ && go env -w CGO_ENABLED=0 \ From 34613306b311365f08b2cf2f8fd709d09c235c49 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 17:07:10 +0800 Subject: [PATCH 323/612] internal/mime: use stdlib to detect mimetype --- coolq/api.go | 4 +-- go.mod | 1 - go.sum | 6 ---- modules/config/config.go | 10 +----- modules/mime/mime.go | 71 +++++++++++++++------------------------- 5 files changed, 30 insertions(+), 62 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index dba906f7f..9c26ec284 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -38,7 +38,7 @@ type guildMemberPageToken struct { nextQueryParam string } -var defaultPageToken = &guildMemberPageToken{ +var defaultPageToken = guildMemberPageToken{ guildID: 0, nextIndex: 0, nextRoleID: 2, @@ -150,7 +150,7 @@ func (bot *CQBot) CQGetGuildMembers(guildID uint64, nextToken string) global.MSG if guild == nil { return Failed(100, "GUILD_NOT_FOUND") } - token := defaultPageToken + token := &defaultPageToken if nextToken != "" { i, exists := bot.nextTokenCache.Get(nextToken) if !exists { diff --git a/go.mod b/go.mod index 28b155c82..68169dcc8 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 - github.com/gabriel-vasile/mimetype v1.4.0 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/mattn/go-colorable v0.1.12 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 1a7f972ed..15d94b482 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,6 @@ github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4= -github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= -github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -120,7 +118,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -139,16 +136,13 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/modules/config/config.go b/modules/config/config.go index 9c3c65edb..158bd623b 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -137,8 +137,7 @@ func expand(s string, mapping func(string) string) string { r := regexp.MustCompile(`\${([a-zA-Z_]+[a-zA-Z0-9_:/.]*)}`) return r.ReplaceAllStringFunc(s, func(s string) string { s = strings.Trim(s, "${}") - // todo: use strings.Cut once go1.18 is released - before, after, ok := cut(s, ":") + before, after, ok := strings.Cut(s, ":") m := mapping(before) if ok && m == "" { return after @@ -146,10 +145,3 @@ func expand(s string, mapping func(string) string) string { return m }) } - -func cut(s, sep string) (before, after string, found bool) { - if i := strings.Index(s, sep); i >= 0 { - return s[:i], s[i+len(sep):], true - } - return s, "", false -} diff --git a/modules/mime/mime.go b/modules/mime/mime.go index 50d762db0..dbaa831a8 100644 --- a/modules/mime/mime.go +++ b/modules/mime/mime.go @@ -3,68 +3,51 @@ package mime import ( "io" - - "github.com/gabriel-vasile/mimetype" - "github.com/sirupsen/logrus" + "net/http" + "strings" "github.com/Mrs4s/go-cqhttp/internal/base" ) func init() { - base.IsLawfulAudio = checkImage + base.IsLawfulImage = checkImage base.IsLawfulAudio = checkAudio } -// keep sync with /docs/file.md#MINE -var lawfulImage = [...]string{ - "image/bmp", - "image/gif", - "image/jpeg", - "image/png", - "image/webp", -} +const limit = 4 * 1024 -var lawfulAudio = [...]string{ - "audio/aac", - "audio/aiff", - "audio/amr", - "audio/ape", - "audio/flac", - "audio/midi", - "audio/mp4", - "audio/mpeg", - "audio/ogg", - "audio/wav", - "audio/x-m4a", -} - -func check(r io.ReadSeeker, list []string) (bool, string) { - if base.SkipMimeScan { - return true, "" - } +func scan(r io.ReadSeeker) string { _, _ = r.Seek(0, io.SeekStart) defer r.Seek(0, io.SeekStart) - t, err := mimetype.DetectReader(r) - if err != nil { - logrus.Debugf("扫描 Mime 时出现问题: %v", err) - return false, "" - } - for _, lt := range list { - if t.Is(lt) { - return true, t.String() - } - } - return false, t.String() + in := make([]byte, limit) + _, _ = r.Read(in) + return http.DetectContentType(in) } // checkImage 判断给定流是否为合法图片 // 返回 是否合法, 实际Mime // 判断后会自动将 Stream Seek 至 0 -func checkImage(r io.ReadSeeker) (bool, string) { - return check(r, lawfulImage[:]) +func checkImage(r io.ReadSeeker) (ok bool, t string) { + if base.SkipMimeScan { + return true, "" + } + t = scan(r) + switch t { + case "image/bmp", "image/gif", "image/jpeg", "image/png", "image/webp": + ok = true + } + return } // checkImage 判断给定流是否为合法音频 func checkAudio(r io.ReadSeeker) (bool, string) { - return check(r, lawfulAudio[:]) + if base.SkipMimeScan { + return true, "" + } + t := scan(r) + // std mime type detection is not full supported for audio + if strings.Contains(t, "text") || strings.Contains(t, "image") { + return false, t + } + return true, t } From 0a603dee92f75e4f3b7b0d9df3aa1daa07c4b54c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 18:49:08 +0800 Subject: [PATCH 324/612] dep: revert go-silk version --- cmd/gocq/main.go | 5 ++--- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index cb52a0cb2..c62008038 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -324,10 +324,9 @@ func Main() { log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") - go selfupdate.CheckUpdate() go func() { - time.Sleep(5 * time.Second) - go selfdiagnosis.NetworkDiagnosis(cli) + selfupdate.CheckUpdate() + selfdiagnosis.NetworkDiagnosis(cli) }() <-global.SetupMainSignalHandler() diff --git a/go.mod b/go.mod index 68169dcc8..caa6d4637 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tidwall/gjson v1.14.0 - github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea + github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 go.mongodb.org/mongo-driver v1.8.3 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 diff --git a/go.sum b/go.sum index 15d94b482..41fea2769 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,8 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea h1:sl1pYm1kHtIndckTY8YDt+QFt77vI0JnKHP0U8rZtKc= -github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= +github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4= +github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= From afbf42b7094ed457c27614af57eeafb815f04818 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 19:36:06 +0800 Subject: [PATCH 325/612] dep: update MiraiGo --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index caa6d4637..2075be343 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3 + github.com/Mrs4s/MiraiGo v0.0.0-20220318113435-f5950d72fa77 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible @@ -24,7 +24,7 @@ require ( ) require ( - github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 // indirect + github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect diff --git a/go.sum b/go.sum index 41fea2769..abe4bf4ec 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3 h1:U3UumMt052Ii1gGrkKM1MbX1uxCzxKhlfuNQ42LtIRQ= -github.com/Mrs4s/MiraiGo v0.0.0-20220317085721-6d84141b8dd3/go.mod h1:qJWkRO5vry/sUHthX5kh6go2llYIVAJ+Mq8p+N/FW+8= -github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248 h1:1jRB6xuBKwfgZrg0bA7XJin0VeNwG9iJKx9RXwDobt4= -github.com/RomiChan/protobuf v0.0.0-20220227114948-643565fff248/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/Mrs4s/MiraiGo v0.0.0-20220318113435-f5950d72fa77 h1:SmyVuRtew04EETyL24+IxzQ+Tr8YhhPpAFmgx20V2nI= +github.com/Mrs4s/MiraiGo v0.0.0-20220318113435-f5950d72fa77/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= +github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= +github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 937538a7cbb6b3c689b23b8e54aa66b782513304 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 19:49:32 +0800 Subject: [PATCH 326/612] coolq: remove support for old cache path --- coolq/cqcode.go | 7 ------- global/fs.go | 17 ++++------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index f727a9d84..39e0de03a 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -917,9 +917,6 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So case "record": f := d["file"] data, err := global.FindFile(f, d["cache"], global.VoicePath) - if err == global.ErrSyntax { - data, err = global.FindFile(f, d["cache"], global.VoicePathOld) - } if err != nil { return nil, err } @@ -1241,10 +1238,6 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy } } exist := global.PathExists(rawPath) - if !exist && global.PathExists(path.Join(global.ImagePathOld, f)) { - exist = true - rawPath = path.Join(global.ImagePathOld, f) - } if !exist { if d["url"] != "" { return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, sourceType) diff --git a/global/fs.go b/global/fs.go index f46934442..be15a5899 100644 --- a/global/fs.go +++ b/global/fs.go @@ -22,27 +22,18 @@ import ( const ( // ImagePath go-cqhttp使用的图片缓存目录 ImagePath = "data/images" - // ImagePathOld 兼容旧版go-cqhttp使用的图片缓存目录 - ImagePathOld = "data/image" // VoicePath go-cqhttp使用的语音缓存目录 VoicePath = "data/voices" - // VoicePathOld 兼容旧版go-cqhttp使用的语音缓存目录 - VoicePathOld = "data/record" // VideoPath go-cqhttp使用的视频缓存目录 VideoPath = "data/videos" // CachePath go-cqhttp使用的缓存目录 CachePath = "data/cache" // DumpsPath go-cqhttp使用错误转储目录 DumpsPath = "dumps" -) - -var ( - // ErrSyntax Path语法错误时返回的错误 - ErrSyntax = errors.New("syntax error") // HeaderAmr AMR文件头 - HeaderAmr = []byte("#!AMR") + HeaderAmr = "#!AMR" // HeaderSilk Silkv3文件头 - HeaderSilk = []byte("\x02#!SILK_V3") + HeaderSilk = "\x02#!SILK_V3" ) // PathExists 判断给定path是否存在 @@ -78,13 +69,13 @@ func Check(err error, deleteSession bool) { // IsAMRorSILK 判断给定文件是否为Amr或Silk格式 func IsAMRorSILK(b []byte) bool { - return bytes.HasPrefix(b, HeaderAmr) || bytes.HasPrefix(b, HeaderSilk) + return bytes.HasPrefix(b, []byte(HeaderAmr)) || bytes.HasPrefix(b, []byte(HeaderSilk)) } // FindFile 从给定的File寻找文件,并返回文件byte数组。File是一个合法的URL。p为文件寻找位置。 // 对于HTTP/HTTPS形式的URL,Cache为"1"或空时表示启用缓存 func FindFile(file, cache, p string) (data []byte, err error) { - data, err = nil, ErrSyntax + data, err = nil, os.ErrNotExist switch { case strings.HasPrefix(file, "http"): // https also has prefix http hash := md5.Sum([]byte(file)) From cbcfee9f69ca2102ceda069245af2fde5d16fca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=83=E6=A9=98=20=E9=9B=AB=E9=9C=9E?= Date: Fri, 18 Mar 2022 21:15:53 +0800 Subject: [PATCH 327/612] feat: support get group notice #493 (#1418) * feat: support get group notice #493 * feat: update `go.mod` * fix --- coolq/api.go | 11 +++++++++++ go.mod | 2 +- go.sum | 4 ++-- modules/api/api.go | 3 +++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 9c26ec284..cdb7677d3 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -995,6 +995,17 @@ func (bot *CQBot) CQSetGroupName(groupID int64, name string) global.MSG { return Failed(100, "GROUP_NOT_FOUND", "群聊不存在") } +// CQGetGroupMemo 扩展API-获取群公告 +// @route(_get_group_notice) +func (bot *CQBot) CQGetGroupMemo(groupID int64) global.MSG { + r, err := bot.Client.GetGroupNotice(groupID) + if err != nil { + return Failed(100, "获取群公告失败", err.Error()) + } + + return OK(r) +} + // CQSetGroupMemo 扩展API-发送群公告 // // https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E7%BE%A4%E5%85%AC%E5%91%8A diff --git a/go.mod b/go.mod index 2075be343..80e124cad 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220318113435-f5950d72fa77 + github.com/Mrs4s/MiraiGo v0.0.0-20220318123613-8fa49fedb994 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible diff --git a/go.sum b/go.sum index abe4bf4ec..d36375bb7 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220318113435-f5950d72fa77 h1:SmyVuRtew04EETyL24+IxzQ+Tr8YhhPpAFmgx20V2nI= -github.com/Mrs4s/MiraiGo v0.0.0-20220318113435-f5950d72fa77/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= +github.com/Mrs4s/MiraiGo v0.0.0-20220318123613-8fa49fedb994 h1:wqsMt+ob6KMNEl0EWzn9nPoCEQx886UlFRPU9YdRpBQ= +github.com/Mrs4s/MiraiGo v0.0.0-20220318123613-8fa49fedb994/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= diff --git a/modules/api/api.go b/modules/api/api.go index e936269ac..fc40e2663 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -21,6 +21,9 @@ func (c *Caller) call(action string, p Getter) global.MSG { case ".ocr_image", "ocr_image": p0 := p.Get("image").String() return c.bot.CQOcrImage(p0) + case "_get_group_notice": + p0 := p.Get("group_id").Int() + return c.bot.CQGetGroupMemo(p0) case "_get_model_show": p0 := p.Get("model").String() return c.bot.CQGetModelShow(p0) From 429ff80cf0643e02f5e8b0574813e2f60d882da7 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 21:38:50 +0800 Subject: [PATCH 328/612] update MiraiGo & remove `_get_vip_info` Fixes #1429 --- coolq/api.go | 21 --------------------- go.mod | 2 +- go.sum | 4 ++-- modules/api/api.go | 3 --- 4 files changed, 3 insertions(+), 27 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index cdb7677d3..4cc15265b 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1235,27 +1235,6 @@ func (bot *CQBot) CQSetGroupAdmin(groupID, userID int64, enable bool) global.MSG return OK(nil) } -// CQGetVipInfo 扩展API-获取VIP信息 -// -// https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96vip%E4%BF%A1%E6%81%AF -// @route(_get_vip_info) -func (bot *CQBot) CQGetVipInfo(userID int64) global.MSG { - vip, err := bot.Client.GetVipInfo(userID) - if err != nil { - return Failed(100, "VIP_API_ERROR", err.Error()) - } - msg := global.MSG{ - "user_id": vip.Uin, - "nickname": vip.Name, - "level": vip.Level, - "level_speed": vip.LevelSpeed, - "vip_level": vip.VipLevel, - "vip_growth_speed": vip.VipGrowthSpeed, - "vip_growth_total": vip.VipGrowthTotal, - } - return OK(msg) -} - // CQGetGroupHonorInfo 获取群荣誉信息 // // https://git.io/Jtz1H diff --git a/go.mod b/go.mod index 80e124cad..922e213b9 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220318123613-8fa49fedb994 + github.com/Mrs4s/MiraiGo v0.0.0-20220318133222-87e53dd6e2f2 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible diff --git a/go.sum b/go.sum index d36375bb7..a415ab092 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220318123613-8fa49fedb994 h1:wqsMt+ob6KMNEl0EWzn9nPoCEQx886UlFRPU9YdRpBQ= -github.com/Mrs4s/MiraiGo v0.0.0-20220318123613-8fa49fedb994/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= +github.com/Mrs4s/MiraiGo v0.0.0-20220318133222-87e53dd6e2f2 h1:8Q2eZyYxpd8En9JTe79tx0dekENJ5OTto8fQiLS9tQA= +github.com/Mrs4s/MiraiGo v0.0.0-20220318133222-87e53dd6e2f2/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= diff --git a/modules/api/api.go b/modules/api/api.go index fc40e2663..b57c2500f 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -27,9 +27,6 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "_get_model_show": p0 := p.Get("model").String() return c.bot.CQGetModelShow(p0) - case "_get_vip_info": - p0 := p.Get("user_id").Int() - return c.bot.CQGetVipInfo(p0) case "_send_group_notice": p0 := p.Get("group_id").Int() p1 := p.Get("content").String() From de4cfe073395428249c78725069ee34ec4dd2867 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 22:58:14 +0800 Subject: [PATCH 329/612] dep: update MiraiGo Fix wrong self message event dispatch --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 922e213b9..1c246b02d 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220318133222-87e53dd6e2f2 + github.com/Mrs4s/MiraiGo v0.0.0-20220318145114-35f774b86afa github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible diff --git a/go.sum b/go.sum index a415ab092..17c27fd1b 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220318133222-87e53dd6e2f2 h1:8Q2eZyYxpd8En9JTe79tx0dekENJ5OTto8fQiLS9tQA= -github.com/Mrs4s/MiraiGo v0.0.0-20220318133222-87e53dd6e2f2/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= +github.com/Mrs4s/MiraiGo v0.0.0-20220318145114-35f774b86afa h1:UgOs+GImN0IYAZ/P/OH9nCOkEbidauK40F5axm11TcM= +github.com/Mrs4s/MiraiGo v0.0.0-20220318145114-35f774b86afa/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From d34531790ca5005d8c27d30fbf0d09d887329bda Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 18 Mar 2022 23:50:21 +0800 Subject: [PATCH 330/612] coolq: handle extract cover error For #1426 --- coolq/cqcode.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 39e0de03a..1e97f4b8e 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -1103,21 +1103,18 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So if cover, ok := d["cover"]; ok { data, _ = global.FindFile(cover, d["cache"], global.ImagePath) } else { - _ = global.ExtractCover(v.File, v.File+".jpg") + err = global.ExtractCover(v.File, v.File+".jpg") + if err != nil { + return nil, err + } data, _ = os.ReadFile(v.File + ".jpg") } v.thumb = bytes.NewReader(data) video, _ := os.Open(v.File) defer video.Close() - _, err = video.Seek(4, io.SeekStart) - if err != nil { - return nil, err - } + _, _ = video.Seek(4, io.SeekStart) header := make([]byte, 4) - _, err = video.Read(header) - if err != nil { - return nil, err - } + _, _ = video.Read(header) if !bytes.Equal(header, []byte{0x66, 0x74, 0x79, 0x70}) { // check file header ftyp _, _ = video.Seek(0, io.SeekStart) hash, _ := utils.ComputeMd5AndLength(video) From 70da0ce6e4dfa0aa318fef6809425fe8e4ef3246 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 19 Mar 2022 10:11:15 +0800 Subject: [PATCH 331/612] ci: enable golangci-lint latest version support go1.18 --- .github/workflows/golint.yml | 1 - go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index e4f61e857..6ab8830aa 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -15,7 +15,6 @@ jobs: go-version: 1.18 - name: golangci-lint - if: ${{ false }} uses: golangci/golangci-lint-action@v2 with: version: latest diff --git a/go.mod b/go.mod index 1c246b02d..6cb3f648c 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220318145114-35f774b86afa + github.com/Mrs4s/MiraiGo v0.0.0-20220319020337-5903226f2534 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible diff --git a/go.sum b/go.sum index 17c27fd1b..cfae5fad2 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220318145114-35f774b86afa h1:UgOs+GImN0IYAZ/P/OH9nCOkEbidauK40F5axm11TcM= -github.com/Mrs4s/MiraiGo v0.0.0-20220318145114-35f774b86afa/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= +github.com/Mrs4s/MiraiGo v0.0.0-20220319020337-5903226f2534 h1:Ah5fmskBIYruUGBIH1mLoeXmmGDO92N8JPDPW0Fd7f4= +github.com/Mrs4s/MiraiGo v0.0.0-20220319020337-5903226f2534/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From 5b148d6c5e4f395d959f8337a8ca6819d8048124 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 19 Mar 2022 10:53:58 +0800 Subject: [PATCH 332/612] ci: disable some lint some lint work failed with generic --- .golangci.yml | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 4ff3d91cc..0a87b864b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,35 +21,28 @@ linters: disable-all: true fast: false enable: - - bodyclose - - deadcode - - depguard - - dogsled + #- bodyclose + #- deadcode + #- depguard + #- dogsled + - gofmt + - goimports - errcheck - exportloopref - exhaustive - bidichk - #- funlen - #- goconst - gocritic - #- gocyclo - - gofmt - - goimports - - goprintffuncname - #- gosec - - gosimple + #- gosimple - govet - ineffassign - #- misspell - - nolintlint - - rowserrcheck - - staticcheck + #- nolintlint + #- rowserrcheck + #- staticcheck - structcheck - - stylecheck - - typecheck + #- stylecheck - unconvert - - unparam - - unused + #- unparam + #- unused - varcheck - whitespace - prealloc From 062eea62abfe2338f21218fce16fa11aeeeacafc Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 20 Mar 2022 15:19:47 +0800 Subject: [PATCH 333/612] coolq: use generic sync.Map --- coolq/api.go | 4 ++-- coolq/bot.go | 7 ++++--- go.mod | 3 ++- go.sum | 6 ++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 4cc15265b..020f2115e 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1126,9 +1126,9 @@ func (bot *CQBot) CQProcessFriendRequest(flag string, approve bool) global.MSG { return Failed(100, "FLAG_NOT_FOUND", "FLAG不存在") } if approve { - req.(*client.NewFriendRequest).Accept() + req.Accept() } else { - req.(*client.NewFriendRequest).Reject() + req.Reject() } return OK(nil) } diff --git a/coolq/bot.go b/coolq/bot.go index bd69f05d4..407800f66 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -14,6 +14,7 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" + "github.com/RomiChan/syncx" "github.com/pkg/errors" "github.com/segmentio/asm/base64" log "github.com/sirupsen/logrus" @@ -30,8 +31,8 @@ type CQBot struct { lock sync.RWMutex events []func(*Event) - friendReqCache sync.Map - tempSessionCache sync.Map + friendReqCache syncx.Map[string, *client.NewFriendRequest] + tempSessionCache syncx.Map[int64, *client.TempSessionInfo] nextTokenCache *utils.Cache[*guildMemberPageToken] } @@ -366,7 +367,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen } break } - msg, err := session.(*client.TempSessionInfo).SendMessage(m) + msg, err := session.SendMessage(m) if err != nil { log.Errorf("发送临时会话消息失败: %v", err) break diff --git a/go.mod b/go.mod index 6cb3f648c..43649e2ba 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,8 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220319020337-5903226f2534 + github.com/Mrs4s/MiraiGo v0.0.0-20220320070754-38990f6e1cf9 + github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257 github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible diff --git a/go.sum b/go.sum index cfae5fad2..2442768e9 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,11 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220319020337-5903226f2534 h1:Ah5fmskBIYruUGBIH1mLoeXmmGDO92N8JPDPW0Fd7f4= -github.com/Mrs4s/MiraiGo v0.0.0-20220319020337-5903226f2534/go.mod h1:kkUjOuOj2FMfqEEtnan/mxxKBZopYRBVIR4gSAdt7Hs= +github.com/Mrs4s/MiraiGo v0.0.0-20220320070754-38990f6e1cf9 h1:YJeHJRNVfpbuC7wSQD3WOcysC7AcILgQf5d8kFPL12Y= +github.com/Mrs4s/MiraiGo v0.0.0-20220320070754-38990f6e1cf9/go.mod h1:qseuVVwl5wxdhphaS29F6CN0gV2J0ojT880Ql5g2MBo= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= +github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257 h1:fdMod+DEoiICoTtS1Wij/wl1d57FPvKVmretLi2sGD8= +github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 112441d76e9e66ed5b6b369d440a4eb85baa2c7c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 21 Mar 2022 21:41:29 +0800 Subject: [PATCH 334/612] coolq: refactor send forward message --- coolq/api.go | 204 ++++++++++++++++++++++++++------------------------- go.mod | 4 +- go.sum | 8 +- 3 files changed, 110 insertions(+), 106 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 020f2115e..486b543b8 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -789,116 +789,120 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.ForwardElement { ts := time.Now().Add(-time.Minute * 5) - fm := message.NewForwardMessage() source := message.Source{SourceType: message.SourceGroup, PrimaryID: groupID} - - var w worker - resolveElement := func(elems []message.IMessageElement) []message.IMessageElement { - for i, elem := range elems { - p := &elems[i] - switch o := elem.(type) { - case *LocalVideoElement: - w.do(func() { - gm, err := bot.uploadLocalVideo(source, o) - if err != nil { - log.Warnf(uploadFailedTemplate, "群", groupID, "视频", err) - } else { - *p = gm - } - }) - case *LocalImageElement: - w.do(func() { - gm, err := bot.uploadLocalImage(source, o) - if err != nil { - log.Warnf(uploadFailedTemplate, "群", groupID, "图片", err) - } else { - *p = gm - } - }) + builder := bot.Client.NewForwardMessageBuilder(groupID) + + var convertMessage func(m gjson.Result) *message.ForwardMessage + convertMessage = func(m gjson.Result) *message.ForwardMessage { + fm := message.NewForwardMessage() + var w worker + resolveElement := func(elems []message.IMessageElement) []message.IMessageElement { + for i, elem := range elems { + p := &elems[i] + switch o := elem.(type) { + case *LocalVideoElement: + w.do(func() { + gm, err := bot.uploadLocalVideo(source, o) + if err != nil { + log.Warnf(uploadFailedTemplate, "群", groupID, "视频", err) + } else { + *p = gm + } + }) + case *LocalImageElement: + w.do(func() { + gm, err := bot.uploadLocalImage(source, o) + if err != nil { + log.Warnf(uploadFailedTemplate, "群", groupID, "图片", err) + } else { + *p = gm + } + }) + } } + return elems } - return elems - } - convert := func(e gjson.Result) *message.ForwardNode { - if e.Get("type").Str != "node" { - return nil - } - ts.Add(time.Second) - if e.Get("data.id").Exists() { - i := e.Get("data.id").Int() - m, _ := db.GetGroupMessageByGlobalID(int32(i)) - if m != nil { - return &message.ForwardNode{ - SenderId: m.Attribute.SenderUin, - SenderName: m.Attribute.SenderName, - Time: func() int32 { - msgTime := m.Attribute.Timestamp - if msgTime == 0 { - return int32(ts.Unix()) - } - return int32(msgTime) - }(), - Message: resolveElement(bot.ConvertContentMessage(m.Content, message.SourceGroup)), + convert := func(e gjson.Result) *message.ForwardNode { + if e.Get("type").Str != "node" { + return nil + } + if e.Get("data.id").Exists() { + i := e.Get("data.id").Int() + m, _ := db.GetGroupMessageByGlobalID(int32(i)) + if m != nil { + msgTime := m.Attribute.Timestamp + if msgTime == 0 { + msgTime = ts.Unix() + } + return &message.ForwardNode{ + SenderId: m.Attribute.SenderUin, + SenderName: m.Attribute.SenderName, + Time: int32(msgTime), + Message: resolveElement(bot.ConvertContentMessage(m.Content, message.SourceGroup)), + } } + log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str) + return nil } - log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str) - return nil - } - uin := e.Get("data.[user_id,uin].0").Int() - msgTime := e.Get("data.time").Int() - if msgTime == 0 { - msgTime = ts.Unix() - } - name := e.Get("data.name").Str - c := e.Get("data.content") - if c.IsArray() { - nested := false - c.ForEach(func(_, value gjson.Result) bool { - if value.Get("type").Str == "node" { - nested = true - return false + uin := e.Get("data.[user_id,uin].0").Int() + msgTime := e.Get("data.time").Int() + if msgTime == 0 { + msgTime = ts.Unix() + } + name := e.Get("data.name").Str + c := e.Get("data.content") + if c.IsArray() { + nested := false + c.ForEach(func(_, value gjson.Result) bool { + if value.Get("type").Str == "node" { + nested = true + return false + } + return true + }) + if nested { // 处理嵌套 + nestedNode := builder.NestedNode() + builder.Link(nestedNode, convertMessage(c)) + return &message.ForwardNode{ + SenderId: uin, + SenderName: name, + Time: int32(msgTime), + Message: []message.IMessageElement{nestedNode}, + } } - return true - }) - if nested { // 处理嵌套 - fe := bot.uploadForwardElement(c, groupID) + } + content := bot.ConvertObjectMessage(c, message.SourceGroup) + if uin != 0 && name != "" && len(content) > 0 { return &message.ForwardNode{ SenderId: uin, SenderName: name, Time: int32(msgTime), - Message: []message.IMessageElement{fe}, + Message: resolveElement(content), } } + log.Warnf("警告: 非法 Forward node 将跳过. uin: %v name: %v content count: %v", uin, name, len(content)) + return nil } - content := bot.ConvertObjectMessage(c, message.SourceGroup) - if uin != 0 && name != "" && len(content) > 0 { - return &message.ForwardNode{ - SenderId: uin, - SenderName: name, - Time: int32(msgTime), - Message: resolveElement(content), - } - } - log.Warnf("警告: 非法 Forward node 将跳过. uin: %v name: %v content count: %v", uin, name, len(content)) - return nil - } - if m.IsArray() { - for _, item := range m.Array() { - node := convert(item) + if m.IsArray() { + for _, item := range m.Array() { + node := convert(item) + if node != nil { + fm.AddNode(node) + } + } + } else { + node := convert(m) if node != nil { fm.AddNode(node) } } - } else { - node := convert(m) - if node != nil { - fm.AddNode(node) - } + + w.wait() + return fm } - w.wait() - return bot.Client.UploadGroupForwardMessage(groupID, fm) + return builder.Main(convertMessage(m)) } // CQSendGroupForwardMessage 扩展API-发送合并转发(群) @@ -912,17 +916,17 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa } fe := bot.uploadForwardElement(m, groupID) - if fe != nil { - ret := bot.Client.SendGroupForwardMessage(groupID, fe) - if ret == nil || ret.Id == -1 { - log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.") - return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") - } - return OK(global.MSG{ - "message_id": bot.InsertGroupMessage(ret), - }) + if fe == nil { + return Failed(100, "EMPTY_NODES", "未找到任何可发送的合并转发信息") + } + ret := bot.Client.SendGroupForwardMessage(groupID, fe) + if ret == nil || ret.Id == -1 { + log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.") + return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") } - return Failed(100, "EMPTY_NODES", "未找到任何可发送的合并转发信息") + return OK(global.MSG{ + "message_id": bot.InsertGroupMessage(ret), + }) } // CQSendPrivateMessage 发送私聊消息 diff --git a/go.mod b/go.mod index 43649e2ba..abf9dc248 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220320070754-38990f6e1cf9 - github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257 + github.com/Mrs4s/MiraiGo v0.0.0-20220321133915-aa657c0f09d0 + github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible diff --git a/go.sum b/go.sum index 2442768e9..95564b9e3 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220320070754-38990f6e1cf9 h1:YJeHJRNVfpbuC7wSQD3WOcysC7AcILgQf5d8kFPL12Y= -github.com/Mrs4s/MiraiGo v0.0.0-20220320070754-38990f6e1cf9/go.mod h1:qseuVVwl5wxdhphaS29F6CN0gV2J0ojT880Ql5g2MBo= +github.com/Mrs4s/MiraiGo v0.0.0-20220321133915-aa657c0f09d0 h1:RsFAUylRh9T4zIx4iVe8KTbG/XtI/uHw2qhhQfoCetE= +github.com/Mrs4s/MiraiGo v0.0.0-20220321133915-aa657c0f09d0/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= -github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257 h1:fdMod+DEoiICoTtS1Wij/wl1d57FPvKVmretLi2sGD8= -github.com/RomiChan/syncx v0.0.0-20220320065321-8d448f958257/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= +github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= +github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From e4d10eb2ae37ff07c93f571214efbbbacb70c1ff Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 22 Mar 2022 22:56:06 +0800 Subject: [PATCH 335/612] global: use net/netip --- global/fs.go | 14 ++++++-------- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/global/fs.go b/global/fs.go index be15a5899..3c197712a 100644 --- a/global/fs.go +++ b/global/fs.go @@ -5,12 +5,11 @@ import ( "crypto/md5" "encoding/hex" "errors" - "net" + "net/netip" "net/url" "os" "path" "runtime" - "strconv" "strings" "github.com/Mrs4s/MiraiGo/utils" @@ -129,19 +128,18 @@ func DelFile(path string) bool { } // ReadAddrFile 从给定path中读取合法的IP地址与端口,每个IP地址以换行符"\n"作为分隔 -func ReadAddrFile(path string) []*net.TCPAddr { +func ReadAddrFile(path string) []netip.AddrPort { d, err := os.ReadFile(path) if err != nil { return nil } str := string(d) lines := strings.Split(str, "\n") - var ret []*net.TCPAddr + var ret []netip.AddrPort for _, l := range lines { - ip := strings.Split(strings.TrimSpace(l), ":") - if len(ip) == 2 { - port, _ := strconv.Atoi(ip[1]) - ret = append(ret, &net.TCPAddr{IP: net.ParseIP(ip[0]), Port: port}) + addr, err := netip.ParseAddrPort(l) + if err == nil { + ret = append(ret, addr) } } return ret diff --git a/go.mod b/go.mod index abf9dc248..38ab2a0da 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220321133915-aa657c0f09d0 + github.com/Mrs4s/MiraiGo v0.0.0-20220322144437-665c6acf024a github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 diff --git a/go.sum b/go.sum index 95564b9e3..0b7e4b44c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220321133915-aa657c0f09d0 h1:RsFAUylRh9T4zIx4iVe8KTbG/XtI/uHw2qhhQfoCetE= -github.com/Mrs4s/MiraiGo v0.0.0-20220321133915-aa657c0f09d0/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= +github.com/Mrs4s/MiraiGo v0.0.0-20220322144437-665c6acf024a h1:rG0RcLo/kWRAXnVLgMkudktVO2yg3kVgdV1/Zc9fwh4= +github.com/Mrs4s/MiraiGo v0.0.0-20220322144437-665c6acf024a/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= From f63c59f1a40a4210bea6a59b10a658257722a2e0 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 23 Mar 2022 19:32:32 +0800 Subject: [PATCH 336/612] server: new config format for HTTP server and Websocket server For #1415 Fixes #1438 --- go.mod | 2 +- go.sum | 4 ++-- server/http.go | 30 ++++++++++++++++++------------ server/websocket.go | 23 ++++++++++++++++++++--- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 38ab2a0da..645550d10 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220322144437-665c6acf024a + github.com/Mrs4s/MiraiGo v0.0.0-20220323044857-868828f3da83 github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 diff --git a/go.sum b/go.sum index 0b7e4b44c..cedc95790 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220322144437-665c6acf024a h1:rG0RcLo/kWRAXnVLgMkudktVO2yg3kVgdV1/Zc9fwh4= -github.com/Mrs4s/MiraiGo v0.0.0-20220322144437-665c6acf024a/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= +github.com/Mrs4s/MiraiGo v0.0.0-20220323044857-868828f3da83 h1:2OA6ciqUytCaV7LQr/IvwCqRXJpc8bfvta63bn2XkHc= +github.com/Mrs4s/MiraiGo v0.0.0-20220323044857-868828f3da83/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= diff --git a/server/http.go b/server/http.go index b289eef8e..a43a2408a 100644 --- a/server/http.go +++ b/server/http.go @@ -8,6 +8,7 @@ import ( "encoding/json" "fmt" "io" + "net" "net/http" "net/url" "os" @@ -31,6 +32,7 @@ import ( // HTTPServer HTTP通信相关配置 type HTTPServer struct { Disabled bool `yaml:"disabled"` + Address string `yaml:"address"` Host string `yaml:"host"` Port int `yaml:"port"` Timeout int32 `yaml:"timeout"` @@ -242,12 +244,20 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) { return } - var addr string + network, addr := "tcp", "" s := &httpServer{accessToken: conf.AccessToken} - if conf.Host == "" || conf.Port == 0 { + if conf.Address != "" { + uri, err := url.Parse(conf.Address) + if err == nil && uri.Scheme != "" { + network = uri.Scheme + addr = uri.Host + } + } else if conf.Host != "" || conf.Port != 0 { + addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port) + log.Warnln("HTTP 服务器使用了过时的配置格式,请更新配置文件!") + } else { goto client } - addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port) s.api = api.NewCaller(bot) if conf.RateLimit.Enabled { s.api.Use(rateLimit(conf.RateLimit.Frequency, conf.RateLimit.Bucket)) @@ -255,20 +265,16 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) { if conf.LongPolling.Enabled { s.api.Use(longPolling(bot, conf.LongPolling.MaxQueueSize)) } - go func() { - log.Infof("CQ HTTP 服务器已启动: %v", addr) - server := &http.Server{ - Addr: addr, - Handler: s, - } - if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Error(err) - log.Infof("HTTP 服务启动失败, 请检查端口是否被占用.") + listener, err := net.Listen(network, addr) + if err != nil { + log.Infof("HTTP 服务启动失败, 请检查端口是否被占用: %v", err) log.Warnf("将在五秒后退出.") time.Sleep(time.Second * 5) os.Exit(1) } + log.Infof("CQ HTTP 服务器已启动: %v", listener.Addr()) + log.Fatal(http.Serve(listener, s)) }() client: for _, c := range conf.Post { diff --git a/server/websocket.go b/server/websocket.go index 20d3a73ba..888cd49c5 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -4,7 +4,9 @@ import ( "bytes" "encoding/json" "fmt" + "net" "net/http" + "net/url" "runtime/debug" "strconv" "strings" @@ -100,6 +102,7 @@ const wsReverseDefault = ` # 反向WS设置 // WebsocketServer 正向WS相关配置 type WebsocketServer struct { Disabled bool `yaml:"disabled"` + Address string `yaml:"address"` Host string `yaml:"host"` Port int `yaml:"port"` @@ -139,6 +142,17 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) { return } + network, address := "tcp", conf.Address + if conf.Address == "" && (conf.Host != "" || conf.Port != 0) { + log.Warn("正向 Websocket 使用了过时的配置格式,请更新配置文件") + address = fmt.Sprintf("%s:%d", conf.Host, conf.Port) + } else { + addr, err := url.Parse(conf.Address) + if err == nil && addr.Scheme != "" { + network = addr.Scheme + address = addr.Host + } + } s := &webSocketServer{ bot: b, conf: &conf, @@ -146,7 +160,6 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) { filter: conf.Filter, } filter.Add(s.filter) - addr := fmt.Sprintf("%s:%d", conf.Host, conf.Port) s.handshake = fmt.Sprintf(`{"_post_method":2,"meta_event_type":"lifecycle","post_type":"meta_event","self_id":%d,"sub_type":"connect","time":%d}`, b.Client.Uin, time.Now().Unix()) b.OnEventPush(s.onBotPushEvent) @@ -154,8 +167,12 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) { mux.HandleFunc("/event", s.event) mux.HandleFunc("/api", s.api) mux.HandleFunc("/", s.any) - log.Infof("CQ WebSocket 服务器已启动: %v", addr) - log.Fatal(http.ListenAndServe(addr, &mux)) + listener, err := net.Listen(network, address) + if err != nil { + log.Fatal(err) + } + log.Infof("CQ WebSocket 服务器已启动: %v", listener.Addr()) + log.Fatal(http.Serve(listener, &mux)) } // runWSClient 运行一个反向向WS client From d42d8dd3951545d38b92c571eed614986abd1e65 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 23 Mar 2022 19:55:21 +0800 Subject: [PATCH 337/612] server: add uri Path to address For #1415 --- server/http.go | 11 ++++++----- server/websocket.go | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/server/http.go b/server/http.go index a43a2408a..8bc362c9f 100644 --- a/server/http.go +++ b/server/http.go @@ -244,18 +244,19 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) { return } - network, addr := "tcp", "" + network, addr := "tcp", conf.Address s := &httpServer{accessToken: conf.AccessToken} - if conf.Address != "" { + switch { + case conf.Address != "": uri, err := url.Parse(conf.Address) if err == nil && uri.Scheme != "" { network = uri.Scheme - addr = uri.Host + addr = uri.Host + uri.Path } - } else if conf.Host != "" || conf.Port != 0 { + case conf.Host != "" || conf.Port != 0: addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port) log.Warnln("HTTP 服务器使用了过时的配置格式,请更新配置文件!") - } else { + default: goto client } s.api = api.NewCaller(bot) diff --git a/server/websocket.go b/server/websocket.go index 888cd49c5..df8c9a221 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -147,10 +147,10 @@ func runWSServer(b *coolq.CQBot, node yaml.Node) { log.Warn("正向 Websocket 使用了过时的配置格式,请更新配置文件") address = fmt.Sprintf("%s:%d", conf.Host, conf.Port) } else { - addr, err := url.Parse(conf.Address) - if err == nil && addr.Scheme != "" { - network = addr.Scheme - address = addr.Host + uri, err := url.Parse(conf.Address) + if err == nil && uri.Scheme != "" { + network = uri.Scheme + address = uri.Host + uri.Path } } s := &webSocketServer{ From 40a765b1171e432e2e21c55cb926c23326762eb2 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 23 Mar 2022 21:06:32 +0800 Subject: [PATCH 338/612] server: support unix socket Fixes #1415 --- server/http.go | 33 ++++++++++++++++++++++----- server/websocket.go | 54 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/server/http.go b/server/http.go index 8bc362c9f..321634316 100644 --- a/server/http.go +++ b/server/http.go @@ -2,8 +2,10 @@ package server import ( "bytes" + "context" "crypto/hmac" "crypto/sha1" + "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -65,6 +67,7 @@ type HTTPClient struct { filter string apiPort int timeout int32 + client *http.Client MaxRetries uint64 RetriesInterval uint64 } @@ -77,8 +80,7 @@ type httpCtx struct { const httpDefault = ` - http: # HTTP 通信设置 - host: 127.0.0.1 # 服务端监听地址 - port: 5700 # 服务端监听端口 + address: 0.0.0.0:5700 # HTTP监听地址 timeout: 5 # 反向 HTTP 超时时间, 单位秒,<5 时将被忽略 long-polling: # 长轮询拓展 enabled: false # 是否开启 @@ -300,8 +302,30 @@ func (c HTTPClient) Run() { if c.timeout < 5 { c.timeout = 5 } + rawAddress := c.addr + network, address := resolveURI(c.addr) + client := &http.Client{ + Timeout: time.Second * time.Duration(c.timeout), + Transport: &http.Transport{ + DialContext: func(_ context.Context, _, addr string) (net.Conn, error) { + if network == "unix" { + host, _, err := net.SplitHostPort(addr) + if err != nil { + host = addr + } + filepath, err := base64.RawURLEncoding.DecodeString(host) + if err == nil { + addr = string(filepath) + } + } + return net.Dial(network, addr) + }, + }, + } + c.addr = address // clean path + c.client = client + log.Infof("HTTP POST上报器已启动: %v", rawAddress) c.bot.OnEventPush(c.onBotPushEvent) - log.Infof("HTTP POST上报器已启动: %v", c.addr) } func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { @@ -313,7 +337,6 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { } } - client := http.Client{Timeout: time.Second * time.Duration(c.timeout)} header := make(http.Header) header.Set("X-Self-ID", strconv.FormatInt(c.bot.Client.Uin, 10)) header.Set("User-Agent", "CQHttp/4.15.0") @@ -338,7 +361,7 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { } req.Header = header - res, err = client.Do(req) + res, err = c.client.Do(req) if res != nil { //goland:noinspection GoDeferInLoop defer res.Body.Close() diff --git a/server/websocket.go b/server/websocket.go index df8c9a221..0c09c12a7 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -2,6 +2,7 @@ package server import ( "bytes" + "encoding/base64" "encoding/json" "fmt" "net" @@ -77,9 +78,7 @@ var upgrader = websocket.Upgrader{ const wsDefault = ` # 正向WS设置 - ws: # 正向WS服务器监听地址 - host: 127.0.0.1 - # 正向WS服务器监听端口 - port: 6700 + address: 0.0.0.0:8080 middlewares: <<: *default # 引用默认中间件 ` @@ -213,8 +212,25 @@ func runWSClient(b *coolq.CQBot, node yaml.Node) { } } -func (c *websocketClient) connect(typ, url string, conptr **wsConn) { - log.Infof("开始尝试连接到反向WebSocket %s服务器: %v", typ, url) +func resolveURI(addr string) (network, address string) { + network, address = "tcp", addr + uri, err := url.Parse(addr) + if err == nil && uri.Scheme != "" { + scheme, ext, _ := strings.Cut(uri.Scheme, "+") + if ext != "" { + network = ext + uri.Scheme = scheme // remove `+unix`/`+tcp4` + if ext == "unix" { + uri.Host = base64.StdEncoding.EncodeToString([]byte(uri.Host + uri.Path)) + } + address = uri.String() + } + } + return +} + +func (c *websocketClient) connect(typ, addr string, conptr **wsConn) { + log.Infof("开始尝试连接到反向WebSocket %s服务器: %v", typ, addr) header := http.Header{ "X-Client-Role": []string{typ}, "X-Self-ID": []string{strconv.FormatInt(c.bot.Client.Uin, 10)}, @@ -223,12 +239,30 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { if c.token != "" { header["Authorization"] = []string{"Token " + c.token} } - conn, _, err := websocket.DefaultDialer.Dial(url, header) // nolint + + network, address := resolveURI(addr) + dialer := websocket.Dialer{ + NetDial: func(_, addr string) (net.Conn, error) { + if network == "unix" { + host, _, err := net.SplitHostPort(addr) + if err != nil { + host = addr + } + filepath, err := base64.RawURLEncoding.DecodeString(host) + if err == nil { + addr = string(filepath) + } + } + return net.Dial(network, addr) // support unix socket transport + }, + } + + conn, _, err := dialer.Dial(address, header) // nolint if err != nil { - log.Warnf("连接到反向WebSocket %s服务器 %v 时出现错误: %v", typ, url, err) + log.Warnf("连接到反向WebSocket %s服务器 %v 时出现错误: %v", typ, addr, err) if c.reconnectInterval != 0 { time.Sleep(c.reconnectInterval) - c.connect(typ, url, conptr) + c.connect(typ, addr, conptr) } return } @@ -242,7 +276,7 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { } } - log.Infof("已连接到反向WebSocket %s服务器 %v", typ, url) + log.Infof("已连接到反向WebSocket %s服务器 %v", typ, addr) var wrappedConn *wsConn if conptr != nil && *conptr != nil { @@ -261,7 +295,7 @@ func (c *websocketClient) connect(typ, url string, conptr **wsConn) { } if typ != "Event" { - go c.listenAPI(typ, url, wrappedConn) + go c.listenAPI(typ, addr, wrappedConn) } } From d161f35c69d8c61779d755fa070e4870083c2b69 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 23 Mar 2022 21:25:31 +0800 Subject: [PATCH 339/612] server: fix unix socket path For #1415 --- server/websocket.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/websocket.go b/server/websocket.go index 0c09c12a7..f6000172f 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -221,7 +221,8 @@ func resolveURI(addr string) (network, address string) { network = ext uri.Scheme = scheme // remove `+unix`/`+tcp4` if ext == "unix" { - uri.Host = base64.StdEncoding.EncodeToString([]byte(uri.Host + uri.Path)) + uri.Host, uri.Path, _ = strings.Cut(uri.Path, ":") + uri.Host = base64.StdEncoding.EncodeToString([]byte(uri.Host)) } address = uri.String() } From ee9af5fa69c5e7b9f8d95492cc824eb4e45ae0d1 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 26 Mar 2022 14:39:28 +0800 Subject: [PATCH 340/612] server: refactor http post retry --- server/http.go | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/server/http.go b/server/http.go index 321634316..a47101150 100644 --- a/server/http.go +++ b/server/http.go @@ -350,36 +350,32 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { header.Set("X-API-Port", strconv.FormatInt(int64(c.apiPort), 10)) } + var req *http.Request var res *http.Response + var err error for i := uint64(0); i <= c.MaxRetries; i++ { // see https://stackoverflow.com/questions/31337891/net-http-http-contentlength-222-with-body-length-0 // we should create a new request for every single post trial - req, err := http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) + req, err = http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) if err != nil { log.Warnf("上报 Event 数据到 %v 时创建请求失败: %v", c.addr, err) return } req.Header = header - res, err = c.client.Do(req) - if res != nil { - //goland:noinspection GoDeferInLoop - defer res.Body.Close() - } - if err == nil { - break - } - if i < c.MaxRetries { - log.Warnf("上报 Event 数据到 %v 失败: %v 将进行第 %d 次重试", c.addr, err, i+1) - } else { - log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上线", e.JSONBytes(), c.addr, err) - return + if err != nil { + if i < c.MaxRetries { + log.Warnf("上报 Event 数据到 %v 失败: %v 将进行第 %d 次重试", c.addr, err, i+1) + } else { + log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上限", e.JSONBytes(), c.addr, err) + return + } + time.Sleep(time.Millisecond * time.Duration(c.RetriesInterval)) } - time.Sleep(time.Millisecond * time.Duration(c.RetriesInterval)) } + defer res.Body.Close() log.Debugf("上报Event数据 %s 到 %v", e.JSONBytes(), c.addr) - r, err := io.ReadAll(res.Body) if err != nil { return From d52237831544dcc9a4a84181b78bf16d259478bc Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 27 Mar 2022 22:53:00 +0800 Subject: [PATCH 341/612] all: optimize detected by go-perfguard --- coolq/api.go | 4 ++-- coolq/cqcode.go | 4 ++-- db/leveldb/writer.go | 5 ++--- global/log_hook.go | 3 ++- global/net.go | 4 ++-- internal/selfupdate/update.go | 2 +- server/daemon.go | 4 ++-- server/scf.go | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 486b543b8..44a52e87b 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1342,7 +1342,7 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) global if reply.Exists() { autoEscape := param.EnsureBool(operation.Get("auto_escape"), false) - at := operation.Get("at_sender").Bool() && !isAnonymous && msgType == "group" + at := !isAnonymous && operation.Get("at_sender").Bool() && msgType == "group" if at && reply.IsArray() { // 在 reply 数组头部插入CQ码 replySegments := make([]global.MSG, 0) @@ -1388,7 +1388,7 @@ func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) global if operation.Get("delete").Bool() { bot.CQDeleteMessage(int32(context.Get("message_id").Int())) } - if operation.Get("kick").Bool() && !isAnonymous { + if !isAnonymous && operation.Get("kick").Bool() { bot.CQSetGroupKick(context.Get("group_id").Int(), context.Get("user_id").Int(), "", operation.Get("reject_add_request").Bool()) } if operation.Get("ban").Bool() { diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 1e97f4b8e..6b9edac18 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -209,7 +209,7 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo case *message.DiceElement: m = global.MSG{ "type": "dice", - "data": map[string]string{"value": fmt.Sprint(o.Value)}, + "data": map[string]string{"value": strconv.FormatInt(int64(o.Value), 10)}, } case *message.MarketFaceElement: m = global.MSG{ @@ -1192,7 +1192,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy } return &LocalImageElement{File: fu.Path, URL: f}, nil } - if strings.HasPrefix(f, "base64") && !video { + if !video && strings.HasPrefix(f, "base64") { b, err := param.Base64DecodeString(strings.TrimPrefix(f, "base64://")) if err != nil { return nil, err diff --git a/db/leveldb/writer.go b/db/leveldb/writer.go index a8fe70755..baed92a25 100644 --- a/db/leveldb/writer.go +++ b/db/leveldb/writer.go @@ -2,7 +2,6 @@ package leveldb import ( "bytes" - "io" "github.com/Mrs4s/go-cqhttp/global" ) @@ -125,7 +124,7 @@ func (w *writer) bytes() []byte { out.uvarint(dataVersion) out.uvarint(uint64(w.strings.Len())) out.uvarint(uint64(w.data.Len())) - _, _ = io.Copy(&out, &w.strings) - _, _ = io.Copy(&out, &w.data) + _, _ = w.strings.WriteTo(&out) + _, _ = w.data.WriteTo(&out) return out.Bytes() } diff --git a/global/log_hook.go b/global/log_hook.go index b93f13077..44f4f1d83 100644 --- a/global/log_hook.go +++ b/global/log_hook.go @@ -195,7 +195,8 @@ func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) { buf.WriteString(colorReset) } - ret := append([]byte(nil), buf.Bytes()...) // copy buffer + ret := make([]byte, len(buf.Bytes())) + copy(ret, buf.Bytes()) // copy buffer return ret, nil } diff --git a/global/net.go b/global/net.go index ce74edbe9..c72000a2b 100644 --- a/global/net.go +++ b/global/net.go @@ -79,7 +79,7 @@ func DownloadFile(url, path string, limit int64, headers map[string]string) erro if limit > 0 && resp.ContentLength > limit { return ErrOverSize } - _, err = io.Copy(file, resp.Body) + _, err = file.ReadFrom(resp.Body) if err != nil { return err } @@ -107,7 +107,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int, return err } defer file.Close() - if _, err = io.Copy(file, s); err != nil { + if _, err = file.ReadFrom(s); err != nil { return err } return errUnsupportedMultiThreading diff --git a/internal/selfupdate/update.go b/internal/selfupdate/update.go index 5aa52a3a7..8771502bc 100644 --- a/internal/selfupdate/update.go +++ b/internal/selfupdate/update.go @@ -188,7 +188,7 @@ func fromStream(updateWith io.Reader) (err error, errRecover error) { } // We won't log this error, because it's always going to happen. defer func() { _ = fp.Close() }() - if _, err = io.Copy(fp, bufio.NewReader(updateWith)); err != nil { + if _, err = bufio.NewReader(updateWith).WriteTo(fp); err != nil { logrus.Errorf("Unable to copy data: %v\n", err) } diff --git a/server/daemon.go b/server/daemon.go index f2746d018..0a570b623 100644 --- a/server/daemon.go +++ b/server/daemon.go @@ -3,9 +3,9 @@ package server // daemon 功能写在这,目前仅支持了-d 作为后台运行参数,stop,start,restart这些功能目前看起来并不需要,可以通过api控制,后续需要的话再补全。 import ( - "fmt" "os" "os/exec" + "strconv" "strings" "github.com/Mrs4s/go-cqhttp/global" @@ -36,7 +36,7 @@ func Daemon() { log.Info("[PID] ", proc.Process.Pid) // pid写入到pid文件中,方便后续stop的时候kill - pidErr := savePid("go-cqhttp.pid", fmt.Sprintf("%d", proc.Process.Pid)) + pidErr := savePid("go-cqhttp.pid", strconv.FormatInt(int64(proc.Process.Pid), 10)) if pidErr != nil { log.Errorf("save pid file error: %v", pidErr) } diff --git a/server/scf.go b/server/scf.go index f41b3597f..7d95e7f01 100644 --- a/server/scf.go +++ b/server/scf.go @@ -54,7 +54,7 @@ func (l *lambdaResponseWriter) flush() error { buffer := global.NewBuffer() defer global.PutBuffer(buffer) body := utils.B2S(l.buf.Bytes()) - header := make(map[string]string) + header := make(map[string]string, len(l.header)) for k, v := range l.header { header[k] = v[0] } From fb33d93b311557d4115e22c216109fcb656d6873 Mon Sep 17 00:00:00 2001 From: ishkong <19740260+ishkong@users.noreply.github.com> Date: Mon, 28 Mar 2022 13:00:29 +0800 Subject: [PATCH 342/612] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20Update=20ffrmpe?= =?UTF-8?q?g=20screenshot=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ref: https://trac.ffmpeg.org/wiki/Create%20a%20thumbnail%20image%20every%20X%20seconds%20of%20the%20video Force a screenshot of the 0th second of the video --- global/codec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global/codec.go b/global/codec.go index 223c5337b..6a5f088cf 100644 --- a/global/codec.go +++ b/global/codec.go @@ -43,6 +43,6 @@ func EncodeMP4(src string, dst string) error { // -y 覆盖文件 // ExtractCover 获取给定视频文件的Cover func ExtractCover(src string, target string) error { - cmd := exec.Command("ffmpeg", "-i", src, "-y", "-r", "1", "-f", "image2", target) + cmd := exec.Command("ffmpeg", "-i", src, "-y", "-ss", "0", "-frames:v", "1", target) return errors.Wrap(cmd.Run(), "extract video cover failed") } From b4c3f2340e0d288d46633b006f90b94840364f86 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 28 Mar 2022 13:43:39 +0800 Subject: [PATCH 343/612] dep: update MiraiGo --- coolq/cqcode.go | 12 +++++------- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 6b9edac18..c9d3c5414 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -1119,14 +1119,12 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So _, _ = video.Seek(0, io.SeekStart) hash, _ := utils.ComputeMd5AndLength(video) cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash)+".mp4") - if global.PathExists(cacheFile) && (d["cache"] == "" || d["cache"] == "1") { - goto ok - } - err = global.EncodeMP4(v.File, cacheFile) - if err != nil { - return nil, err + if !(d["cache"] == "" || d["cache"] == "1") || !global.PathExists(cacheFile) { + err = global.EncodeMP4(v.File, cacheFile) + if err != nil { + return nil, err + } } - ok: v.File = cacheFile } return v, nil diff --git a/go.mod b/go.mod index 645550d10..574f3a38a 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220323044857-868828f3da83 + github.com/Mrs4s/MiraiGo v0.0.0-20220328053558-4f05838b6c18 github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 diff --git a/go.sum b/go.sum index cedc95790..1c8a29a04 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220323044857-868828f3da83 h1:2OA6ciqUytCaV7LQr/IvwCqRXJpc8bfvta63bn2XkHc= -github.com/Mrs4s/MiraiGo v0.0.0-20220323044857-868828f3da83/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= +github.com/Mrs4s/MiraiGo v0.0.0-20220328053558-4f05838b6c18 h1:v8nim0qO+OvtaQPD0tMxUAEUH1ktfV7hmo6o4k+9aw8= +github.com/Mrs4s/MiraiGo v0.0.0-20220328053558-4f05838b6c18/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= From 550c17c1844ce9b47700c1e9fb704402572b1f20 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 28 Mar 2022 15:30:10 +0800 Subject: [PATCH 344/612] dep: update MiraiGo --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 574f3a38a..94f0fee7b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220328053558-4f05838b6c18 + github.com/Mrs4s/MiraiGo v0.0.0-20220328072759-e2a42e542581 github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 diff --git a/go.sum b/go.sum index 1c8a29a04..59d720fed 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220328053558-4f05838b6c18 h1:v8nim0qO+OvtaQPD0tMxUAEUH1ktfV7hmo6o4k+9aw8= -github.com/Mrs4s/MiraiGo v0.0.0-20220328053558-4f05838b6c18/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= +github.com/Mrs4s/MiraiGo v0.0.0-20220328072759-e2a42e542581 h1:6RRK4cG73LD9VUDMgNAVu7cOJ5KjZ3R57gbAbbXy930= +github.com/Mrs4s/MiraiGo v0.0.0-20220328072759-e2a42e542581/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= From 0d291f79faaf65a24b38e634745433d4b2985150 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 4 Apr 2022 21:40:58 +0800 Subject: [PATCH 345/612] ci: add static check golangci-lint didn't release yet --- .github/workflows/golint.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index 6ab8830aa..fb9cc4d5b 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -19,6 +19,12 @@ jobs: with: version: latest + - name: Static Check + uses: dominikh/staticcheck-action@v1.2.0 + with: + install-go: false + version: "2022.1" + - name: Tests run: | go test $(go list ./...) From 4a27a60456cb808964502390c9b88dfeabb2527e Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 4 Apr 2022 21:56:18 +0800 Subject: [PATCH 346/612] coolq: fix static check --- coolq/bot.go | 2 ++ coolq/cqcode.go | 35 +++++++++++++++-------------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 407800f66..1d6a26153 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -361,6 +361,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen default: if session == nil && groupID != 0 { msg := bot.Client.SendGroupTempMessage(groupID, target, m) + //lint:ignore SA9003 if msg != nil { // nolint // todo(Mrs4s) // id = bot.InsertTempMessage(target, msg) @@ -372,6 +373,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen log.Errorf("发送临时会话消息失败: %v", err) break } + //lint:ignore SA9003 if msg != nil { // nolint // todo(Mrs4s) // id = bot.InsertTempMessage(target, msg) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index c9d3c5414..96e1b922e 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -142,16 +142,13 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo "data": map[string]string{"data": o.Content}, } case *message.AtElement: - if o.Target == 0 { - m = global.MSG{ - "type": "at", - "data": map[string]string{"qq": "all"}, - } - } else { - m = global.MSG{ - "type": "at", - "data": map[string]string{"qq": strconv.FormatUint(uint64(o.Target), 10)}, - } + target := "all" + if o.Target != 0 { + target = strconv.FormatUint(uint64(o.Target), 10) + } + m = global.MSG{ + "type": "at", + "data": map[string]string{"qq": target}, } case *message.RedBagElement: m = global.MSG{ @@ -236,9 +233,7 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo default: continue } - if m != nil { - r = append(r, m) - } + r = append(r, m) } return } @@ -315,9 +310,9 @@ func ToStringMessage(e []message.IMessageElement, source message.Source, isRaw . } arg += ",subType=" + strconv.FormatInt(int64(o.ImageBizType), 10) if ur { - write("[CQ:image,file=%s%s]", hex.EncodeToString(o.Md5)+".image", arg) + write("[CQ:image,file=%x.image%s]", o.Md5, arg) } else { - write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url), arg) + write("[CQ:image,file=%x.image,url=%s%s]", o.Md5, cqcode.EscapeValue(o.Url), arg) } case *message.FriendImageElement: var arg string @@ -325,9 +320,9 @@ func ToStringMessage(e []message.IMessageElement, source message.Source, isRaw . arg = ",type=flash" } if ur { - write("[CQ:image,file=%s%s]", hex.EncodeToString(o.Md5)+".image", arg) + write("[CQ:image,file=%x.image%s]", o.Md5, arg) } else { - write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url), arg) + write("[CQ:image,file=%x.image,url=%s%s]", cqcode.EscapeValue(o.Url), arg) } case *LocalImageElement: var arg string @@ -338,13 +333,13 @@ func ToStringMessage(e []message.IMessageElement, source message.Source, isRaw . if err == nil { m := md5.Sum(data) if ur { - write("[CQ:image,file=%s%s]", hex.EncodeToString(m[:])+".image", arg) + write("[CQ:image,file=%x.image%s]", m[:], arg) } else { - write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(m[:])+".image", cqcode.EscapeValue(o.URL), arg) + write("[CQ:image,file=%x.image,url=%s%s]", m[:], cqcode.EscapeValue(o.URL), arg) } } case *message.GuildImageElement: - write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", cqcode.EscapeValue(o.Url)) + write("[CQ:image,file=%x.image,url=%s]", o.Md5, cqcode.EscapeValue(o.Url)) case *message.DiceElement: write("[CQ:dice,value=%v]", o.Value) case *message.MarketFaceElement: From b22ff34cb7b04221d7c4fd916f8747d5ae881537 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 4 Apr 2022 22:02:10 +0800 Subject: [PATCH 347/612] coolq: fix static check --- coolq/bot.go | 4 ++-- coolq/cqcode.go | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 1d6a26153..00ec19cc8 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -361,7 +361,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen default: if session == nil && groupID != 0 { msg := bot.Client.SendGroupTempMessage(groupID, target, m) - //lint:ignore SA9003 + //lint:ignore SA9003 there is a todo if msg != nil { // nolint // todo(Mrs4s) // id = bot.InsertTempMessage(target, msg) @@ -373,7 +373,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen log.Errorf("发送临时会话消息失败: %v", err) break } - //lint:ignore SA9003 + //lint:ignore SA9003 there is a todo if msg != nil { // nolint // todo(Mrs4s) // id = bot.InsertTempMessage(target, msg) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 96e1b922e..8ea7aac0c 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -131,6 +131,7 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo e[i+1] = nil } } + continue case *message.TextElement: m = global.MSG{ "type": "text", @@ -472,9 +473,7 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { default: continue } - if m != nil { - r = append(r, m) - } + r = append(r, m) } return } From eaf34288de725ba4e01e93f078d94fd5509ef012 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 5 Apr 2022 21:49:03 +0800 Subject: [PATCH 348/612] fix forward --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 94f0fee7b..c23a94813 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220328072759-e2a42e542581 + github.com/Mrs4s/MiraiGo v0.0.0-20220405134734-9cb9e80d99d8 github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 diff --git a/go.sum b/go.sum index 59d720fed..425fad23a 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220328072759-e2a42e542581 h1:6RRK4cG73LD9VUDMgNAVu7cOJ5KjZ3R57gbAbbXy930= -github.com/Mrs4s/MiraiGo v0.0.0-20220328072759-e2a42e542581/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= +github.com/Mrs4s/MiraiGo v0.0.0-20220405134734-9cb9e80d99d8 h1:qkv7t94MNxwObbl1flZEZD5O0E8+R42XiZxOrppQNXc= +github.com/Mrs4s/MiraiGo v0.0.0-20220405134734-9cb9e80d99d8/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= From 18a091145af358687f653af1ed3b5322f7eba3d4 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 14 Apr 2022 21:53:00 +0800 Subject: [PATCH 349/612] style: go fmt ./... also delete mkrw.go, we can maintain this file by hand. --- coolq/cqcode/escape.go | 62 ++++------ db/leveldb/mkrw.go | 129 --------------------- db/leveldb/{database_gen.go => structs.go} | 2 - db/leveldb/writer.go | 21 +++- modules/config/config.go | 1 + 5 files changed, 40 insertions(+), 175 deletions(-) delete mode 100644 db/leveldb/mkrw.go rename db/leveldb/{database_gen.go => structs.go} (98%) diff --git a/coolq/cqcode/escape.go b/coolq/cqcode/escape.go index a9f0cdf40..cee90330b 100644 --- a/coolq/cqcode/escape.go +++ b/coolq/cqcode/escape.go @@ -3,15 +3,11 @@ package cqcode import "strings" -/*EscapeText 将字符串raw中部分字符转义 - -& -> & - -[ -> [ - -] -> ] - -*/ +// EscapeText 将字符串raw中部分字符转义 +// +// - & -> & +// - [ -> [ +// - ] -> ] func EscapeText(s string) string { count := strings.Count(s, "&") count += strings.Count(s, "[") @@ -47,31 +43,22 @@ func EscapeText(s string) string { return b.String() } -/*EscapeValue 将字符串value中部分字符转义 - -, -> , - -& -> & - -[ -> [ - -] -> ] - -*/ +// EscapeValue 将字符串value中部分字符转义 +// +// - , -> , +// - & -> & +// - [ -> [ +// - ] -> ] func EscapeValue(value string) string { ret := EscapeText(value) return strings.ReplaceAll(ret, ",", ",") } -/*UnescapeText 将字符串content中部分字符反转义 - -& -> & - -[ -> [ - -] -> ] - -*/ +// UnescapeText 将字符串content中部分字符反转义 +// +// - & -> & +// - [ -> [ +// - ] -> ] func UnescapeText(content string) string { ret := content ret = strings.ReplaceAll(ret, "[", "[") @@ -80,17 +67,12 @@ func UnescapeText(content string) string { return ret } -/*UnescapeValue 将字符串content中部分字符反转义 - -, -> , - -& -> & - -[ -> [ - -] -> ] - -*/ +// UnescapeValue 将字符串content中部分字符反转义 +// +// - , -> , +// - & -> & +// - [ -> [ +// - ] -> ] func UnescapeValue(content string) string { ret := strings.ReplaceAll(content, ",", ",") return UnescapeText(ret) diff --git a/db/leveldb/mkrw.go b/db/leveldb/mkrw.go deleted file mode 100644 index d7a7489cd..000000000 --- a/db/leveldb/mkrw.go +++ /dev/null @@ -1,129 +0,0 @@ -//go:build ignore - -package main - -import ( - "bytes" - "fmt" - "go/ast" - "go/format" - "go/parser" - "go/token" - "os" -) - -var output bytes.Buffer - -func fprintf(format string, args ...interface{}) { - _, _ = fmt.Fprintf(&output, format, args...) -} - -func main() { - f, _ := parser.ParseFile(token.NewFileSet(), "./../database.go", nil, 0) - fprintf("// Code generated by mkrw.go; DO NOT EDIT.\n\n") - fprintf("package leveldb\n\n") - fprintf("import \"github.com/Mrs4s/go-cqhttp/db\"\n\n") - ast.Inspect(f, func(node ast.Node) bool { - switch node := node.(type) { - case *ast.FuncDecl: - return false - case *ast.TypeSpec: - if !node.Name.IsExported() { - return false - } - x, ok := node.Type.(*ast.StructType) - if !ok { - return false - } - if x.Fields != nil && x.Fields.List != nil { - mkWrite(node) - mkRead(node) - } - } - return true - }) - out, err := format.Source(output.Bytes()) - if err != nil { - fmt.Println(string(output.Bytes())) - panic(err) - } - os.WriteFile("database_gen.go", out, 0o644) -} - -func typeName(typ ast.Expr) string { - switch typ := typ.(type) { - case *ast.Ident: - return typ.Name - case *ast.ArrayType: - if typ.Len != nil { - panic("unexpected array type") - } - return "[]" + typeName(typ.Elt) - case *ast.SelectorExpr: - return typeName(typ.X) + "." + typ.Sel.Name - } - panic("unexpected type") -} - -func mkWrite(node *ast.TypeSpec) { - typename := node.Name.String() - structType := node.Type.(*ast.StructType) - fprintf("func (w *writer) write%s(x *db.%s) {\n", typename, typename) - fprintf("if x == nil {\n") - fprintf("w.nil()\n") - fprintf("return\n") - fprintf("}\n") - fprintf("w.coder(coderStruct)\n") - for _, field := range structType.Fields.List { - switch typ := field.Type.(type) { - case *ast.Ident: - for _, name := range field.Names { - fprintf("w.%s(x.%s)\n", typ.Name, name.Name) - } - case *ast.ArrayType: - if typeName(typ) != "[]global.MSG" { - panic("unexpected array type") - } - for _, name := range field.Names { - fprintf("w.arrayMsg(x.%s)\n", name.Name) - } - case *ast.StarExpr: - for _, name := range field.Names { - fprintf("w.write%s(x.%s)\n", typeName(typ.X), name.Name) - } - } - } - fprintf("}\n\n") -} - -func mkRead(node *ast.TypeSpec) { - typename := node.Name.String() - structType := node.Type.(*ast.StructType) - fprintf(`func (r *reader) read%s() *db.%s { - coder := r.coder() - if coder == coderNil { - return nil - }`+"\n", typename, typename) - fprintf("x := &db.%s{}\n", typename) - for _, field := range structType.Fields.List { - switch typ := field.Type.(type) { - case *ast.Ident: - for _, name := range field.Names { - fprintf("x.%s = r.%s()\n", name.Name, typ.Name) - } - case *ast.ArrayType: - if typeName(typ) != "[]global.MSG" { - panic("unexpected array type") - } - for _, name := range field.Names { - fprintf("x.%s = r.arrayMsg()\n", name.Name) - } - case *ast.StarExpr: - for _, name := range field.Names { - fprintf("x.%s = r.read%s()\n", name.Name, typeName(typ.X)) - } - } - } - fprintf("return x\n") - fprintf("}\n\n") -} diff --git a/db/leveldb/database_gen.go b/db/leveldb/structs.go similarity index 98% rename from db/leveldb/database_gen.go rename to db/leveldb/structs.go index 929aeac1d..dfb7caade 100644 --- a/db/leveldb/database_gen.go +++ b/db/leveldb/structs.go @@ -1,5 +1,3 @@ -// Code generated by mkrw.go; DO NOT EDIT. - package leveldb import "github.com/Mrs4s/go-cqhttp/db" diff --git a/db/leveldb/writer.go b/db/leveldb/writer.go index baed92a25..6067ca102 100644 --- a/db/leveldb/writer.go +++ b/db/leveldb/writer.go @@ -23,12 +23,25 @@ func (w *intWriter) uvarint(x uint64) { } // writer implements the index write. +// // data format(use uvarint to encode integers): -// | version | string data length | index data length | string data | index data | +// +// - version +// - string data length +// - index data length +// - string data +// - index data +// // for string data part, each string is encoded as: -// | string length | string | -// for index data part, each value is encoded as: -// | coder | value | +// +// - string length +// - string +// +// for index data part, each object value is encoded as: +// +// - coder +// - value +// // * coder is the identifier of value's type. // * specially for string, it's value is the offset in string data part. type writer struct { diff --git a/modules/config/config.go b/modules/config/config.go index 158bd623b..23329d7f4 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -14,6 +14,7 @@ import ( ) // defaultConfig 默认配置文件 +// //go:embed default_config.yml var defaultConfig string From c3581c86319930beaaf332585b8f3c56bd242d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 23 Apr 2022 21:15:14 +0800 Subject: [PATCH 350/612] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E6=A6=82=E8=A7=88=E4=B8=BA=20kr=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=EF=BC=8C=E5=9B=A0=E4=B8=BAccwav=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=E6=97=A0=E6=B3=95=E6=9F=A5=E8=AF=A2=E4=B8=9C=E4=B8=9C?= =?UTF-8?q?=E5=86=9C=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 2 +- qqrobot/qinglong/summary_test.go | 2 +- .../2022-04-21-21-30-00.log | 95 +++++++++++++++++++ .../2021-12-13-09-30-00.log | 75 --------------- 4 files changed, 97 insertions(+), 77 deletions(-) create mode 100644 qqrobot/qinglong/test_data/log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log delete mode 100644 qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index b70245269..6c065b797 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -70,7 +70,7 @@ func QuerySummary(info *JdCookieInfo) string { return "" } - summaryDir := getPath("log/ccwav_QLScript2_jd_bean_change") + summaryDir := getPath("log/KingRan_KR_jd_bean_change_pro") logFiles, err := ioutil.ReadDir(summaryDir) if err != nil { logger.Errorf("read log dir failed, err=%v", err) diff --git a/qqrobot/qinglong/summary_test.go b/qqrobot/qinglong/summary_test.go index 374a43b52..85c364194 100644 --- a/qqrobot/qinglong/summary_test.go +++ b/qqrobot/qinglong/summary_test.go @@ -10,7 +10,7 @@ import ( // 2021/12/13 20:48 by fzls func Test_parseSummary(t *testing.T) { - logPath := getPath("log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log") + logPath := getPath("log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log") info := QueryCookieInfo("pin_1") assert.Contains(t, parseSummary(info, logPath), "测试账号-1") diff --git a/qqrobot/qinglong/test_data/log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log b/qqrobot/qinglong/test_data/log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log new file mode 100644 index 000000000..9c0e68496 --- /dev/null +++ b/qqrobot/qinglong/test_data/log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log @@ -0,0 +1,95 @@ +******开始查询【京东账号1】pin_1********* + +【账号1🆔】测试账号-1 +【账号信息】💎钻石Plus,京享值7617 +【今日京豆】收225豆 +【昨日京豆】收193豆 +【当前京豆】754豆(≈7.54元) +【今日喜豆】收0豆 +【昨日喜豆】收0豆 +【当前喜豆】0喜豆(≈0.00元) +【京喜牧场】1635枚鸡蛋 +【极速金币】135517币(≈13.55元) +【京东赚赚】157050币(≈15.71元) +【京东秒杀】3506币(≈3.51元) +【其他信息】领现金:11.34元 +【东东农场】海南贵妃芒果2斤(89%,2天) +【京喜工厂】AD钙乳酸菌(100% +【东东萌宠】全自动 晴雨伞(94%,4/5块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】13.55(总过期6.28)元 +【京喜红包】11.41(将过期5.30)元 +【极速红包】0.68(将过期0.40)元 +【京东红包】1.46(将过期0.58)元 +【全品类券】满5.01减5.00元(今日过期,仅限在京东极速版APP使用) + +jd_70a2bcede031c: 检测到NVJDC的一对一格式,瑞思拜~! +查询到Uid :UID_8cXGWvMc4Qloma3nHC0enspEmE1Z +正在发送一对一通知,请稍后... +处理完成,开始发送通知... +WxPusher 发送通知消息成功! + +******开始查询【京东账号2】pin_2********* + +❗️京东资产变动, 错误! + +【账号2🆔】测试账号-2 +【账号信息】💎钻石会员 +【今日京豆】收110豆 +【昨日京豆】收128豆 +【当前京豆】5613豆(≈56.13元) +【今日喜豆】收0豆 +【昨日喜豆】收0豆 +【当前喜豆】19喜豆(≈0.19元) +【京喜牧场】1423枚鸡蛋 +【极速金币】100822币(≈10.08元) +【京东赚赚】148050币(≈14.80元) +【京东秒杀】3520币(≈3.52元) +【其他信息】领现金:7.4元 +【东东农场】3斤猕猴桃(30%,178天) +【京喜工厂】泰国香米(85%,2天) +【东东工厂】九阳家用多功能早餐机(91%,剩711) +【东东萌宠】3层抽纸巾30包已可领取! +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】8.61(总过期0.39)元 +【京喜红包】6.64(将过期0.32)元 +【极速红包】0.20(将过期0.07)元 +【京东红包】1.77(将过期0.00)元 +【全品类券】满5.01减5.00元(今日过期,仅限在京东极速版APP使用) + +897796533: 检测到NVJDC的一对一格式,瑞思拜~! +查询到Uid :UID_Iw7A0Ao5dP4uPs5GQzf4PB7VCv4v +正在发送一对一通知,请稍后... +处理完成,开始发送通知... +WxPusher 发送通知消息成功! + +******开始查询【京东账号4】中文pin********* +【账号4🆔】中文名字 +【账号信息】🥉铜牌Plus,京享值5199 +【今日京豆】收99豆 +【昨日京豆】收125豆 +【当前京豆】7142豆(≈71.42元) +【今日喜豆】收0豆 +【昨日喜豆】收0豆 +【当前喜豆】0喜豆(≈0.00元) +【京喜牧场】862枚鸡蛋 +【极速金币】325712币(≈32.57元) +【京东赚赚】151200币(≈15.12元) +【京东秒杀】3534币(≈3.53元) +【其他信息】领现金:17.68元 +【东东农场】3盒龙虾尾(56%,36天) +【京喜工厂】真空压缩收纳袋(63%,14天) +【东东工厂】九阳家用多功能早餐机(29%,剩711) +【东东萌宠】3层抽纸巾30包(71%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】14.84(总过期6.19)元 +【京喜红包】12.00(将过期5.21)元 +【极速红包】0.16(将过期0.10)元 +【京东红包】2.68(将过期0.88)元 +【免运费券】5张(有效期至2022-05-07) + +开始发送通知... +通知标题: 京东资产变动 +正在处理账号Remark..... +处理完成,开始发送通知... +发送go-cqhttp通知调用API失败!! diff --git a/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log b/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log deleted file mode 100644 index e70d84272..000000000 --- a/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log +++ /dev/null @@ -1,75 +0,0 @@ -******开始查询【京东账号1】pin_1********* - -京东服务器返回空数据,将无法获取等级及VIP信息 -正在获取领现金任务签名... -领现金任务签名获取成功... -【账号1🆔】测试账号-1 -【账号信息】京享值7871 -【今日京豆】收203豆 -【昨日京豆】收771豆 -【当前京豆】18527豆(≈185.27元) -【京喜牧场】283枚鸡蛋 -【极速金币】255254币(≈25.53元) -【京东赚赚】65300币(≈6.53元) -【京东秒杀】7598币(≈7.60元) -【其他信息】领现金:5.78元 -【东东农场】4斤冰糖橙(97%,3天) -【京喜工厂】兑换超时,请重选商品! -【东东工厂】小米11青春版8GB+256GB(15%,剩29) -【东东萌宠】憨憨宠 猫砂除臭珠 (32%,3/4块) -🧧🧧🧧红包明细🧧🧧🧧 -【红包总额】49.83(总过期26.42)元 -【京喜红包】24.15(将过期19.52)元 -【京东红包】25.68(将过期6.90)元 - -******开始查询【京东账号2】pin_2********* -京东服务器返回空数据,将无法获取等级及VIP信息 -正在获取领现金任务签名... -领现金任务签名获取成功... -【账号2🆔】测试账号-2 -【账号信息】京享值6529 -【今日京豆】收133豆 -【昨日京豆】收310豆,支2豆 -【当前京豆】4184豆(≈41.84元) -【京喜牧场】160枚鸡蛋 -【极速金币】110142币(≈11.01元) -【京东赚赚】60200币(≈6.02元) -【京东秒杀】6854币(≈6.85元) -【其他信息】汪汪:19级,领现金:3.4元 -【东东农场】16枚鸡蛋(61%,9天) -【京喜工厂】鸡蛋挂面1kg(84%,1天) -【东东工厂】小米11青春版8GB+256GB(10%,剩29) -【东东萌宠】3层抽纸巾30包(14%,3/4块) -🧧🧧🧧红包明细🧧🧧🧧 -【红包总额】9.68(总过期4.03)元 -【京喜红包】8.88(将过期4.00)元 -【京东红包】0.80(将过期0.03)元 - -******开始查询【京东账号4】中文pin********* -京东服务器返回空数据,将无法获取等级及VIP信息 -正在获取领现金任务签名... -领现金任务签名获取成功... -【账号2🆔】中文名字 -【账号信息】京享值6529 -【今日京豆】收133豆 -【昨日京豆】收310豆,支2豆 -【当前京豆】4184豆(≈41.84元) -【京喜牧场】160枚鸡蛋 -【极速金币】110142币(≈11.01元) -【京东赚赚】60200币(≈6.02元) -【京东秒杀】6854币(≈6.85元) -【其他信息】汪汪:19级,领现金:3.4元 -【东东农场】16枚鸡蛋(61%,9天) -【京喜工厂】鸡蛋挂面1kg(84%,1天) -【东东工厂】小米11青春版8GB+256GB(10%,剩29) -【东东萌宠】3层抽纸巾30包(14%,3/4块) -🧧🧧🧧红包明细🧧🧧🧧 -【红包总额】9.68(总过期4.03)元 -【京喜红包】8.88(将过期4.00)元 -【京东红包】0.80(将过期0.03)元 - -开始发送通知... -通知标题: 京东资产变动 -正在处理账号Remark..... -处理完成,开始发送通知... -发送go-cqhttp通知调用API失败!! From e1e2523b7d5851676a793a7433d5ac037ac791a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 23 Apr 2022 21:31:54 +0800 Subject: [PATCH 351/612] =?UTF-8?q?fix:=20go=20mod=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=9C=89=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 30 ++++++++------- go.sum | 114 +++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 2a4f5d393..e02f295bc 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ replace github.com/Mrs4s/go-cqhttp => github.com/fzls/qq_robot_go v1.0.0-beta8 // 魔改后需要额外引入的依赖项,单独列出,避免后面又冲突 require ( - github.com/BurntSushi/toml v0.3.1 + github.com/BurntSushi/toml v1.1.0 github.com/fzls/logger v1.1.1 github.com/gookit/color v1.5.0 github.com/hashicorp/golang-lru v0.5.4 @@ -14,10 +14,7 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 ) -require ( - github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - gopkg.in/yaml.v2 v2.2.2 // indirect -) +require github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect // 以下为go-cqhttp原本的依赖 require ( @@ -36,12 +33,12 @@ require ( github.com/pkg/errors v0.9.1 github.com/segmentio/asm v1.1.3 github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/syndtr/goleveldb v1.0.0 github.com/tidwall/gjson v1.12.1 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 go.mongodb.org/mongo-driver v1.8.1 - golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 + golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b @@ -50,15 +47,20 @@ require ( require ( github.com/RomiChan/protobuf v0.0.0-20211223055824-048df49a8956 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fsnotify/fsnotify v1.5.3 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/gocq/rs v1.0.1 // indirect - github.com/golang/snappy v0.0.1 // indirect - github.com/google/go-cmp v0.5.5 // indirect - github.com/google/uuid v1.1.0 // indirect + github.com/golang/snappy v0.0.3 // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/kr/text v0.2.0 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/onsi/ginkgo v1.16.4 // indirect + github.com/onsi/gomega v1.18.1 // indirect github.com/pierrec/lz4/v4 v4.1.11 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect @@ -70,10 +72,12 @@ require ( github.com/xdg-go/stringprep v1.0.2 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect + golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect modernc.org/libc v1.8.1 // indirect modernc.org/mathutil v1.2.2 // indirect modernc.org/memory v1.0.4 // indirect diff --git a/go.sum b/go.sum index 567e6349c..6b79c0858 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII= github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Mrs4s/MiraiGo v0.0.0-20220117165008-3b461554b747 h1:i9IcyQOVc3eYHXAXgTAMsEdAcGkxpbjBEvVb87z50ZA= @@ -12,12 +12,19 @@ github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/x github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.3 h1:vNFpj2z7YIbwh2bw7x35sqYpp2wfuq+pivKbWG09B8c= +github.com/fsnotify/fsnotify v1.5.3/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= @@ -28,37 +35,52 @@ github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0Fn github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98 h1:NJDZEa7gibUa0w4tie8qKeQFKdeKFUbecWyQDPdRx40= github.com/gocq/qrcode v0.0.0-20211114040510-366b953fcd98/go.mod h1:E5TBHc60dsWtOL7sbXCb3P9i4xrj2J7Zm5sEJftIc1w= github.com/gocq/rs v1.0.1 h1:ng7nhXmnx3SnfM0DOqmbP6GmQp1xGwRG9XmBiLFDWuM= github.com/gocq/rs v1.0.1/go.mod h1:8oaQnRvqn1fMh8i5zsetgQo03OUXksJV1k+dpmExxcY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= @@ -72,11 +94,23 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/pierrec/lz4/v4 v4.1.11 h1:LVs17FAZJFOjgmJXl9Tf13WfLUvZq7/RjfEJrnwZ9OE= github.com/pierrec/lz4/v4 v4.1.11/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -97,9 +131,11 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 h1:2yISRb1sPRr0lUye5M/Co4uvyj7Y1JfF/7nq2vayF4g= @@ -126,32 +162,50 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHg github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.mongodb.org/mongo-driver v1.8.1 h1:OZE4Wni/SJlrcmSIBRYNzunX5TKxjrTS4jKSnA99oKU= go.mongodb.org/mongo-driver v1.8.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -159,34 +213,54 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 50d177edbc0d13249b701396ad2ecf4fa11c4882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 23 Apr 2022 21:33:32 +0800 Subject: [PATCH 352/612] fix: lint --- qqrobot/food.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/food.go b/qqrobot/food.go index 1c64f0415..a8448e75d 100644 --- a/qqrobot/food.go +++ b/qqrobot/food.go @@ -61,7 +61,7 @@ func (r *QQRobot) tryFetchMoreFoodImages(rule *Rule, foodSiteURL string) error { siteURL := strings.ReplaceAll(foodSiteURL, templateargsFoodpage, strconv.FormatInt(rule.SiteToFoodPage[foodSiteURL], 10)) resp, err := r.httpClient.Get(siteURL) if err != nil { - return errors.Errorf("get food site err=%v, siteURL=%v\n", err, siteURL) + return errors.Errorf("get food site err=%v, siteURL=%v", err, siteURL) } defer resp.Body.Close() From 1e0dedaff0ccfffbd07929317ed1e7d6f71ac6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 23 Apr 2022 22:07:28 +0800 Subject: [PATCH 353/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E5=A6=82=E4=BD=95=E5=90=88=E5=B9=B6=E4=B8=8A=E6=B8=B8?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go.mod b/go.mod index b0303c990..2b4e28ab7 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,10 @@ go 1.18 replace github.com/Mrs4s/go-cqhttp => github.com/fzls/qq_robot_go v1.0.0-beta8 // 魔改后需要额外引入的依赖项,单独列出,避免后面又冲突 +// note: 合并upstream的方法: +// 1. 打开github desktop,点击左侧的 History +// 2. 在 选择分支的输入框(提示有 Select branch to compare),选择 upstream/dev +// 3. 点击 左下方的合并按钮(Create a merge commit) require ( github.com/BurntSushi/toml v1.1.0 github.com/fzls/logger v1.1.1 From 4cbc1a15f89dbedc9cd412c04c5e8205101c89f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 23 Apr 2022 22:39:50 +0800 Subject: [PATCH 354/612] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8D=E6=96=B0?= =?UTF-8?q?=E7=9A=84=E4=BA=8B=E4=BB=B6=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 57 +++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 2bef9c260..b3ffb4527 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -189,31 +189,38 @@ func (r *QQRobot) settleTicker() { // RegisterHandlers 注册事件处理函数 func (r *QQRobot) RegisterHandlers() { // TODO: re: 添加其他事件的处理 @2021-10-02 05:35:37 - - r.cqBot.Client.OnGroupMessage(r.OnGroupMessage) - r.cqBot.Client.OnPrivateMessage(r.OnPrivateMessage) - // r.cqBot.Client.OnSelfPrivateMessage(rprivateMessageEvent) - // r.cqBot.Client.OnSelfGroupMessage(rgroupMessageEvent) - r.cqBot.Client.OnTempMessage(r.OnTempMessage) - // r.cqBot.Client.OnGroupMuted(rgroupMutedEvent) - // r.cqBot.Client.OnGroupMessageRecalled(rgroupRecallEvent) - // r.cqBot.Client.OnGroupNotify(rgroupNotifyEvent) - // r.cqBot.Client.OnFriendNotify(rfriendNotifyEvent) - // r.cqBot.Client.OnMemberSpecialTitleUpdated(rmemberTitleUpdatedEvent) - // r.cqBot.Client.OnFriendMessageRecalled(rfriendRecallEvent) - // r.cqBot.Client.OnReceivedOfflineFile(rofflineFileEvent) - // r.cqBot.Client.OnJoinGroup(rjoinGroupEvent) - // r.cqBot.Client.OnLeaveGroup(rleaveGroupEvent) - r.cqBot.Client.OnGroupMemberJoined(r.OnGroupMemberJoined) - // r.cqBot.Client.OnGroupMemberLeaved(rmemberLeaveEvent) - // r.cqBot.Client.OnGroupMemberPermissionChanged(rmemberPermissionChangedEvent) - // r.cqBot.Client.OnGroupMemberCardUpdated(rmemberCardUpdatedEvent) - // r.cqBot.Client.OnNewFriendRequest(rfriendRequestEvent) - // r.cqBot.Client.OnNewFriendAdded(rfriendAddedEvent) - // r.cqBot.Client.OnGroupInvited(rgroupInvitedEvent) - // r.cqBot.Client.OnUserWantJoinGroup(rgroupJoinReqEvent) - // r.cqBot.Client.OnOtherClientStatusChanged(rotherClientStatusChangedEvent) - // r.cqBot.Client.OnGroupDigest(rgroupEssenceMsg) + r.cqBot.Client.GroupMessageEvent.Subscribe(r.OnGroupMessage) + r.cqBot.Client.PrivateMessageEvent.Subscribe(r.OnPrivateMessage) + // if base.ReportSelfMessage { + // r.cqBot.Client.SelfPrivateMessageEvent.Subscribe(r.cqBot.privateMessageEvent) + // r.cqBot.Client.SelfGroupMessageEvent.Subscribe(r.cqBot.groupMessageEvent) + // } + r.cqBot.Client.TempMessageEvent.Subscribe(r.OnTempMessage) + // r.cqBot.Client.GuildService.OnGuildChannelMessage(r.cqBot.guildChannelMessageEvent) + // r.cqBot.Client.GuildService.OnGuildMessageReactionsUpdated(r.cqBot.guildMessageReactionsUpdatedEvent) + // r.cqBot.Client.GuildService.OnGuildMessageRecalled(r.cqBot.guildChannelMessageRecalledEvent) + // r.cqBot.Client.GuildService.OnGuildChannelUpdated(r.cqBot.guildChannelUpdatedEvent) + // r.cqBot.Client.GuildService.OnGuildChannelCreated(r.cqBot.guildChannelCreatedEvent) + // r.cqBot.Client.GuildService.OnGuildChannelDestroyed(r.cqBot.guildChannelDestroyedEvent) + // r.cqBot.Client.GroupMuteEvent.Subscribe(r.cqBot.groupMutedEvent) + // r.cqBot.Client.GroupMessageRecalledEvent.Subscribe(r.cqBot.groupRecallEvent) + // r.cqBot.Client.GroupNotifyEvent.Subscribe(r.cqBot.groupNotifyEvent) + // r.cqBot.Client.FriendNotifyEvent.Subscribe(r.cqBot.friendNotifyEvent) + // r.cqBot.Client.MemberSpecialTitleUpdatedEvent.Subscribe(r.cqBot.memberTitleUpdatedEvent) + // r.cqBot.Client.FriendMessageRecalledEvent.Subscribe(r.cqBot.friendRecallEvent) + // r.cqBot.Client.OfflineFileEvent.Subscribe(r.cqBot.offlineFileEvent) + // r.cqBot.Client.GroupJoinEvent.Subscribe(r.cqBot.joinGroupEvent) + // r.cqBot.Client.GroupLeaveEvent.Subscribe(r.cqBot.leaveGroupEvent) + r.cqBot.Client.GroupMemberJoinEvent.Subscribe(r.OnGroupMemberJoined) + // r.cqBot.Client.GroupMemberLeaveEvent.Subscribe(r.cqBot.memberLeaveEvent) + // r.cqBot.Client.GroupMemberPermissionChangedEvent.Subscribe(r.cqBot.memberPermissionChangedEvent) + // r.cqBot.Client.MemberCardUpdatedEvent.Subscribe(r.cqBot.memberCardUpdatedEvent) + // r.cqBot.Client.NewFriendRequestEvent.Subscribe(r.cqBot.friendRequestEvent) + // r.cqBot.Client.NewFriendEvent.Subscribe(r.cqBot.friendAddedEvent) + // r.cqBot.Client.GroupInvitedEvent.Subscribe(r.cqBot.groupInvitedEvent) + // r.cqBot.Client.UserWantJoinGroupEvent.Subscribe(r.cqBot.groupJoinReqEvent) + // r.cqBot.Client.OtherClientStatusChangedEvent.Subscribe(r.cqBot.otherClientStatusChangedEvent) + // r.cqBot.Client.GroupDigestEvent.Subscribe(r.cqBot.groupEssenceMsg) } // OnGroupMessage 处理群消息 From df43b70e4c8581cf8db441e2cade95786ec79af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sat, 23 Apr 2022 22:52:18 +0800 Subject: [PATCH 355/612] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=20go=20mod?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.sum | 81 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/go.sum b/go.sum index 425fad23a..762ef6da8 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Mrs4s/MiraiGo v0.0.0-20220405134734-9cb9e80d99d8 h1:qkv7t94MNxwObbl1flZEZD5O0E8+R42XiZxOrppQNXc= @@ -8,18 +10,25 @@ github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlz github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.3 h1:vNFpj2z7YIbwh2bw7x35sqYpp2wfuq+pivKbWG09B8c= +github.com/fsnotify/fsnotify v1.5.3/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4= +github.com/fzls/logger v1.1.1 h1:9ZteZN4pxh16MVsjlNgCurQb6qBDxKYDKKq7zifWHIU= +github.com/fzls/logger v1.1.1/go.mod h1:FBZoU1PuXNCQBNr19NZjUWCq1vV6/FoGwc3D9q6of/0= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -27,6 +36,8 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -36,12 +47,17 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= @@ -58,15 +74,22 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/pierrec/lz4/v4 v4.1.11 h1:LVs17FAZJFOjgmJXl9Tf13WfLUvZq7/RjfEJrnwZ9OE= github.com/pierrec/lz4/v4 v4.1.11/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -84,11 +107,16 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 h1:2yISRb1sPRr0lUye5M/Co4uvyj7Y1JfF/7nq2vayF4g= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 h1:m7y3kw3JfAmyIiz/mwWEcuV+WayjrxrfZpiqW94uJU4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -104,27 +132,36 @@ github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= -golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -134,47 +171,67 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 679f56d95ff67608039e4ad1ab618e9f90ec6b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 4 May 2022 21:15:47 +0800 Subject: [PATCH 356/612] =?UTF-8?q?feat:=20=E6=A6=82=E8=A7=88=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E4=BB=8E=E4=B8=A4=E4=B8=AA=E6=9D=A5=E6=BA=90=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E6=9F=A5=E8=AF=A2=EF=BC=8C=E5=8F=96=E5=85=B6=E4=B8=AD?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E4=B8=AA=E5=8C=85=E5=90=AB=E5=86=9C=E5=9C=BA?= =?UTF-8?q?=E7=9A=84=E7=BB=93=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 21 +++++- .../2021-12-13-09-30-00.log | 75 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 6c065b797..44281fbec 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -70,7 +70,26 @@ func QuerySummary(info *JdCookieInfo) string { return "" } - summaryDir := getPath("log/KingRan_KR_jd_bean_change_pro") + targetSummary := "" + + logFileList := []string{ + "log/ccwav_QLScript2_jd_bean_change", + "log/KingRan_KR_jd_bean_change_pro", + } + for _, logFile := range logFileList { + summary := querySummary(info, logFile) + if summary != "" && strings.Contains(summary, "东东农场") { + // 从多个日志来源中查询概览,优先包含 东东农场 信息的那个,因为有时候可能其中一个接口会无法查询农场的信息 + targetSummary = summary + break + } + } + + return targetSummary +} + +func querySummary(info *JdCookieInfo, logFile string) string { + summaryDir := getPath(logFile) logFiles, err := ioutil.ReadDir(summaryDir) if err != nil { logger.Errorf("read log dir failed, err=%v", err) diff --git a/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log b/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log new file mode 100644 index 000000000..e70d84272 --- /dev/null +++ b/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log @@ -0,0 +1,75 @@ +******开始查询【京东账号1】pin_1********* + +京东服务器返回空数据,将无法获取等级及VIP信息 +正在获取领现金任务签名... +领现金任务签名获取成功... +【账号1🆔】测试账号-1 +【账号信息】京享值7871 +【今日京豆】收203豆 +【昨日京豆】收771豆 +【当前京豆】18527豆(≈185.27元) +【京喜牧场】283枚鸡蛋 +【极速金币】255254币(≈25.53元) +【京东赚赚】65300币(≈6.53元) +【京东秒杀】7598币(≈7.60元) +【其他信息】领现金:5.78元 +【东东农场】4斤冰糖橙(97%,3天) +【京喜工厂】兑换超时,请重选商品! +【东东工厂】小米11青春版8GB+256GB(15%,剩29) +【东东萌宠】憨憨宠 猫砂除臭珠 (32%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】49.83(总过期26.42)元 +【京喜红包】24.15(将过期19.52)元 +【京东红包】25.68(将过期6.90)元 + +******开始查询【京东账号2】pin_2********* +京东服务器返回空数据,将无法获取等级及VIP信息 +正在获取领现金任务签名... +领现金任务签名获取成功... +【账号2🆔】测试账号-2 +【账号信息】京享值6529 +【今日京豆】收133豆 +【昨日京豆】收310豆,支2豆 +【当前京豆】4184豆(≈41.84元) +【京喜牧场】160枚鸡蛋 +【极速金币】110142币(≈11.01元) +【京东赚赚】60200币(≈6.02元) +【京东秒杀】6854币(≈6.85元) +【其他信息】汪汪:19级,领现金:3.4元 +【东东农场】16枚鸡蛋(61%,9天) +【京喜工厂】鸡蛋挂面1kg(84%,1天) +【东东工厂】小米11青春版8GB+256GB(10%,剩29) +【东东萌宠】3层抽纸巾30包(14%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】9.68(总过期4.03)元 +【京喜红包】8.88(将过期4.00)元 +【京东红包】0.80(将过期0.03)元 + +******开始查询【京东账号4】中文pin********* +京东服务器返回空数据,将无法获取等级及VIP信息 +正在获取领现金任务签名... +领现金任务签名获取成功... +【账号2🆔】中文名字 +【账号信息】京享值6529 +【今日京豆】收133豆 +【昨日京豆】收310豆,支2豆 +【当前京豆】4184豆(≈41.84元) +【京喜牧场】160枚鸡蛋 +【极速金币】110142币(≈11.01元) +【京东赚赚】60200币(≈6.02元) +【京东秒杀】6854币(≈6.85元) +【其他信息】汪汪:19级,领现金:3.4元 +【东东农场】16枚鸡蛋(61%,9天) +【京喜工厂】鸡蛋挂面1kg(84%,1天) +【东东工厂】小米11青春版8GB+256GB(10%,剩29) +【东东萌宠】3层抽纸巾30包(14%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】9.68(总过期4.03)元 +【京喜红包】8.88(将过期4.00)元 +【京东红包】0.80(将过期0.03)元 + +开始发送通知... +通知标题: 京东资产变动 +正在处理账号Remark..... +处理完成,开始发送通知... +发送go-cqhttp通知调用API失败!! From 859f40db837bc0ec60deb31458976a730a277b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 10 May 2022 16:50:38 +0800 Subject: [PATCH 357/612] feat: support binary encoding method base16384 --- coolq/cqcode.go | 8 ++++++++ global/fs.go | 7 +++++++ go.mod | 5 +++-- go.sum | 9 ++++++--- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 8ea7aac0c..870b0e66f 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -19,6 +19,7 @@ import ( "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" + b14 "github.com/fumiama/go-base16384" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" @@ -1191,6 +1192,13 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy } return &LocalImageElement{Stream: bytes.NewReader(b), URL: f}, nil } + if !video && strings.HasPrefix(f, "base16384") { + b, err := b14.UTF82UTF16BE(utils.S2B(strings.TrimPrefix(f, "base16384://"))) + if err != nil { + return nil, err + } + return &LocalImageElement{Stream: bytes.NewReader(b14.Decode(b)), URL: f}, nil + } rawPath := path.Join(global.ImagePath, f) if video { if strings.HasSuffix(f, ".video") { diff --git a/global/fs.go b/global/fs.go index 3c197712a..ccfac88df 100644 --- a/global/fs.go +++ b/global/fs.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/Mrs4s/MiraiGo/utils" + b14 "github.com/fumiama/go-base16384" log "github.com/sirupsen/logrus" "github.com/Mrs4s/go-cqhttp/internal/param" @@ -92,6 +93,12 @@ func FindFile(file, cache, p string) (data []byte, err error) { if err != nil { return nil, err } + case strings.HasPrefix(file, "base16384"): + data, err = b14.UTF82UTF16BE(utils.S2B(strings.TrimPrefix(file, "base16384://"))) + if err != nil { + return nil, err + } + data = b14.Decode(data) case strings.HasPrefix(file, "file"): var fu *url.URL fu, err = url.Parse(file) diff --git a/go.mod b/go.mod index c23a94813..a27206b32 100644 --- a/go.mod +++ b/go.mod @@ -7,13 +7,14 @@ require ( github.com/Mrs4s/MiraiGo v0.0.0-20220405134734-9cb9e80d99d8 github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc + github.com/fumiama/go-base16384 v1.5.2 github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible github.com/mattn/go-colorable v0.1.12 github.com/pkg/errors v0.9.1 github.com/segmentio/asm v1.1.3 github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tidwall/gjson v1.14.0 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 @@ -48,7 +49,7 @@ require ( golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/text v0.3.7 // indirect modernc.org/libc v1.8.1 // indirect modernc.org/mathutil v1.2.2 // indirect modernc.org/memory v1.0.4 // indirect diff --git a/go.sum b/go.sum index 425fad23a..5b57b6dd5 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fumiama/go-base16384 v1.5.2 h1:cbxXTcDH92PNgG7bEBwiCEoWb5O+nwZKxKOG94ilFo8= +github.com/fumiama/go-base16384 v1.5.2/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= @@ -85,8 +87,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= @@ -151,8 +154,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 1f3573636fd38e3135a7fc0df0474381918dbd1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 11 May 2022 23:24:00 +0800 Subject: [PATCH 358/612] =?UTF-8?q?feat:=20structcheck=E8=A7=84=E5=88=99?= =?UTF-8?q?=E6=9A=82=E4=B8=8D=E6=94=AF=E6=8C=811.18=EF=BC=8C=E5=85=88?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .golangci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 31ba65c49..48b81162f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -38,7 +38,7 @@ linters: #- nolintlint #- rowserrcheck #- staticcheck - - structcheck + #- structcheck #- stylecheck - unconvert #- unparam From a72d658c5263dbba849ef3459b30fc4315e575f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 12 May 2022 18:02:42 +0800 Subject: [PATCH 359/612] =?UTF-8?q?fix:=20=E5=BD=93=E6=89=80=E6=9C=89?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=A7=A3=E6=9E=90=E7=BB=93=E6=9E=9C=E9=83=BD?= =?UTF-8?q?=E4=B8=8D=E5=8C=85=E5=90=AB=E5=86=9C=E5=9C=BA=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E5=BA=94=E8=BF=94=E5=9B=9E=E6=9C=80=E5=90=8E?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E4=B8=8D=E4=B8=BA=E7=A9=BA=E7=9A=84=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/query.go | 8 +- qqrobot/qinglong/query_test.go | 80 ++++++++++--------- qqrobot/qinglong/test_data/config/env.sh | 2 +- qqrobot/qinglong/test_data/db/env.db | 1 + .../2022-04-21-21-30-00.log | 24 ++++++ .../2021-12-13-09-30-00.log | 22 +++++ 6 files changed, 95 insertions(+), 42 deletions(-) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 44281fbec..6fa255eda 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -64,7 +64,7 @@ func QueryChartPath(info *JdCookieInfo) string { return path } -// QuerySummary 查询账号对应的最新统计信息 +// QuerySummary 查询账号对应的最新统计信息,返回第一个包含农场信息的结果,如果都不包含,则返回最后一个不为空的结果 func QuerySummary(info *JdCookieInfo) string { if info == nil { return "" @@ -78,9 +78,11 @@ func QuerySummary(info *JdCookieInfo) string { } for _, logFile := range logFileList { summary := querySummary(info, logFile) - if summary != "" && strings.Contains(summary, "东东农场") { - // 从多个日志来源中查询概览,优先包含 东东农场 信息的那个,因为有时候可能其中一个接口会无法查询农场的信息 + if summary != "" { targetSummary = summary + } + if strings.Contains(summary, "东东农场") { + // 从多个日志来源中查询概览,优先包含 东东农场 信息的那个,因为有时候可能其中一个接口会无法查询农场的信息 break } } diff --git a/qqrobot/qinglong/query_test.go b/qqrobot/qinglong/query_test.go index 1719e3611..7b5d746f5 100644 --- a/qqrobot/qinglong/query_test.go +++ b/qqrobot/qinglong/query_test.go @@ -1,67 +1,71 @@ package qinglong import ( - "net/url" - "path/filepath" - "testing" + "net/url" + "path/filepath" + "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) // 2021/12/13 20:33 by fzls func TestQueryCookieInfo(t *testing.T) { - var info *JdCookieInfo + var info *JdCookieInfo - // 无参数 - info = QueryCookieInfo("") - assert.Nil(t, info) + // 无参数 + info = QueryCookieInfo("") + assert.Nil(t, info) - // pin - info = QueryCookieInfo("pin_1") - assert.NotNil(t, info) + // pin + info = QueryCookieInfo("pin_1") + assert.NotNil(t, info) - // 备注 - info = QueryCookieInfo("测试账号-1") - assert.NotNil(t, info) + // 备注 + info = QueryCookieInfo("测试账号-1") + assert.NotNil(t, info) - // 仅env中存在的账号,使用pin - info = QueryCookieInfo("pin_3") - assert.NotNil(t, info) + // 仅env中存在的账号,使用pin + info = QueryCookieInfo("pin_3") + assert.NotNil(t, info) - // 不存在的账号 - info = QueryCookieInfo("not exists") - assert.Nil(t, info) + // 不存在的账号 + info = QueryCookieInfo("not exists") + assert.Nil(t, info) } func TestQueryChartPath(t *testing.T) { - info := QueryCookieInfo("pin_1") - chartPath := QueryChartPath(info) - expected, _ := filepath.Abs(getPath("log/.bean_chart/chart_pin_1.jpeg")) - assert.Equal(t, expected, chartPath) + info := QueryCookieInfo("pin_1") + chartPath := QueryChartPath(info) + expected, _ := filepath.Abs(getPath("log/.bean_chart/chart_pin_1.jpeg")) + assert.Equal(t, expected, chartPath) } func TestQuerySummary(t *testing.T) { - info := QueryCookieInfo("pin_1") - assert.NotEmpty(t, QuerySummary(info)) + info := QueryCookieInfo("pin_1") + assert.NotEmpty(t, QuerySummary(info)) - info = QueryCookieInfo("pin_3") - assert.Empty(t, QuerySummary(info)) + info = QueryCookieInfo("pin_3") + assert.Empty(t, QuerySummary(info)) + + // 即使所有日志都不包含农场,也应返回最后一个不为空的日志解析结果 + info = QueryCookieInfo(url.QueryEscape("pin_5")) + assert.NotEmpty(t, QuerySummary(info)) } func TestQueryCookieExpired(t *testing.T) { - info := QueryCookieInfo("pin_1") - assert.NotEmpty(t, QueryCookieExpired(info)) + info := QueryCookieInfo("pin_1") + assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("pin_2") - assert.NotEmpty(t, QueryCookieExpired(info)) + info = QueryCookieInfo("pin_2") + assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("pin_3") - assert.NotEmpty(t, QueryCookieExpired(info)) + info = QueryCookieInfo("pin_3") + assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo(url.QueryEscape("中文pin")) - assert.NotEmpty(t, QueryCookieExpired(info)) + info = QueryCookieInfo(url.QueryEscape("中文pin")) + assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("pin_99999") - assert.Empty(t, QueryCookieExpired(info)) + info = QueryCookieInfo("pin_99999") + assert.Empty(t, QueryCookieExpired(info)) } diff --git a/qqrobot/qinglong/test_data/config/env.sh b/qqrobot/qinglong/test_data/config/env.sh index f274c197f..74d826f3b 100644 --- a/qqrobot/qinglong/test_data/config/env.sh +++ b/qqrobot/qinglong/test_data/config/env.sh @@ -1 +1 @@ -export JD_COOKIE="pt_key=key_1;pt_pin=pin_1;&pt_key=key_2;pt_pin=pin_2;&pt_key=key_3;pt_pin=pin_3;&pt_key=key_4;pt_pin=%E4%B8%AD%E6%96%87pin;" +export JD_COOKIE="pt_key=key_1;pt_pin=pin_1;&pt_key=key_2;pt_pin=pin_2;&pt_key=key_3;pt_pin=pin_3;&pt_key=key_4;pt_pin=%E4%B8%AD%E6%96%87pin;&pt_key=key_5;pt_pin=pin_5;" diff --git a/qqrobot/qinglong/test_data/db/env.db b/qqrobot/qinglong/test_data/db/env.db index a7788bfe9..a8ead0c46 100644 --- a/qqrobot/qinglong/test_data/db/env.db +++ b/qqrobot/qinglong/test_data/db/env.db @@ -1,3 +1,4 @@ {"value":"pt_key=key_1;pt_pin=pin_1;","_id":"1","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"remark=测试账号-1;"} {"value":"pt_key=key_2;pt_pin=pin_2;","_id":"2","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"测试账号-2"} {"value":"pt_key=key_4;pt_pin=%E4%B8%AD%E6%96%87pin;","_id":"2","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"中文名字"} +{"value":"pt_key=key_5;pt_pin=pin_5;","_id":"2","created":1636536508075,"status":1,"timestamp":"Wed Nov 10 2021 17:28:28 GMT+0800 (中国标准时间)","position":74.5058059617877,"name":"JD_COOKIE","remarks":"不包含农场"} diff --git a/qqrobot/qinglong/test_data/log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log b/qqrobot/qinglong/test_data/log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log index 9c0e68496..f79a44e19 100644 --- a/qqrobot/qinglong/test_data/log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log +++ b/qqrobot/qinglong/test_data/log/KingRan_KR_jd_bean_change_pro/2022-04-21-21-30-00.log @@ -88,6 +88,30 @@ WxPusher 发送通知消息成功! 【京东红包】2.68(将过期0.88)元 【免运费券】5张(有效期至2022-05-07) +******开始查询【京东账号5】pin_5********* +【账号4🆔】不包含农场 +【账号信息】🥉铜牌Plus,京享值5199 +【今日京豆】收99豆 +【昨日京豆】收125豆 +【当前京豆】7142豆(≈71.42元) +【今日喜豆】收0豆 +【昨日喜豆】收0豆 +【当前喜豆】0喜豆(≈0.00元) +【京喜牧场】862枚鸡蛋 +【极速金币】325712币(≈32.57元) +【京东赚赚】151200币(≈15.12元) +【京东秒杀】3534币(≈3.53元) +【其他信息】领现金:17.68元 +【京喜工厂】真空压缩收纳袋(63%,14天) +【东东工厂】九阳家用多功能早餐机(29%,剩711) +【东东萌宠】3层抽纸巾30包(71%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】14.84(总过期6.19)元 +【京喜红包】12.00(将过期5.21)元 +【极速红包】0.16(将过期0.10)元 +【京东红包】2.68(将过期0.88)元 +【免运费券】5张(有效期至2022-05-07) + 开始发送通知... 通知标题: 京东资产变动 正在处理账号Remark..... diff --git a/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log b/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log index e70d84272..12d225d73 100644 --- a/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log +++ b/qqrobot/qinglong/test_data/log/ccwav_QLScript2_jd_bean_change/2021-12-13-09-30-00.log @@ -68,6 +68,28 @@ 【京喜红包】8.88(将过期4.00)元 【京东红包】0.80(将过期0.03)元 +******开始查询【京东账号5】pin_5********* +京东服务器返回空数据,将无法获取等级及VIP信息 +正在获取领现金任务签名... +领现金任务签名获取成功... +【账号2🆔】不包含农场 +【账号信息】京享值6529 +【今日京豆】收133豆 +【昨日京豆】收310豆,支2豆 +【当前京豆】4184豆(≈41.84元) +【京喜牧场】160枚鸡蛋 +【极速金币】110142币(≈11.01元) +【京东赚赚】60200币(≈6.02元) +【京东秒杀】6854币(≈6.85元) +【其他信息】汪汪:19级,领现金:3.4元 +【京喜工厂】鸡蛋挂面1kg(84%,1天) +【东东工厂】小米11青春版8GB+256GB(10%,剩29) +【东东萌宠】3层抽纸巾30包(14%,3/4块) +🧧🧧🧧红包明细🧧🧧🧧 +【红包总额】9.68(总过期4.03)元 +【京喜红包】8.88(将过期4.00)元 +【京东红包】0.80(将过期0.03)元 + 开始发送通知... 通知标题: 京东资产变动 正在处理账号Remark..... From 71f1a6108f04bb1c90f17cb35ff59ad11f6fe446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 13 May 2022 19:29:00 +0800 Subject: [PATCH 360/612] =?UTF-8?q?feat:=20=E8=BF=87=E6=9C=9F=E6=8C=87?= =?UTF-8?q?=E4=BB=A4=E8=B7=B3=E8=BF=87=E6=9C=AA=E5=AE=9E=E9=99=85=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E7=9A=84=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qinglong/expired.go | 18 ++++++++++++------ qqrobot/qinglong/expired_test.go | 2 +- qqrobot/qinglong/query.go | 18 ++++++++++++------ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/qqrobot/qinglong/expired.go b/qqrobot/qinglong/expired.go index 2e7386445..90b77f3a0 100644 --- a/qqrobot/qinglong/expired.go +++ b/qqrobot/qinglong/expired.go @@ -7,17 +7,23 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) -func parseCookieExpired(info *JdCookieInfo, logFilePath string) (result string, isLogComplete bool) { +func parseCookieExpired(info *JdCookieInfo, logFilePath string) (result string, isLogComplete bool, skipThis bool) { if !global.PathExists(logFilePath) { - return "", false + return "", false, true } contentBytes, err := ioutil.ReadFile(logFilePath) if err != nil { - return "", false + return "", false, true } content := string(contentBytes) + // 跳过未实际执行的日志 + // Error: Cannot find module '/ql/scripts/ccwav_QLScript2_jd_CheckCK.js' + if strings.Contains(content, "Error: Cannot find module") { + return "", false, true + } + // 判断这个日志是否运行完整 isLogComplete = strings.Contains(content, "开始发送通知...") @@ -28,18 +34,18 @@ func parseCookieExpired(info *JdCookieInfo, logFilePath string) (result string, // 定位前缀 prefixIndex := strings.Index(content, prefix) if prefixIndex == -1 { - return "", isLogComplete + return "", isLogComplete, false } prefixIndex += len(prefixToRemove) // 定位后缀 suffixIndex := strings.Index(content[prefixIndex:], suffix) if suffixIndex == -1 { - return "", isLogComplete + return "", isLogComplete, false } suffixIndex += prefixIndex result = content[prefixIndex:suffixIndex] - return result, isLogComplete + return result, isLogComplete, false } diff --git a/qqrobot/qinglong/expired_test.go b/qqrobot/qinglong/expired_test.go index a109e826d..2b01a8921 100644 --- a/qqrobot/qinglong/expired_test.go +++ b/qqrobot/qinglong/expired_test.go @@ -9,7 +9,7 @@ import ( func Test_parseCookieExpired(t *testing.T) { getResult := func(info *JdCookieInfo, logFilePath string) string { - result, _ := parseCookieExpired(info, logFilePath) + result, _, _ := parseCookieExpired(info, logFilePath) return result } diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 6fa255eda..7984e5cb0 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -141,17 +141,23 @@ func QueryCookieExpired(info *JdCookieInfo) string { }) // 因为有可能最新的日志还在处理中,因此逆序搜索一定数目的日志,直到搜索到为止 - for idx, logFile := range logFiles { - if idx >= maxExpireCheckCount { - break - } - - result, isLogComplete := parseCookieExpired(info, filepath.Join(checkCookieDir, logFile.Name())) + processedValidLogCount := 0 + for _, logFile := range logFiles { + result, isLogComplete, skipThis := parseCookieExpired(info, filepath.Join(checkCookieDir, logFile.Name())) if result != "" { return appendLogFileInfo(result, logFile.Name()) } else if isLogComplete { // 没有解析到该账号,但该日志是完整日志,说明这个账号未过期 return "" + } else if skipThis { + // 如果内部判定需要跳过该文件,则不计数,继续尝试下一个 + } else { + processedValidLogCount++ + } + + // 仅尝试该数目个正常的日志 + if processedValidLogCount >= maxExpireCheckCount { + break } } From df3168ffd3df488d16872c3ca110517213f58e2b Mon Sep 17 00:00:00 2001 From: RTAkland <79452994+MarkusJoe@users.noreply.github.com> Date: Wed, 18 May 2022 22:05:02 +0800 Subject: [PATCH 361/612] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E9=94=99?= =?UTF-8?q?=E5=88=AB=E5=AD=97=20(#1471)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第346行的文字: `线` => `限` --- server/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/http.go b/server/http.go index 7d696c529..6971f05c5 100644 --- a/server/http.go +++ b/server/http.go @@ -343,7 +343,7 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { if i < c.MaxRetries { log.Warnf("上报 Event 数据到 %v 失败: %v 将进行第 %d 次重试", c.addr, err, i+1) } else { - log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上线", e.JSONBytes(), c.addr, err) + log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上限", e.JSONBytes(), c.addr, err) return } time.Sleep(time.Millisecond * time.Duration(c.RetriesInterval)) From c141501ae5a14bd600c70d6c86ffc9760dcd8dc5 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 23 May 2022 11:13:44 +0800 Subject: [PATCH 362/612] all: update MiraiGo and some minor changes --- cmd/gocq/main.go | 1 + coolq/api.go | 18 ++++++++---------- coolq/bot.go | 2 +- go.mod | 2 +- server/http.go | 6 +++--- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index c62008038..1cb85caee 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -365,6 +365,7 @@ func PasswordHashDecrypt(encryptedPasswordHash string, key []byte) ([]byte, erro func newClient() *client.QQClient { c := client.NewClientEmpty() + c.UseFragmentMessage = base.ForceFragmented c.OnServerUpdated(func(bot *client.QQClient, e *client.ServerUpdatedEvent) bool { if !base.UseSSOAddress { log.Infof("收到服务器地址更新通知, 根据配置文件已忽略.") diff --git a/coolq/api.go b/coolq/api.go index 44a52e87b..ece707655 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1470,18 +1470,18 @@ func (bot *CQBot) CQDownloadFile(url string, headers gjson.Result, threadCount i h := map[string]string{} if headers.IsArray() { for _, sub := range headers.Array() { - str := strings.SplitN(sub.String(), "=", 2) - if len(str) == 2 { - h[str[0]] = str[1] + first, second, ok := strings.Cut(sub.String(), "=") + if ok { + h[first] = second } } } if headers.Type == gjson.String { lines := strings.Split(headers.String(), "\r\n") for _, sub := range lines { - str := strings.SplitN(sub, "=", 2) - if len(str) == 2 { - h[str[0]] = str[1] + first, second, ok := strings.Cut(sub, "=") + if ok { + h[first] = second } } } @@ -1772,12 +1772,10 @@ func (bot *CQBot) CQSetGroupAnonymousBan(groupID int64, flag string, duration in return Failed(100, "INVALID_FLAG", "无效的flag") } if g := bot.Client.FindGroup(groupID); g != nil { - s := strings.SplitN(flag, "|", 2) - if len(s) != 2 { + id, nick, ok := strings.Cut(flag, "|") + if !ok { return Failed(100, "INVALID_FLAG", "无效的flag") } - id := s[0] - nick := s[1] if err := g.MuteAnonymous(id, nick, duration); err != nil { log.Warnf("anonymous ban error: %v", err) return Failed(100, "CALL_API_ERROR", err.Error()) diff --git a/coolq/bot.go b/coolq/bot.go index 00ec19cc8..aea4845b9 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -289,7 +289,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int } m.Elements = newElem bot.checkMedia(newElem, groupID) - ret := bot.Client.SendGroupMessage(groupID, m, base.ForceFragmented) + ret := bot.Client.SendGroupMessage(groupID, m) if ret == nil || ret.Id == -1 { log.Warnf("群消息发送失败: 账号可能被风控.") return -1 diff --git a/go.mod b/go.mod index c23a94813..58c17493f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220405134734-9cb9e80d99d8 + github.com/Mrs4s/MiraiGo v0.0.0-20220523030651-b28ec81f546e github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 diff --git a/server/http.go b/server/http.go index a47101150..710610dee 100644 --- a/server/http.go +++ b/server/http.go @@ -212,9 +212,9 @@ func checkAuth(req *http.Request, token string) int { if auth == "" { auth = req.URL.Query().Get("access_token") } else { - authN := strings.SplitN(auth, " ", 2) - if len(authN) == 2 { - auth = authN[1] + _, after, ok := strings.Cut(auth, " ") + if ok { + auth = after } } From cfa35b6b0a3441786e967b58db0db4b6a66184a5 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 23 May 2022 11:15:51 +0800 Subject: [PATCH 363/612] dep: update go.sum --- go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index 425fad23a..d1ce95314 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220405134734-9cb9e80d99d8 h1:qkv7t94MNxwObbl1flZEZD5O0E8+R42XiZxOrppQNXc= -github.com/Mrs4s/MiraiGo v0.0.0-20220405134734-9cb9e80d99d8/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= +github.com/Mrs4s/MiraiGo v0.0.0-20220523030651-b28ec81f546e h1:OJu+7G+U9dRjE0VDvZI0YVbbPRspuOeOmUiYR9jOo5A= +github.com/Mrs4s/MiraiGo v0.0.0-20220523030651-b28ec81f546e/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= From 111a5506b9de0a1b7fbb28ee812133ae868cf200 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 24 May 2022 15:07:24 +0800 Subject: [PATCH 364/612] dep: update MiraiGo --- coolq/api.go | 2 -- coolq/converter.go | 2 +- go.mod | 6 +++--- go.sum | 12 ++++++------ 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index ece707655..23b3285d9 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -407,7 +407,6 @@ func (bot *CQBot) CQGetGroupList(noCache bool) global.MSG { gs = append(gs, global.MSG{ "group_id": g.Code, "group_name": g.Name, - "group_memo": g.Memo, "group_create_time": g.GroupCreateTime, "group_level": g.GroupLevel, "max_member_count": g.MaxMemberCount, @@ -449,7 +448,6 @@ func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool) global.MSG { return OK(global.MSG{ "group_id": group.Code, "group_name": group.Name, - "group_memo": group.Memo, "group_create_time": group.GroupCreateTime, "group_level": group.GroupLevel, "max_member_count": group.MaxMemberCount, diff --git a/coolq/converter.go b/coolq/converter.go index fbe3d4c5b..43be08ee4 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -41,7 +41,7 @@ func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG "role": role, "unfriendly": false, "title": m.SpecialTitle, - "title_expire_time": m.SpecialTitleExpireTime, + "title_expire_time": 0, "card_changeable": false, } } diff --git a/go.mod b/go.mod index 58c17493f..5b70bf7b2 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220523030651-b28ec81f546e - github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c + github.com/Mrs4s/MiraiGo v0.0.0-20220524053130-007f228e1092 + github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible @@ -25,7 +25,7 @@ require ( ) require ( - github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 // indirect + github.com/RomiChan/protobuf v0.1.1-0.20220524030518-4f349493f9da // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect diff --git a/go.sum b/go.sum index d1ce95314..1be6fb9bb 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220523030651-b28ec81f546e h1:OJu+7G+U9dRjE0VDvZI0YVbbPRspuOeOmUiYR9jOo5A= -github.com/Mrs4s/MiraiGo v0.0.0-20220523030651-b28ec81f546e/go.mod h1:APw03y3A3Qp5In9gI/S9m7DBJkWsd6FQMV+fB636bmU= -github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896 h1:UFAqSbH6VqW5mEzQV2HVB7+p3k9JfTbidWJ/9F15yz0= -github.com/RomiChan/protobuf v0.0.0-20220318113238-d8a99598f896/go.mod h1:CKKOWC7mBxd36zxsCB1V8DTrwlTNRQvkSVbYqyUiGEE= -github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c h1:zHWyqx7A71A/+mlzthPVcVrNGuTPyTpCW3meUPtaULU= -github.com/RomiChan/syncx v0.0.0-20220320130821-c88644afda9c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= +github.com/Mrs4s/MiraiGo v0.0.0-20220524053130-007f228e1092 h1:8TDeBGcywkNWXAmAVVL7u3xIXdlSD0IfXCs/8qf2G0I= +github.com/Mrs4s/MiraiGo v0.0.0-20220524053130-007f228e1092/go.mod h1:7jCgifOheeioTGhQnNUNlinEx7XfBszCEueyZCkTc2A= +github.com/RomiChan/protobuf v0.1.1-0.20220524030518-4f349493f9da h1:T+Sc+QtOfpIRnfnOXaToyLvxmUmFwF2iaqxpJddI4Cc= +github.com/RomiChan/protobuf v0.1.1-0.20220524030518-4f349493f9da/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= +github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= +github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 810c781c25aed4e7de2361307994be1c99bb8555 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 25 May 2022 14:19:59 +0800 Subject: [PATCH 365/612] server: simplify long poll implementation --- server/middlewares.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/server/middlewares.go b/server/middlewares.go index d7835a42f..301efbd24 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -50,33 +50,37 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler { return nil } var ( - once sync.Once - ch = make(chan []interface{}, 1) + ch = make(chan []interface{}) timeout = time.Duration(p.Get("timeout").Int()) * time.Second ) - defer close(ch) go func() { mutex.Lock() defer mutex.Unlock() - if queue.Len() == 0 { + for queue.Len() == 0 { cond.Wait() } - once.Do(func() { - limit := int(p.Get("limit").Int()) - if limit <= 0 || queue.Len() < limit { - limit = queue.Len() - } - ret := make([]interface{}, limit) - for i := 0; i < limit; i++ { - ret[i] = queue.Remove(queue.Front()) + limit := int(p.Get("limit").Int()) + if limit <= 0 || queue.Len() < limit { + limit = queue.Len() + } + ret := make([]interface{}, limit) + elem := queue.Front() + for i := 0; i < limit; i++ { + ret[i] = elem.Value + elem = elem.Next() + } + select { + case ch <- ret: + for i := 0; i < limit; i++ { // remove sent msg + queue.Remove(queue.Front()) } - ch <- ret - }) + default: + // don't block if parent already return due to timeout + } }() if timeout != 0 { select { case <-time.After(timeout): - once.Do(func() {}) return coolq.OK([]interface{}{}) case ret := <-ch: return coolq.OK(ret) From 0fb8970fd7ea810fd356c766b46d67e1a1c8accf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 25 May 2022 14:53:51 +0800 Subject: [PATCH 366/612] =?UTF-8?q?feat:=20=E7=A6=81=E7=94=A8=20UseHighway?= =?UTF-8?q?Message=EF=BC=8C=E5=90=A6=E5=88=99=E5=BE=88=E5=A4=9A=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=8F=91=E4=B8=8D=E5=87=BA=E5=8E=BB=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=20Protocol=20->=20sendPacket=20msg=20error:=2046?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 497fd88c4..f30362827 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -293,14 +293,16 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int var ret *message.GroupMessage // 魔改一下,兼容过长的消息,使得青龙面板那边可以发送很多个账号的统计信息 + // 禁用 UseHighwayMessage,否则很多消息发不出去,提示 Protocol -> sendPacket msg error: 46 + bot.Client.UseHighwayMessage = false msgLen := message.EstimateLength(m.Elements) if msgLen <= MaxMessageSize { - ret = bot.Client.SendGroupMessage(groupID, m, base.ForceFragmented) + ret = bot.Client.SendGroupMessage(groupID, m) } else { // 特殊处理过长的消息,此时mid返回第一个消息 parts := SplitLongMessage(m) for idx, part := range parts { - _ret := bot.Client.SendGroupMessage(groupID, part, base.ForceFragmented) + _ret := bot.Client.SendGroupMessage(groupID, part) if idx == 0 { ret = _ret } From cb86403290a2fa5afe2af18ff2998ca21f0b5933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 25 May 2022 15:01:22 +0800 Subject: [PATCH 367/612] fix: lint --- qqrobot/qinglong/query.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index 7984e5cb0..d1de8773b 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -144,14 +144,15 @@ func QueryCookieExpired(info *JdCookieInfo) string { processedValidLogCount := 0 for _, logFile := range logFiles { result, isLogComplete, skipThis := parseCookieExpired(info, filepath.Join(checkCookieDir, logFile.Name())) - if result != "" { + switch { + case result != "": return appendLogFileInfo(result, logFile.Name()) - } else if isLogComplete { + case isLogComplete: // 没有解析到该账号,但该日志是完整日志,说明这个账号未过期 return "" - } else if skipThis { + case skipThis: // 如果内部判定需要跳过该文件,则不计数,继续尝试下一个 - } else { + default: processedValidLogCount++ } From 296668441f3eeaa084164a6eb130c8a7bcfa6ea3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 25 May 2022 15:24:48 +0800 Subject: [PATCH 368/612] coolq: unify converting IMessage to string&array message --- coolq/api.go | 6 +- coolq/bot.go | 4 +- coolq/converter.go | 12 +- coolq/cqcode.go | 322 +++++++++++++++------------------------- coolq/cqcode/element.go | 51 +++++++ coolq/event.go | 14 +- 6 files changed, 194 insertions(+), 215 deletions(-) create mode 100644 coolq/cqcode/element.go diff --git a/coolq/api.go b/coolq/api.go index 23b3285d9..190484d6a 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1577,7 +1577,7 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { // @route(get_guild_msg) func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { source, seq := decodeGuildMessageID(messageID) - if source == nil { + if source.SourceType == 0 { log.Warnf("获取消息时出现错误: 无效消息ID") return Failed(100, "INVALID_MESSAGE_ID", "无效消息ID") } @@ -1613,7 +1613,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "tiny_id": fU64(pull[0].Sender.TinyId), "nickname": pull[0].Sender.Nickname, } - m["message"] = ToFormattedMessage(pull[0].Elements, *source, false) + m["message"] = ToFormattedMessage(pull[0].Elements, source, false) m["reactions"] = convertReactions(pull[0].Reactions) bot.InsertGuildChannelMessage(pull[0]) } else { @@ -1628,7 +1628,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "tiny_id": fU64(channelMsgByDB.Attribute.SenderTinyID), "nickname": channelMsgByDB.Attribute.SenderName, } - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), *source) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), source, false) } case message.SourceGuildDirect: // todo(mrs4s): 支持 direct 消息 diff --git a/coolq/bot.go b/coolq/bot.go index aea4845b9..e6507cd52 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -630,13 +630,13 @@ func encodeGuildMessageID(primaryID, subID, seq uint64, source message.SourceTyp })) } -func decodeGuildMessageID(id string) (source *message.Source, seq uint64) { +func decodeGuildMessageID(id string) (source message.Source, seq uint64) { b, _ := base64.StdEncoding.DecodeString(id) if len(b) < 25 { return } r := binary.NewReader(b) - source = &message.Source{ + source = message.Source{ SourceType: message.SourceType(r.ReadByte()), PrimaryID: r.ReadInt64(), SecondaryID: r.ReadInt64(), diff --git a/coolq/converter.go b/coolq/converter.go index 43be08ee4..65f26c4fe 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -2,6 +2,7 @@ package coolq import ( "strconv" + "strings" "github.com/Mrs4s/MiraiGo/topic" @@ -64,7 +65,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { SourceType: message.SourceGroup, PrimaryID: m.GroupCode, } - cqm := ToStringMessage(m.Elements, source, true) + cqm := toStringMessage(m.Elements, source, true) postType := "message" if m.Sender.Uin == bot.Client.Uin { postType = "message_sent" @@ -211,6 +212,15 @@ func convertReactions(reactions []*message.GuildMessageEmojiReaction) (r []globa return } +func toStringMessage(m []message.IMessageElement, source message.Source, raw bool) string { + elems := toElements(m, source, raw) + var sb strings.Builder + for _, elem := range elems { + sb.WriteString(elem.CQCode()) + } + return sb.String() +} + func fU64(v uint64) string { return strconv.FormatUint(v, 10) } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 8ea7aac0c..d97c63d8b 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -92,9 +92,12 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { return db.ToGlobalID(id, seq) } -// ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报 -func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []global.MSG) { - r = make([]global.MSG, 0, len(e)) +// toElements 将消息元素数组转为MSG数组以用于消息上报 +func toElements(e []message.IMessageElement, source message.Source, raw bool) (r []cqcode.Element) { + type pair = cqcode.Pair // simplify code + type pairs = []pair + + r = make([]cqcode.Element, 0, len(e)) m := &message.SendingMessage{Elements: e} reply := m.FirstOrNil(func(e message.IMessageElement) bool { _, ok := e.(*message.ReplyElement) @@ -103,26 +106,24 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo if reply != nil && source.SourceType&(message.SourceGroup|message.SourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) id := replyID(replyElem, source) - if base.ExtraReplyData { - r = append(r, global.MSG{ - "type": "reply", - "data": map[string]string{ - "id": strconv.FormatInt(int64(id), 10), - "seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10), - "qq": strconv.FormatInt(replyElem.Sender, 10), - "time": strconv.FormatInt(int64(replyElem.Time), 10), - "text": ToStringMessage(replyElem.Elements, source), - }, - }) - } else { - r = append(r, global.MSG{ - "type": "reply", - "data": map[string]string{"id": strconv.FormatInt(int64(id), 10)}, - }) + elem := cqcode.Element{ + Type: "reply", + Data: pairs{ + {"id", strconv.FormatInt(int64(id), 10)}, + }, } + if base.ExtraReplyData { + elem.Data = append(elem.Data, + pair{K: "seq", V: strconv.FormatInt(int64(replyElem.ReplySeq), 10)}, + pair{K: "qq", V: strconv.FormatInt(replyElem.Sender, 10)}, + pair{K: "time", V: strconv.FormatInt(int64(replyElem.Time), 10)}, + pair{K: "text", V: toStringMessage(replyElem.Elements, source, true)}, + ) + } + r = append(r, elem) } for i, elem := range e { - var m global.MSG + var m cqcode.Element switch o := elem.(type) { case *message.ReplyElement: if base.RemoveReplyAt && i+1 < len(e) { @@ -133,103 +134,143 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo } continue case *message.TextElement: - m = global.MSG{ - "type": "text", - "data": map[string]string{"text": o.Content}, + m = cqcode.Element{ + Type: "text", + Data: pairs{ + {"text", o.Content}, + }, } case *message.LightAppElement: - m = global.MSG{ - "type": "json", - "data": map[string]string{"data": o.Content}, + m = cqcode.Element{ + Type: "json", + Data: pairs{ + {"data", o.Content}, + }, } case *message.AtElement: target := "all" if o.Target != 0 { target = strconv.FormatUint(uint64(o.Target), 10) } - m = global.MSG{ - "type": "at", - "data": map[string]string{"qq": target}, + m = cqcode.Element{ + Type: "at", + Data: pairs{ + {"qq", target}, + }, } case *message.RedBagElement: - m = global.MSG{ - "type": "redbag", - "data": map[string]string{"title": o.Title}, + m = cqcode.Element{ + Type: "redbag", + Data: pairs{ + {"title", o.Title}, + }, } case *message.ForwardElement: - m = global.MSG{ - "type": "forward", - "data": map[string]string{"id": o.ResId}, + m = cqcode.Element{ + Type: "forward", + Data: pairs{ + {"id", o.ResId}, + }, } case *message.FaceElement: - m = global.MSG{ - "type": "face", - "data": map[string]string{"id": strconv.FormatInt(int64(o.Index), 10)}, + m = cqcode.Element{ + Type: "face", + Data: pairs{ + {"id", strconv.FormatInt(int64(o.Index), 10)}, + }, } case *message.VoiceElement: - m = global.MSG{ - "type": "record", - "data": map[string]string{"file": o.Name, "url": o.Url}, + m = cqcode.Element{ + Type: "record", + Data: pairs{ + {"file", o.Name}, + {"url", o.Url}, + }, } case *message.ShortVideoElement: - m = global.MSG{ - "type": "video", - "data": map[string]string{"file": o.Name, "url": o.Url}, + m = cqcode.Element{ + Type: "video", + Data: pairs{ + {"file", o.Name}, + {"url", o.Url}, + }, } case *message.GroupImageElement: - data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url, "subType": strconv.FormatInt(int64(o.ImageBizType), 10)} + data := pairs{ + {"file", hex.EncodeToString(o.Md5) + ".image"}, + {"subType", strconv.FormatInt(int64(o.ImageBizType), 10)}, + } + if raw { + data = append(data, pair{K: "url", V: o.Url}) + } switch { case o.Flash: - data["type"] = "flash" + data = append(data, pair{K: "type", V: "flash"}) case o.EffectID != 0: - data["type"] = "show" - data["id"] = strconv.FormatInt(int64(o.EffectID), 10) + data = append(data, pair{K: "type", V: "show"}) + data = append(data, pair{K: "id", V: strconv.FormatInt(int64(o.EffectID), 10)}) } - m = global.MSG{ - "type": "image", - "data": data, + m = cqcode.Element{ + Type: "image", + Data: data, } case *message.GuildImageElement: - data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url} - m = global.MSG{ - "type": "image", - "data": data, + data := pairs{ + {"file", hex.EncodeToString(o.Md5) + ".image"}, + } + if raw { + data = append(data, pair{K: "url", V: o.Url}) + } + m = cqcode.Element{ + Type: "image", + Data: data, } case *message.FriendImageElement: - data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url} + data := pairs{ + {"file", hex.EncodeToString(o.Md5) + ".image"}, + } + if raw { + data = append(data, pair{K: "url", V: o.Url}) + } if o.Flash { - data["type"] = "flash" + data = append(data, pair{K: "type", V: "flash"}) } - m = global.MSG{ - "type": "image", - "data": data, + m = cqcode.Element{ + Type: "image", + Data: data, } case *message.DiceElement: - m = global.MSG{ - "type": "dice", - "data": map[string]string{"value": strconv.FormatInt(int64(o.Value), 10)}, + m = cqcode.Element{ + Type: "dice", + Data: pairs{ + {"value", strconv.FormatInt(int64(o.Value), 10)}, + }, } case *message.MarketFaceElement: - m = global.MSG{ - "type": "text", - "data": map[string]string{"text": o.Name}, + m = cqcode.Element{ + Type: "text", + Data: pairs{ + {"text", o.Name}, + }, } case *message.ServiceElement: - if isOk := strings.Contains(o.Content, " i+1 { - elem, ok := e[i+1].(*message.AtElement) - if ok && elem.Target == o.Sender { - e[i+1] = nil - } - } - case *message.TextElement: - sb.WriteString(cqcode.EscapeText(o.Content)) - case *message.AtElement: - if o.Target == 0 { - write("[CQ:at,qq=all]") - continue - } - write("[CQ:at,qq=%d]", uint64(o.Target)) - case *message.RedBagElement: - write("[CQ:redbag,title=%s]", o.Title) - case *message.ForwardElement: - write("[CQ:forward,id=%s]", o.ResId) - case *message.FaceElement: - write(`[CQ:face,id=%d]`, o.Index) - case *message.VoiceElement: - if ur { - write(`[CQ:record,file=%s]`, o.Name) - } else { - write(`[CQ:record,file=%s,url=%s]`, o.Name, cqcode.EscapeValue(o.Url)) - } - case *message.ShortVideoElement: - if ur { - write(`[CQ:video,file=%s]`, o.Name) - } else { - write(`[CQ:video,file=%s,url=%s]`, o.Name, cqcode.EscapeValue(o.Url)) - } - case *message.GroupImageElement: - var arg string - if o.Flash { - arg = ",type=flash" - } else if o.EffectID != 0 { - arg = ",type=show,id=" + strconv.FormatInt(int64(o.EffectID), 10) - } - arg += ",subType=" + strconv.FormatInt(int64(o.ImageBizType), 10) - if ur { - write("[CQ:image,file=%x.image%s]", o.Md5, arg) - } else { - write("[CQ:image,file=%x.image,url=%s%s]", o.Md5, cqcode.EscapeValue(o.Url), arg) - } - case *message.FriendImageElement: - var arg string - if o.Flash { - arg = ",type=flash" - } - if ur { - write("[CQ:image,file=%x.image%s]", o.Md5, arg) - } else { - write("[CQ:image,file=%x.image,url=%s%s]", cqcode.EscapeValue(o.Url), arg) - } - case *LocalImageElement: - var arg string - if o.Flash { - arg = ",type=flash" - } - data, err := os.ReadFile(o.File) - if err == nil { - m := md5.Sum(data) - if ur { - write("[CQ:image,file=%x.image%s]", m[:], arg) - } else { - write("[CQ:image,file=%x.image,url=%s%s]", m[:], cqcode.EscapeValue(o.URL), arg) - } - } - case *message.GuildImageElement: - write("[CQ:image,file=%x.image,url=%s]", o.Md5, cqcode.EscapeValue(o.Url)) - case *message.DiceElement: - write("[CQ:dice,value=%v]", o.Value) - case *message.MarketFaceElement: - sb.WriteString(o.Name) - case *message.ServiceElement: - if isOk := strings.Contains(o.Content, " Date: Wed, 25 May 2022 15:28:00 +0800 Subject: [PATCH 369/612] =?UTF-8?q?style:=20=E8=B7=9F=E9=9A=8Fgo-cqhttp?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE=EF=BC=8C=E5=88=87=E6=8D=A2indent?= =?UTF-8?q?=E4=B8=BAtab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index d046cc6aa..77e34f19b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,7 +2,7 @@ root = true [*] -indent_style = space +indent_style = tab indent_size = 4 end_of_line = lf charset = utf-8 From 7dbda5cec7c9a48d4484f2046adbdca625740cf8 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 25 May 2022 15:31:26 +0800 Subject: [PATCH 370/612] coolq: make linter happy --- coolq/cqcode.go | 2 ++ coolq/cqcode/element.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index d97c63d8b..8d9de1d1d 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -93,6 +93,8 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { } // toElements 将消息元素数组转为MSG数组以用于消息上报 +// +// nolint:govet func toElements(e []message.IMessageElement, source message.Source, raw bool) (r []cqcode.Element) { type pair = cqcode.Pair // simplify code type pairs = []pair diff --git a/coolq/cqcode/element.go b/coolq/cqcode/element.go index b7469f92b..835592581 100644 --- a/coolq/cqcode/element.go +++ b/coolq/cqcode/element.go @@ -7,16 +7,19 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) +// Element single message type Element struct { Type string Data []Pair } +// Pair key value pair type Pair struct { K string V string } +// CQCode convert to cqcode func (e *Element) CQCode() string { if e.Type == "text" { return EscapeText(e.Data[0].V) // must be {"text": value} @@ -34,6 +37,7 @@ func (e *Element) CQCode() string { return sb.String() } +// MarshalJSON see encoding/json.Marshaler func (e *Element) MarshalJSON() ([]byte, error) { buf := global.NewBuffer() defer global.PutBuffer(buf) From 9e136b21fa2ecf9d7ef97bce38cca06d35e7f37e Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 26 May 2022 20:45:05 +0800 Subject: [PATCH 371/612] coolq: refactor event msg --- coolq/api.go | 4 +- coolq/bot.go | 42 +++-- coolq/converter.go | 16 +- coolq/event.go | 422 +++++++++++++++++------------------------- server/middlewares.go | 2 +- 5 files changed, 205 insertions(+), 281 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 190484d6a..df7285ed4 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1671,12 +1671,12 @@ func (bot *CQBot) CQGetGroupMessageHistory(groupID int64, seq int64) global.MSG log.Warnf("获取群历史消息失败: %v", err) return Failed(100, "MESSAGES_API_ERROR", err.Error()) } - ms := make([]global.MSG, 0, len(msg)) + ms := make([]*event, 0, len(msg)) for _, m := range msg { bot.checkMedia(m.Elements, groupID) id := bot.InsertGroupMessage(m) t := bot.formatGroupMessage(m) - t["message_id"] = id + t.Others["message_id"] = id ms = append(ms, t) } return OK(global.MSG{ diff --git a/coolq/bot.go b/coolq/bot.go index e6507cd52..7723ea1eb 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "runtime/debug" + "strings" "sync" "time" @@ -38,9 +39,8 @@ type CQBot struct { // Event 事件 type Event struct { - RawMsg global.MSG - once sync.Once + Raw *event buffer *bytes.Buffer } @@ -48,7 +48,7 @@ func (e *Event) marshal() { if e.buffer == nil { e.buffer = global.NewBuffer() } - _ = json.NewEncoder(e.buffer).Encode(e.RawMsg) + _ = json.NewEncoder(e.buffer).Encode(e.Raw) } // JSONBytes return byes of json by lazy marshalling. @@ -110,13 +110,9 @@ func NewQQBot(cli *client.QQClient) *CQBot { t := time.NewTicker(base.HeartbeatInterval) for { <-t.C - bot.dispatchEventMessage(global.MSG{ - "time": time.Now().Unix(), - "self_id": bot.Client.Uin, - "post_type": "meta_event", - "meta_event_type": "heartbeat", - "status": bot.CQGetStatus()["data"], - "interval": base.HeartbeatInterval.Milliseconds(), + bot.dispatchEvent("meta_event/heartbeat", global.MSG{ + "status": bot.CQGetStatus()["data"], + "interval": base.HeartbeatInterval.Milliseconds(), }) } }() @@ -569,11 +565,31 @@ func (bot *CQBot) InsertGuildChannelMessage(m *message.GuildChannelMessage) stri return msg.ID } -func (bot *CQBot) dispatchEventMessage(m global.MSG) { +func (bot *CQBot) event(typ string, others global.MSG) *event { + ev := new(event) + post, detail, ok := strings.Cut(typ, "/") + ev.PostType = post + ev.DetailType = detail + if ok { + detail, sub, _ := strings.Cut(detail, "/") + ev.DetailType = detail + ev.SubType = sub + } + ev.Time = time.Now().Unix() + ev.SelfID = bot.Client.Uin + ev.Others = others + return ev +} + +func (bot *CQBot) dispatchEvent(typ string, others global.MSG) { + bot.dispatch(bot.event(typ, others)) +} + +func (bot *CQBot) dispatch(ev *event) { bot.lock.RLock() defer bot.lock.RUnlock() - event := &Event{RawMsg: m} + event := &Event{Raw: ev} wg := sync.WaitGroup{} wg.Add(len(bot.events)) for _, f := range bot.events { @@ -581,7 +597,7 @@ func (bot *CQBot) dispatchEventMessage(m global.MSG) { defer func() { wg.Done() if pan := recover(); pan != nil { - log.Warnf("处理事件 %v 时出现错误: %v \n%s", m, pan, debug.Stack()) + log.Warnf("处理事件 %v 时出现错误: %v \n%s", event.JSONString(), pan, debug.Stack()) } }() diff --git a/coolq/converter.go b/coolq/converter.go index 65f26c4fe..2116c7ac2 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -60,15 +60,15 @@ func convertGuildMemberInfo(m []*client.GuildMemberInfo) (r []global.MSG) { return } -func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { +func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) *event { source := message.Source{ SourceType: message.SourceGroup, PrimaryID: m.GroupCode, } cqm := toStringMessage(m.Elements, source, true) - postType := "message" + typ := "message/group/normal" if m.Sender.Uin == bot.Client.Uin { - postType = "message_sent" + typ = "message_sent/group/normal" } gm := global.MSG{ "anonymous": nil, @@ -77,9 +77,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { "message": ToFormattedMessage(m.Elements, source, false), "message_type": "group", "message_seq": m.Id, - "post_type": postType, "raw_message": cqm, - "self_id": bot.Client.Uin, "sender": global.MSG{ "age": 0, "area": "", @@ -87,9 +85,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { "sex": "unknown", "user_id": m.Sender.Uin, }, - "sub_type": "normal", - "time": m.Time, - "user_id": m.Sender.Uin, + "user_id": m.Sender.Uin, } if m.Sender.IsAnonymous() { gm["anonymous"] = global.MSG{ @@ -128,7 +124,9 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { ms["card"] = mem.CardName ms["title"] = mem.SpecialTitle } - return gm + ev := bot.event(typ, gm) + ev.Time = int64(m.Time) + return ev } func convertChannelInfo(c *client.ChannelInfo) global.MSG { diff --git a/coolq/event.go b/coolq/event.go index 6f8d35f1d..cef496c5c 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -2,12 +2,12 @@ package coolq import ( "encoding/hex" + "encoding/json" "fmt" "os" "path" "strconv" "strings" - "time" "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" @@ -30,7 +30,45 @@ func ToFormattedMessage(e []message.IMessageElement, source message.Source, raw return } -func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) { +type event struct { + PostType string + DetailType string + SubType string + Time int64 + SelfID int64 + Others global.MSG +} + +func (ev *event) MarshalJSON() ([]byte, error) { + buf := global.NewBuffer() + defer global.PutBuffer(buf) + + detail := "" + switch ev.PostType { + case "message", "message_sent": + detail = "message_type" + case "notice": + detail = "notice_type" + case "request": + detail = "request_type" + case "meta_event": + detail = "meta_event_type" + default: + panic("unknown post type: " + ev.PostType) + } + fmt.Fprintf(buf, `{"post_type":"%s","%s":"%s","time":%d,"self_id":%d`, ev.PostType, detail, ev.DetailType, ev.Time, ev.SelfID) + if ev.SubType != "" { + fmt.Fprintf(buf, `,"sub_type":"%s"`, ev.SubType) + } + for k, v := range ev.Others { + v, _ := json.Marshal(v) + fmt.Fprintf(buf, `,"%s":%s`, k, v) + } + buf.WriteByte('}') + return append([]byte(nil), buf.Bytes()...), nil +} + +func (bot *CQBot) privateMessageEvent(_ *client.QQClient, m *message.PrivateMessage) { bot.checkMedia(m.Elements, m.Sender.Uin) source := message.Source{ SourceType: message.SourcePrivate, @@ -39,23 +77,17 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess cqm := toStringMessage(m.Elements, source, true) id := bot.InsertPrivateMessage(m) log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id) + typ := "message/private/friend" + if m.Sender.Uin == bot.Client.Uin { + typ = "message_sent/private/friend" + } fm := global.MSG{ - "post_type": func() string { - if m.Sender.Uin == bot.Client.Uin { - return "message_sent" - } - return "message" - }(), - "message_type": "private", - "sub_type": "friend", - "message_id": id, - "user_id": m.Sender.Uin, - "target_id": m.Target, - "message": ToFormattedMessage(m.Elements, source, false), - "raw_message": cqm, - "font": 0, - "self_id": c.Uin, - "time": time.Now().Unix(), + "message_id": id, + "user_id": m.Sender.Uin, + "target_id": m.Target, + "message": ToFormattedMessage(m.Elements, source, false), + "raw_message": cqm, + "font": 0, "sender": global.MSG{ "user_id": m.Sender.Uin, "nickname": m.Sender.Nickname, @@ -63,7 +95,7 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess "age": 0, }, } - bot.dispatchEventMessage(fm) + bot.dispatchEvent(typ, fm) } func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) { @@ -71,11 +103,9 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) for _, elem := range m.Elements { if file, ok := elem.(*message.GroupFileElement); ok { log.Infof("群 %v(%v) 内 %v(%v) 上传了文件: %v", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, file.Name) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "group_upload", - "group_id": m.GroupCode, - "user_id": m.Sender.Uin, + bot.dispatchEvent("notice/group_upload", global.MSG{ + "group_id": m.GroupCode, + "user_id": m.Sender.Uin, "file": global.MSG{ "id": file.Path, "name": file.Name, @@ -83,8 +113,6 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) "busid": file.Busid, "url": c.GetGroupFileUrl(m.GroupCode, file.Path, file.Busid), }, - "self_id": c.Uin, - "time": time.Now().Unix(), }) return } @@ -100,8 +128,8 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) if gm == nil { return } - gm["message_id"] = id - bot.dispatchEventMessage(gm) + gm.Others["message_id"] = id + bot.dispatch(gm) } func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) { @@ -120,17 +148,12 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven // } log.Infof("收到来自群 %v(%v) 内 %v(%v) 的临时会话消息: %v", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm) tm := global.MSG{ - "post_type": "message", - "message_type": "private", - "sub_type": "group", - "temp_source": e.Session.Source, - "message_id": id, - "user_id": m.Sender.Uin, - "message": ToFormattedMessage(m.Elements, source, false), - "raw_message": cqm, - "font": 0, - "self_id": c.Uin, - "time": time.Now().Unix(), + "temp_source": e.Session.Source, + "message_id": id, + "user_id": m.Sender.Uin, + "message": ToFormattedMessage(m.Elements, source, false), + "raw_message": cqm, + "font": 0, "sender": global.MSG{ "user_id": m.Sender.Uin, "group_id": m.GroupCode, @@ -139,7 +162,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven "age": 0, }, } - bot.dispatchEventMessage(tm) + bot.dispatchEvent("message/private/group", tm) } func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildChannelMessage) { @@ -156,24 +179,21 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC } log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, toStringMessage(m.Elements, source, true)) id := bot.InsertGuildChannelMessage(m) - bot.dispatchEventMessage(global.MSG{ - "post_type": "message", - "message_type": "guild", - "sub_type": "channel", + ev := bot.event("message/guild/channel", global.MSG{ "guild_id": fU64(m.GuildId), "channel_id": fU64(m.ChannelId), "message_id": id, "user_id": fU64(m.Sender.TinyId), "message": ToFormattedMessage(m.Elements, source, false), // todo: 增加对频道消息 Reply 的支持 - "self_id": bot.Client.Uin, "self_tiny_id": fU64(bot.Client.GuildService.TinyId), - "time": m.Time, "sender": global.MSG{ "user_id": m.Sender.TinyId, "tiny_id": fU64(m.Sender.TinyId), "nickname": m.Sender.Nickname, }, }) + ev.Time = m.Time + bot.dispatch(ev) } func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *client.GuildMessageReactionsUpdatedEvent) { @@ -199,16 +219,12 @@ func (bot *CQBot) guildMessageReactionsUpdatedEvent(c *client.QQClient, e *clien str += "无任何表情" } log.Infof(str) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "message_reactions_updated", + bot.dispatchEvent("notice/message_reactions_updated", global.MSG{ "guild_id": fU64(e.GuildId), "channel_id": fU64(e.ChannelId), "message_id": msgID, "operator_id": fU64(e.OperatorId), "current_reactions": currentReactions, - "time": time.Now().Unix(), - "self_id": bot.Client.Uin, "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, }) @@ -230,15 +246,11 @@ func (bot *CQBot) guildChannelMessageRecalledEvent(c *client.QQClient, e *client } msgID := encodeGuildMessageID(e.GuildId, e.ChannelId, e.MessageId, message.SourceGuildChannel) log.Infof("用户 %v(%v) 撤回了频道 %v(%v) 子频道 %v(%v) 的消息 %v", operator.Nickname, operator.TinyId, guild.GuildName, guild.GuildId, channel.ChannelName, channel.ChannelId, msgID) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "guild_channel_recall", + bot.dispatchEvent("notice/guild_channel_recall", global.MSG{ "guild_id": fU64(e.GuildId), "channel_id": fU64(e.ChannelId), "operator_id": fU64(e.OperatorId), "message_id": msgID, - "time": time.Now().Unix(), - "self_id": bot.Client.Uin, "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, }) @@ -250,14 +262,10 @@ func (bot *CQBot) guildChannelUpdatedEvent(c *client.QQClient, e *client.GuildCh return } log.Infof("频道 %v(%v) 子频道 %v(%v) 信息已更新", guild.GuildName, guild.GuildId, e.NewChannelInfo.ChannelName, e.NewChannelInfo.ChannelId) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "channel_updated", + bot.dispatchEvent("notice/channel_updated", global.MSG{ "guild_id": fU64(e.GuildId), "channel_id": fU64(e.ChannelId), "operator_id": fU64(e.OperatorId), - "time": time.Now().Unix(), - "self_id": bot.Client.Uin, "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, "old_info": convertChannelInfo(e.OldChannelInfo), @@ -275,16 +283,12 @@ func (bot *CQBot) guildChannelCreatedEvent(c *client.QQClient, e *client.GuildCh member = &client.GuildUserProfile{Nickname: "未知"} } log.Infof("频道 %v(%v) 内用户 %v(%v) 创建了子频道 %v(%v)", guild.GuildName, guild.GuildId, member.Nickname, member.TinyId, e.ChannelInfo.ChannelName, e.ChannelInfo.ChannelId) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "channel_created", + bot.dispatchEvent("notice/channel_created", global.MSG{ "guild_id": fU64(e.GuildId), "channel_id": fU64(e.ChannelInfo.ChannelId), "operator_id": fU64(e.OperatorId), - "self_id": bot.Client.Uin, "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, - "time": time.Now().Unix(), "channel_info": convertChannelInfo(e.ChannelInfo), }) } @@ -299,16 +303,12 @@ func (bot *CQBot) guildChannelDestroyedEvent(c *client.QQClient, e *client.Guild member = &client.GuildUserProfile{Nickname: "未知"} } log.Infof("频道 %v(%v) 内用户 %v(%v) 删除了子频道 %v(%v)", guild.GuildName, guild.GuildId, member.Nickname, member.TinyId, e.ChannelInfo.ChannelName, e.ChannelInfo.ChannelId) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "channel_destroyed", + bot.dispatchEvent("notice/channel_destroyed", global.MSG{ "guild_id": fU64(e.GuildId), "channel_id": fU64(e.ChannelInfo.ChannelId), "operator_id": fU64(e.OperatorId), - "self_id": bot.Client.Uin, "self_tiny_id": fU64(bot.Client.GuildService.TinyId), "user_id": e.OperatorId, - "time": time.Now().Unix(), "channel_info": convertChannelInfo(e.ChannelInfo), }) } @@ -332,22 +332,15 @@ func (bot *CQBot) groupMutedEvent(c *client.QQClient, e *client.GroupMuteEvent) formatGroupName(g), formatMemberName(g.FindMember(e.TargetUin)), formatMemberName(g.FindMember(e.OperatorUin))) } } - - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", + typ := "notice/group_ban/ban" + if e.Time == 0 { + typ = "notice/group_ban/lift_ban" + } + bot.dispatchEvent(typ, global.MSG{ "duration": e.Time, "group_id": e.GroupCode, - "notice_type": "group_ban", "operator_id": e.OperatorUin, - "self_id": c.Uin, "user_id": e.TargetUin, - "time": time.Now().Unix(), - "sub_type": func() string { - if e.Time == 0 { - return "lift_ban" - } - return "ban" - }(), }) } @@ -356,16 +349,15 @@ func (bot *CQBot) groupRecallEvent(c *client.QQClient, e *client.GroupMessageRec gid := db.ToGlobalID(e.GroupCode, e.MessageId) log.Infof("群 %v 内 %v 撤回了 %v 的消息: %v.", formatGroupName(g), formatMemberName(g.FindMember(e.OperatorUin)), formatMemberName(g.FindMember(e.AuthorUin)), gid) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", + + ev := bot.event("notice/group_recall", global.MSG{ "group_id": e.GroupCode, - "notice_type": "group_recall", - "self_id": c.Uin, "user_id": e.AuthorUin, "operator_id": e.OperatorUin, - "time": e.Time, "message_id": gid, }) + ev.Time = int64(e.Time) + bot.dispatch(ev) } func (bot *CQBot) groupNotifyEvent(c *client.QQClient, e client.INotifyEvent) { @@ -375,42 +367,27 @@ func (bot *CQBot) groupNotifyEvent(c *client.QQClient, e client.INotifyEvent) { sender := group.FindMember(notify.Sender) receiver := group.FindMember(notify.Receiver) log.Infof("群 %v 内 %v 戳了戳 %v", formatGroupName(group), formatMemberName(sender), formatMemberName(receiver)) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "group_id": group.Code, - "notice_type": "notify", - "sub_type": "poke", - "self_id": c.Uin, - "user_id": notify.Sender, - "sender_id": notify.Sender, - "target_id": notify.Receiver, - "time": time.Now().Unix(), + bot.dispatchEvent("notice/notify/poke", global.MSG{ + "group_id": group.Code, + "user_id": notify.Sender, + "sender_id": notify.Sender, + "target_id": notify.Receiver, }) case *client.GroupRedBagLuckyKingNotifyEvent: sender := group.FindMember(notify.Sender) luckyKing := group.FindMember(notify.LuckyKing) log.Infof("群 %v 内 %v 的红包被抢完, %v 是运气王", formatGroupName(group), formatMemberName(sender), formatMemberName(luckyKing)) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "group_id": group.Code, - "notice_type": "notify", - "sub_type": "lucky_king", - "self_id": c.Uin, - "user_id": notify.Sender, - "sender_id": notify.Sender, - "target_id": notify.LuckyKing, - "time": time.Now().Unix(), + bot.dispatchEvent("notice/notify/lucky_king", global.MSG{ + "group_id": group.Code, + "user_id": notify.Sender, + "sender_id": notify.Sender, + "target_id": notify.LuckyKing, }) case *client.MemberHonorChangedNotifyEvent: log.Info(notify.Content()) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "group_id": group.Code, - "notice_type": "notify", - "sub_type": "honor", - "self_id": c.Uin, - "user_id": notify.Uin, - "time": time.Now().Unix(), + bot.dispatchEvent("notice/notify/honor", global.MSG{ + "group_id": group.Code, + "user_id": notify.Uin, "honor_type": func() string { switch notify.Honor { case client.Talkative: @@ -439,15 +416,10 @@ func (bot *CQBot) friendNotifyEvent(c *client.QQClient, e client.INotifyEvent) { } else { log.Infof("好友 %v 戳了戳你.", friend.Nickname) } - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "notify", - "sub_type": "poke", - "self_id": c.Uin, - "user_id": notify.Sender, - "sender_id": notify.Sender, - "target_id": notify.Receiver, - "time": time.Now().Unix(), + bot.dispatchEvent("notice/notify/poke", global.MSG{ + "user_id": notify.Sender, + "sender_id": notify.Sender, + "target_id": notify.Receiver, }) } } @@ -456,15 +428,10 @@ func (bot *CQBot) memberTitleUpdatedEvent(c *client.QQClient, e *client.MemberSp group := c.FindGroup(e.GroupCode) mem := group.FindMember(e.Uin) log.Infof("群 %v(%v) 内成员 %v(%v) 获得了新的头衔: %v", group.Name, group.Code, mem.DisplayName(), mem.Uin, e.NewTitle) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "notify", - "sub_type": "title", - "group_id": group.Code, - "self_id": c.Uin, - "user_id": e.Uin, - "time": time.Now().Unix(), - "title": e.NewTitle, + bot.dispatchEvent("notice/notify/title", global.MSG{ + "group_id": group.Code, + "user_id": e.Uin, + "title": e.NewTitle, }) } @@ -476,14 +443,12 @@ func (bot *CQBot) friendRecallEvent(c *client.QQClient, e *client.FriendMessageR } else { log.Infof("好友 %v 撤回了消息: %v", e.FriendUin, gid) } - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "friend_recall", - "self_id": c.Uin, - "user_id": e.FriendUin, - "time": e.Time, - "message_id": gid, + ev := bot.event("notice/friend_recall", global.MSG{ + "user_id": e.FriendUin, + "message_id": gid, }) + ev.Time = e.Time + bot.dispatch(ev) } func (bot *CQBot) offlineFileEvent(c *client.QQClient, e *client.OfflineFileEvent) { @@ -492,23 +457,19 @@ func (bot *CQBot) offlineFileEvent(c *client.QQClient, e *client.OfflineFileEven return } log.Infof("好友 %v(%v) 发送了离线文件 %v", f.Nickname, f.Uin, e.FileName) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "offline_file", - "user_id": e.Sender, + bot.dispatchEvent("notice/offline_file", global.MSG{ + "user_id": e.Sender, "file": global.MSG{ "name": e.FileName, "size": e.FileSize, "url": e.DownloadUrl, }, - "self_id": c.Uin, - "time": time.Now().Unix(), }) } func (bot *CQBot) joinGroupEvent(c *client.QQClient, group *client.GroupInfo) { log.Infof("Bot进入了群 %v.", formatGroupName(group)) - bot.dispatchEventMessage(bot.groupIncrease(group.Code, 0, c.Uin)) + bot.dispatch(bot.groupIncrease(group.Code, 0, c.Uin)) } func (bot *CQBot) leaveGroupEvent(c *client.QQClient, e *client.GroupLeaveEvent) { @@ -517,44 +478,33 @@ func (bot *CQBot) leaveGroupEvent(c *client.QQClient, e *client.GroupLeaveEvent) } else { log.Infof("Bot退出了群 %v.", formatGroupName(e.Group)) } - bot.dispatchEventMessage(bot.groupDecrease(e.Group.Code, c.Uin, e.Operator)) + bot.dispatch(bot.groupDecrease(e.Group.Code, c.Uin, e.Operator)) } func (bot *CQBot) memberPermissionChangedEvent(c *client.QQClient, e *client.MemberPermissionChangedEvent) { - st := func() string { - if e.NewPermission == client.Administrator { - return "set" - } - return "unset" - }() - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "group_admin", - "sub_type": st, - "group_id": e.Group.Code, - "user_id": e.Member.Uin, - "time": time.Now().Unix(), - "self_id": c.Uin, + st := "unset" + if e.NewPermission == client.Administrator { + st = "set" + } + bot.dispatchEvent("notice/group_admin/"+st, global.MSG{ + "group_id": e.Group.Code, + "user_id": e.Member.Uin, }) } func (bot *CQBot) memberCardUpdatedEvent(c *client.QQClient, e *client.MemberCardUpdatedEvent) { log.Infof("群 %v 的 %v 更新了名片 %v -> %v", formatGroupName(e.Group), formatMemberName(e.Member), e.OldCard, e.Member.CardName) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "group_card", - "group_id": e.Group.Code, - "user_id": e.Member.Uin, - "card_new": e.Member.CardName, - "card_old": e.OldCard, - "time": time.Now().Unix(), - "self_id": c.Uin, + bot.dispatchEvent("notice/group_card", global.MSG{ + "group_id": e.Group.Code, + "user_id": e.Member.Uin, + "card_new": e.Member.CardName, + "card_old": e.OldCard, }) } func (bot *CQBot) memberJoinEvent(_ *client.QQClient, e *client.MemberJoinGroupEvent) { log.Infof("新成员 %v 进入了群 %v.", formatMemberName(e.Member), formatGroupName(e.Group)) - bot.dispatchEventMessage(bot.groupIncrease(e.Group.Code, 0, e.Member.Uin)) + bot.dispatch(bot.groupIncrease(e.Group.Code, 0, e.Member.Uin)) } func (bot *CQBot) memberLeaveEvent(_ *client.QQClient, e *client.MemberLeaveGroupEvent) { @@ -563,65 +513,47 @@ func (bot *CQBot) memberLeaveEvent(_ *client.QQClient, e *client.MemberLeaveGrou } else { log.Infof("成员 %v 离开了群 %v.", formatMemberName(e.Member), formatGroupName(e.Group)) } - bot.dispatchEventMessage(bot.groupDecrease(e.Group.Code, e.Member.Uin, e.Operator)) + bot.dispatch(bot.groupDecrease(e.Group.Code, e.Member.Uin, e.Operator)) } func (bot *CQBot) friendRequestEvent(c *client.QQClient, e *client.NewFriendRequest) { log.Infof("收到来自 %v(%v) 的好友请求: %v", e.RequesterNick, e.RequesterUin, e.Message) flag := strconv.FormatInt(e.RequestId, 10) bot.friendReqCache.Store(flag, e) - bot.dispatchEventMessage(global.MSG{ - "post_type": "request", - "request_type": "friend", - "user_id": e.RequesterUin, - "comment": e.Message, - "flag": flag, - "time": time.Now().Unix(), - "self_id": c.Uin, + bot.dispatchEvent("request/friend", global.MSG{ + "user_id": e.RequesterUin, + "comment": e.Message, + "flag": flag, }) } func (bot *CQBot) friendAddedEvent(c *client.QQClient, e *client.NewFriendEvent) { log.Infof("添加了新好友: %v(%v)", e.Friend.Nickname, e.Friend.Uin) bot.tempSessionCache.Delete(e.Friend.Uin) - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "friend_add", - "self_id": c.Uin, - "user_id": e.Friend.Uin, - "time": time.Now().Unix(), + bot.dispatchEvent("notice/friend_add", global.MSG{ + "user_id": e.Friend.Uin, }) } func (bot *CQBot) groupInvitedEvent(c *client.QQClient, e *client.GroupInvitedRequest) { log.Infof("收到来自群 %v(%v) 内用户 %v(%v) 的加群邀请.", e.GroupName, e.GroupCode, e.InvitorNick, e.InvitorUin) flag := strconv.FormatInt(e.RequestId, 10) - bot.dispatchEventMessage(global.MSG{ - "post_type": "request", - "request_type": "group", - "sub_type": "invite", - "group_id": e.GroupCode, - "user_id": e.InvitorUin, - "comment": "", - "flag": flag, - "time": time.Now().Unix(), - "self_id": c.Uin, + bot.dispatchEvent("request/group/invite", global.MSG{ + "group_id": e.GroupCode, + "user_id": e.InvitorUin, + "comment": "", + "flag": flag, }) } func (bot *CQBot) groupJoinReqEvent(c *client.QQClient, e *client.UserJoinGroupRequest) { log.Infof("群 %v(%v) 收到来自用户 %v(%v) 的加群请求.", e.GroupName, e.GroupCode, e.RequesterNick, e.RequesterUin) flag := strconv.FormatInt(e.RequestId, 10) - bot.dispatchEventMessage(global.MSG{ - "post_type": "request", - "request_type": "group", - "sub_type": "add", - "group_id": e.GroupCode, - "user_id": e.RequesterUin, - "comment": e.Message, - "flag": flag, - "time": time.Now().Unix(), - "self_id": c.Uin, + bot.dispatchEvent("request/group/add", global.MSG{ + "group_id": e.GroupCode, + "user_id": e.RequesterUin, + "comment": e.Message, + "flag": flag, }) } @@ -631,17 +563,13 @@ func (bot *CQBot) otherClientStatusChangedEvent(c *client.QQClient, e *client.Ot } else { log.Infof("Bot 账号在客户端 %v (%v) 登出.", e.Client.DeviceName, e.Client.DeviceKind) } - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", - "notice_type": "client_status", - "online": e.Online, + bot.dispatchEvent("notice/client_status", global.MSG{ + "online": e.Online, "client": global.MSG{ "app_id": e.Client.AppId, "device_name": e.Client.DeviceName, "device_kind": e.Client.DeviceKind, }, - "self_id": c.Uin, - "time": time.Now().Unix(), }) } @@ -668,61 +596,43 @@ func (bot *CQBot) groupEssenceMsg(c *client.QQClient, e *client.GroupDigestEvent if e.OperatorUin == bot.Client.Uin { return } - bot.dispatchEventMessage(global.MSG{ - "post_type": "notice", + subtype := "delete" + if e.OperationType == 1 { + subtype = "add" + } + bot.dispatchEvent("notice/essence/"+subtype, global.MSG{ "group_id": e.GroupCode, - "notice_type": "essence", - "sub_type": func() string { - if e.OperationType == 1 { - return "add" - } - return "delete" - }(), - "self_id": c.Uin, "sender_id": e.SenderUin, "operator_id": e.OperatorUin, - "time": time.Now().Unix(), "message_id": gid, }) } -func (bot *CQBot) groupIncrease(groupCode, operatorUin, userUin int64) global.MSG { - return global.MSG{ - "post_type": "notice", - "notice_type": "group_increase", +func (bot *CQBot) groupIncrease(groupCode, operatorUin, userUin int64) *event { + return bot.event("notice/group_increase/approve", global.MSG{ "group_id": groupCode, "operator_id": operatorUin, - "self_id": bot.Client.Uin, - "sub_type": "approve", - "time": time.Now().Unix(), "user_id": userUin, - } + }) } -func (bot *CQBot) groupDecrease(groupCode, userUin int64, operator *client.GroupMemberInfo) global.MSG { - return global.MSG{ - "post_type": "notice", - "notice_type": "group_decrease", - "group_id": groupCode, - "operator_id": func() int64 { - if operator != nil { - return operator.Uin - } - return userUin - }(), - "self_id": bot.Client.Uin, - "sub_type": func() string { - if operator != nil { - if userUin == bot.Client.Uin { - return "kick_me" - } - return "kick" - } - return "leave" - }(), - "time": time.Now().Unix(), - "user_id": userUin, +func (bot *CQBot) groupDecrease(groupCode, userUin int64, operator *client.GroupMemberInfo) *event { + op := userUin + if operator != nil { + op = operator.Uin + } + subtype := "leave" + if operator != nil { + if userUin == bot.Client.Uin { + subtype = "kick_me" + } + subtype = "kick" } + return bot.event("notice/group_decrease/"+subtype, global.MSG{ + "group_id": groupCode, + "operator_id": op, + "user_id": userUin, + }) } func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { diff --git a/server/middlewares.go b/server/middlewares.go index 301efbd24..7230248d1 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -39,7 +39,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler { bot.OnEventPush(func(event *coolq.Event) { mutex.Lock() defer mutex.Unlock() - queue.PushBack(event.RawMsg) + queue.PushBack(event.Raw) for maxSize != 0 && queue.Len() > maxSize { queue.Remove(queue.Front()) } From 5daea94157de3f5e8ead4fa7e85b60c74ffba542 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 26 May 2022 20:48:11 +0800 Subject: [PATCH 372/612] fix golangci-lint error --- coolq/event.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coolq/event.go b/coolq/event.go index cef496c5c..74642b161 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -625,8 +625,9 @@ func (bot *CQBot) groupDecrease(groupCode, userUin int64, operator *client.Group if operator != nil { if userUin == bot.Client.Uin { subtype = "kick_me" + } else { + subtype = "kick" } - subtype = "kick" } return bot.event("notice/group_decrease/"+subtype, global.MSG{ "group_id": groupCode, From 8f2ad4eb5215b33c99add3323683d1e595494df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 27 May 2022 00:47:09 +0800 Subject: [PATCH 373/612] =?UTF-8?q?fix:=20=E4=B8=B4=E6=97=B6=E5=A4=84?= =?UTF-8?q?=E7=90=86=E4=B8=8A=E4=BC=A0=E5=9B=BE=E7=89=87=E6=97=B6panic?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/bot.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/coolq/bot.go b/coolq/bot.go index f30362827..d2822ccbf 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -137,6 +137,12 @@ type worker struct { func (w *worker) do(f func()) { w.wg.Add(1) go func() { + defer func() { + if err := recover(); err != nil { + // 临时处理办法,提个issue等处理 + log.Errorf("临时处理办法: worker panic: %v", err) + } + }() defer w.wg.Done() f() }() From d313effb7965ce1cc9fed1ea6b90d65e86e9db2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E8=91=89=E6=9D=8F?= Date: Fri, 27 May 2022 14:24:48 +0800 Subject: [PATCH 374/612] feat: send forward msg to private (#1513) * feature(forward_msg): support send forward msg to private Added two apis: send_forward_msg & send_private_forward_msg * typo: messages * fix: message source target id --- coolq/api.go | 53 +++++++++++++++++++++++++++++++++++++++++----- modules/api/api.go | 10 +++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index df7285ed4..8332ac913 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -684,6 +684,24 @@ func (bot *CQBot) CQSendMessage(groupID, userID int64, m gjson.Result, messageTy return global.MSG{} } +// CQSendForwardMessage 发送合并转发消息 +// +// @route(send_forward_msg) +// @rename(m->messages) +func (bot *CQBot) CQSendForwardMessage(groupID, userID int64, m gjson.Result, messageType string) global.MSG { + switch { + case messageType == "group": + return bot.CQSendGroupForwardMessage(groupID, m) + case messageType == "private": + fallthrough + case userID != 0: + return bot.CQSendPrivateForwardMessage(userID, m) + case groupID != 0: + return bot.CQSendGroupForwardMessage(groupID, m) + } + return global.MSG{} +} + // CQSendGroupMessage 发送群消息 // // https://git.io/Jtz1c @@ -785,9 +803,13 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R return OK(global.MSG{"message_id": mid}) } -func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.ForwardElement { +func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType message.SourceType) *message.ForwardElement { ts := time.Now().Add(-time.Minute * 5) - source := message.Source{SourceType: message.SourceGroup, PrimaryID: groupID} + groupID := target + source := message.Source{SourceType: sourceType, PrimaryID: target} + if sourceType == message.SourcePrivate { + groupID = 0 + } builder := bot.Client.NewForwardMessageBuilder(groupID) var convertMessage func(m gjson.Result) *message.ForwardMessage @@ -802,7 +824,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F w.do(func() { gm, err := bot.uploadLocalVideo(source, o) if err != nil { - log.Warnf(uploadFailedTemplate, "群", groupID, "视频", err) + log.Warnf(uploadFailedTemplate, "合并转发", target, "视频", err) } else { *p = gm } @@ -811,7 +833,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, groupID int64) *message.F w.do(func() { gm, err := bot.uploadLocalImage(source, o) if err != nil { - log.Warnf(uploadFailedTemplate, "群", groupID, "图片", err) + log.Warnf(uploadFailedTemplate, "合并转发", target, "图片", err) } else { *p = gm } @@ -913,7 +935,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa return Failed(100) } - fe := bot.uploadForwardElement(m, groupID) + fe := bot.uploadForwardElement(m, groupID, message.SourceGroup) if fe == nil { return Failed(100, "EMPTY_NODES", "未找到任何可发送的合并转发信息") } @@ -927,6 +949,27 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa }) } +// CQSendPrivateForwardMessage 扩展API-发送合并转发(好友) +// +// https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4 +// @route(send_private_forward_msg) +// @rename(m->messages) +func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) global.MSG { + if m.Type != gjson.JSON { + return Failed(100) + } + fe := bot.uploadForwardElement(m, userID, message.SourcePrivate) + if fe == nil { + return Failed(100, "EMPTY_NODES", "未找到任何可发送的合并转发信息") + } + mid := bot.SendPrivateMessage(userID, 0, &message.SendingMessage{Elements: []message.IMessageElement{fe}}) + if mid == -1 { + log.Warnf("合并转发(好友)消息发送失败: 账号可能被风控.") + return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") + } + return OK(global.MSG{"message_id": mid}) +} + // CQSendPrivateMessage 发送私聊消息 // // https://git.io/Jtz1l diff --git a/modules/api/api.go b/modules/api/api.go index b57c2500f..63d087e7a 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -195,6 +195,12 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "reload_event_filter": p0 := p.Get("file").String() return c.bot.CQReloadEventFilter(p0) + case "send_forward_msg": + p0 := p.Get("group_id").Int() + p1 := p.Get("user_id").Int() + p2 := p.Get("messages") + p3 := p.Get("message_type").String() + return c.bot.CQSendForwardMessage(p0, p1, p2, p3) case "send_group_forward_msg": p0 := p.Get("group_id").Int() p1 := p.Get("messages") @@ -217,6 +223,10 @@ func (c *Caller) call(action string, p Getter) global.MSG { p3 := p.Get("message_type").String() p4 := p.Get("auto_escape").Bool() return c.bot.CQSendMessage(p0, p1, p2, p3, p4) + case "send_private_forward_msg": + p0 := p.Get("user_id").Int() + p1 := p.Get("messages") + return c.bot.CQSendPrivateForwardMessage(p0, p1) case "send_private_msg": p0 := p.Get("user_id").Int() p1 := p.Get("group_id").Int() From c275806c62a4cde69d3af338a1e086a2eda04df0 Mon Sep 17 00:00:00 2001 From: synodriver Date: Fri, 27 May 2022 17:10:23 +0800 Subject: [PATCH 375/612] add group_id for get_forward_msg (#1510) --- coolq/api.go | 14 ++++++++++++-- modules/api/api.go | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 8332ac913..fd2566e31 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1572,8 +1572,9 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG { "user_id": n.SenderId, "nickname": n.SenderName, }, - "time": n.Time, - "content": content, + "time": n.Time, + "content": content, + "group_id": n.GroupId, } } return r @@ -1973,6 +1974,15 @@ func (bot *CQBot) CQGetModelShow(model string) global.MSG { }) } +// CQSendGroupSign 群打卡 +// +// https://club.vip.qq.com/onlinestatus/set +// @route(send_group_sign) +func (bot *CQBot) CQSendGroupSign(groupID int64) global.MSG { + bot.Client.SendGroupSign(groupID) + return OK(nil) +} + // CQSetModelShow 设置在线机型 // // https://club.vip.qq.com/onlinestatus/set diff --git a/modules/api/api.go b/modules/api/api.go index 63d087e7a..ae4e92876 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -210,6 +210,9 @@ func (c *Caller) call(action string, p Getter) global.MSG { p1 := p.Get("message") p2 := p.Get("auto_escape").Bool() return c.bot.CQSendGroupMessage(p0, p1, p2) + case "send_group_sign": + p0 := p.Get("group_id").Int() + return c.bot.CQSendGroupSign(p0) case "send_guild_channel_msg": p0 := p.Get("guild_id").Uint() p1 := p.Get("channel_id").Uint() From 43ea459365569968888bd1297f0d34116f9a7ae8 Mon Sep 17 00:00:00 2001 From: MingxuanGame Date: Sat, 28 May 2022 21:54:10 +0800 Subject: [PATCH 376/612] feat: supported finger-guessing message (#1519) --- coolq/cqcode.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 8d9de1d1d..59c7d23e2 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -248,6 +248,13 @@ func toElements(e []message.IMessageElement, source message.Source, raw bool) (r {"value", strconv.FormatInt(int64(o.Value), 10)}, }, } + case *message.FingerGuessingElement: + m = cqcode.Element{ + Type: "rps", + Data: pairs{ + {"value", strconv.FormatInt(int64(o.Value), 10)}, + }, + } case *message.MarketFaceElement: m = cqcode.Element{ Type: "text", @@ -371,6 +378,8 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { } case *message.DiceElement: m = global.MSG{"type": "dice", "data": global.MSG{"value": o.Value}} + case *message.FingerGuessingElement: + m = global.MSG{"type": "rps", "data": global.MSG{"value": o.Value}} case *message.MarketFaceElement: m = global.MSG{"type": "text", "data": global.MSG{"text": o.Name}} case *message.ServiceElement: @@ -964,6 +973,13 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return nil, errors.New("invalid dice value " + value) } return message.NewDice(int32(i)), nil + case "rps": + value := d["value"] + i, _ := strconv.ParseInt(value, 10, 64) + if i < 0 || i > 2 { + return nil, errors.New("invalid finger-guessing value " + value) + } + return message.NewFingerGuessing(int32(i)), nil case "xml": resID := d["resid"] template := cqcode.EscapeValue(d["data"]) From 15602e1daa0d1cf97ac30ad6279375c87fecabdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Mon, 30 May 2022 14:17:34 +0800 Subject: [PATCH 377/612] revert: re-include url in message (#1521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * revert: re-include url in message * fix: wrong writer usage * fix: composite literal uses unkeyed fields * 优化buffer * fix: illegal use of non-zero Builder * fix: illegal use of non-zero Builder * fix: make lint happy * fix: replace io.Writer to *strings.Builder * fix: replace io.Writer to *strings.Builder --- coolq/api.go | 10 +++---- coolq/converter.go | 10 +++---- coolq/cqcode.go | 60 +++++++++++++++++++---------------------- coolq/cqcode/element.go | 48 ++++++++++++++++++++------------- coolq/event.go | 20 +++++++------- 5 files changed, 77 insertions(+), 71 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index fd2566e31..7d99bf012 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1561,7 +1561,7 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG { r := make([]global.MSG, len(nodes)) for i, n := range nodes { bot.checkMedia(n.Message, 0) - content := ToFormattedMessage(n.Message, message.Source{SourceType: message.SourceGroup}, false) + content := ToFormattedMessage(n.Message, message.Source{SourceType: message.SourceGroup}) if len(n.Message) == 1 { if forward, ok := n.Message[0].(*message.ForwardMessage); ok { content = transformNodes(forward.Nodes) @@ -1610,9 +1610,9 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { switch o := msg.(type) { case *db.StoredGroupMessage: m["group_id"] = o.GroupCode - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourceGroup), message.Source{SourceType: message.SourceGroup, PrimaryID: o.GroupCode}, false) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourceGroup), message.Source{SourceType: message.SourceGroup, PrimaryID: o.GroupCode}) case *db.StoredPrivateMessage: - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourcePrivate), message.Source{SourceType: message.SourcePrivate}, false) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, message.SourcePrivate), message.Source{SourceType: message.SourcePrivate}) } return OK(m) } @@ -1657,7 +1657,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "tiny_id": fU64(pull[0].Sender.TinyId), "nickname": pull[0].Sender.Nickname, } - m["message"] = ToFormattedMessage(pull[0].Elements, source, false) + m["message"] = ToFormattedMessage(pull[0].Elements, source) m["reactions"] = convertReactions(pull[0].Reactions) bot.InsertGuildChannelMessage(pull[0]) } else { @@ -1672,7 +1672,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "tiny_id": fU64(channelMsgByDB.Attribute.SenderTinyID), "nickname": channelMsgByDB.Attribute.SenderName, } - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), source, false) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), source) } case message.SourceGuildDirect: // todo(mrs4s): 支持 direct 消息 diff --git a/coolq/converter.go b/coolq/converter.go index 2116c7ac2..d185b64c8 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -65,7 +65,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) *event { SourceType: message.SourceGroup, PrimaryID: m.GroupCode, } - cqm := toStringMessage(m.Elements, source, true) + cqm := toStringMessage(m.Elements, source) typ := "message/group/normal" if m.Sender.Uin == bot.Client.Uin { typ = "message_sent/group/normal" @@ -74,7 +74,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) *event { "anonymous": nil, "font": 0, "group_id": m.GroupCode, - "message": ToFormattedMessage(m.Elements, source, false), + "message": ToFormattedMessage(m.Elements, source), "message_type": "group", "message_seq": m.Id, "raw_message": cqm, @@ -210,11 +210,11 @@ func convertReactions(reactions []*message.GuildMessageEmojiReaction) (r []globa return } -func toStringMessage(m []message.IMessageElement, source message.Source, raw bool) string { - elems := toElements(m, source, raw) +func toStringMessage(m []message.IMessageElement, source message.Source) string { + elems := toElements(m, source) var sb strings.Builder for _, elem := range elems { - sb.WriteString(elem.CQCode()) + elem.WriteCQCodeTo(&sb) } return sb.String() } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 59c7d23e2..17b777785 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -95,7 +95,7 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { // toElements 将消息元素数组转为MSG数组以用于消息上报 // // nolint:govet -func toElements(e []message.IMessageElement, source message.Source, raw bool) (r []cqcode.Element) { +func toElements(e []message.IMessageElement, source message.Source) (r []cqcode.Element) { type pair = cqcode.Pair // simplify code type pairs = []pair @@ -111,7 +111,7 @@ func toElements(e []message.IMessageElement, source message.Source, raw bool) (r elem := cqcode.Element{ Type: "reply", Data: pairs{ - {"id", strconv.FormatInt(int64(id), 10)}, + {K: "id", V: strconv.FormatInt(int64(id), 10)}, }, } if base.ExtraReplyData { @@ -119,7 +119,7 @@ func toElements(e []message.IMessageElement, source message.Source, raw bool) (r pair{K: "seq", V: strconv.FormatInt(int64(replyElem.ReplySeq), 10)}, pair{K: "qq", V: strconv.FormatInt(replyElem.Sender, 10)}, pair{K: "time", V: strconv.FormatInt(int64(replyElem.Time), 10)}, - pair{K: "text", V: toStringMessage(replyElem.Elements, source, true)}, + pair{K: "text", V: toStringMessage(replyElem.Elements, source)}, ) } r = append(r, elem) @@ -139,14 +139,14 @@ func toElements(e []message.IMessageElement, source message.Source, raw bool) (r m = cqcode.Element{ Type: "text", Data: pairs{ - {"text", o.Content}, + {K: "text", V: o.Content}, }, } case *message.LightAppElement: m = cqcode.Element{ Type: "json", Data: pairs{ - {"data", o.Content}, + {K: "data", V: o.Content}, }, } case *message.AtElement: @@ -157,53 +157,51 @@ func toElements(e []message.IMessageElement, source message.Source, raw bool) (r m = cqcode.Element{ Type: "at", Data: pairs{ - {"qq", target}, + {K: "qq", V: target}, }, } case *message.RedBagElement: m = cqcode.Element{ Type: "redbag", Data: pairs{ - {"title", o.Title}, + {K: "title", V: o.Title}, }, } case *message.ForwardElement: m = cqcode.Element{ Type: "forward", Data: pairs{ - {"id", o.ResId}, + {K: "id", V: o.ResId}, }, } case *message.FaceElement: m = cqcode.Element{ Type: "face", Data: pairs{ - {"id", strconv.FormatInt(int64(o.Index), 10)}, + {K: "id", V: strconv.FormatInt(int64(o.Index), 10)}, }, } case *message.VoiceElement: m = cqcode.Element{ Type: "record", Data: pairs{ - {"file", o.Name}, - {"url", o.Url}, + {K: "file", V: o.Name}, + {K: "url", V: o.Url}, }, } case *message.ShortVideoElement: m = cqcode.Element{ Type: "video", Data: pairs{ - {"file", o.Name}, - {"url", o.Url}, + {K: "file", V: o.Name}, + {K: "url", V: o.Url}, }, } case *message.GroupImageElement: data := pairs{ - {"file", hex.EncodeToString(o.Md5) + ".image"}, - {"subType", strconv.FormatInt(int64(o.ImageBizType), 10)}, - } - if raw { - data = append(data, pair{K: "url", V: o.Url}) + {K: "file", V: hex.EncodeToString(o.Md5) + ".image"}, + {K: "subType", V: strconv.FormatInt(int64(o.ImageBizType), 10)}, + {K: "url", V: o.Url}, } switch { case o.Flash: @@ -218,10 +216,8 @@ func toElements(e []message.IMessageElement, source message.Source, raw bool) (r } case *message.GuildImageElement: data := pairs{ - {"file", hex.EncodeToString(o.Md5) + ".image"}, - } - if raw { - data = append(data, pair{K: "url", V: o.Url}) + {K: "file", V: hex.EncodeToString(o.Md5) + ".image"}, + {K: "url", V: o.Url}, } m = cqcode.Element{ Type: "image", @@ -229,10 +225,8 @@ func toElements(e []message.IMessageElement, source message.Source, raw bool) (r } case *message.FriendImageElement: data := pairs{ - {"file", hex.EncodeToString(o.Md5) + ".image"}, - } - if raw { - data = append(data, pair{K: "url", V: o.Url}) + {K: "file", V: hex.EncodeToString(o.Md5) + ".image"}, + {K: "url", V: o.Url}, } if o.Flash { data = append(data, pair{K: "type", V: "flash"}) @@ -245,29 +239,29 @@ func toElements(e []message.IMessageElement, source message.Source, raw bool) (r m = cqcode.Element{ Type: "dice", Data: pairs{ - {"value", strconv.FormatInt(int64(o.Value), 10)}, + {K: "value", V: strconv.FormatInt(int64(o.Value), 10)}, }, } case *message.FingerGuessingElement: m = cqcode.Element{ Type: "rps", Data: pairs{ - {"value", strconv.FormatInt(int64(o.Value), 10)}, + {K: "value", V: strconv.FormatInt(int64(o.Value), 10)}, }, } case *message.MarketFaceElement: m = cqcode.Element{ Type: "text", Data: pairs{ - {"text", o.Name}, + {K: "text", V: o.Name}, }, } case *message.ServiceElement: m = cqcode.Element{ Type: "xml", Data: pairs{ - {"data", o.Content}, - {"resid", o.ResId}, + {K: "data", V: o.Content}, + {K: "resid", V: o.ResId}, }, } if !strings.Contains(o.Content, " Date: Mon, 30 May 2022 14:19:54 +0800 Subject: [PATCH 378/612] feat: use the same buffer pool as MiraiGo (#1297) --- global/buffer.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/global/buffer.go b/global/buffer.go index 73079f32e..e899dae3f 100644 --- a/global/buffer.go +++ b/global/buffer.go @@ -2,26 +2,16 @@ package global import ( "bytes" - "sync" -) -var bufferPool = sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, -} + "github.com/Mrs4s/MiraiGo/binary" // 和 MiraiGo 共用同一 buffer 池 +) // NewBuffer 从池中获取新 bytes.Buffer func NewBuffer() *bytes.Buffer { - return bufferPool.Get().(*bytes.Buffer) + return (*bytes.Buffer)(binary.SelectWriter()) } // PutBuffer 将 Buffer放入池中 func PutBuffer(buf *bytes.Buffer) { - // See https://golang.org/issue/23199 - const maxSize = 1 << 16 - if buf != nil && buf.Cap() < maxSize { // 对于大Buffer直接丢弃 - buf.Reset() - bufferPool.Put(buf) - } + binary.PutWriter((*binary.Writer)(buf)) } From cc3745130eff2549788218ea8d9e283a845b9ff6 Mon Sep 17 00:00:00 2001 From: Ink33 Date: Tue, 31 May 2022 14:53:34 +0800 Subject: [PATCH 379/612] chore: bump yaml.in/yaml.v3 v3.0.0 to v3.0.1 (#1523) Signed-off-by: Ink33 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5b70bf7b2..6c061290f 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v3 v3.0.1 ) require ( diff --git a/go.sum b/go.sum index 1be6fb9bb..225fc2df6 100644 --- a/go.sum +++ b/go.sum @@ -176,8 +176,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= From 70d1bfe510f1c425a76f765e0c67a9920061f3f3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 5 Jun 2022 16:54:52 +0800 Subject: [PATCH 380/612] dep: update dependency --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6c061290f..575c53953 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220524053130-007f228e1092 + github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-hide-param v0.1.4 @@ -25,7 +25,7 @@ require ( ) require ( - github.com/RomiChan/protobuf v0.1.1-0.20220524030518-4f349493f9da // indirect + github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect diff --git a/go.sum b/go.sum index 225fc2df6..9674779df 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220524053130-007f228e1092 h1:8TDeBGcywkNWXAmAVVL7u3xIXdlSD0IfXCs/8qf2G0I= -github.com/Mrs4s/MiraiGo v0.0.0-20220524053130-007f228e1092/go.mod h1:7jCgifOheeioTGhQnNUNlinEx7XfBszCEueyZCkTc2A= -github.com/RomiChan/protobuf v0.1.1-0.20220524030518-4f349493f9da h1:T+Sc+QtOfpIRnfnOXaToyLvxmUmFwF2iaqxpJddI4Cc= -github.com/RomiChan/protobuf v0.1.1-0.20220524030518-4f349493f9da/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= +github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a h1:DVrEVvLNKb53wTTduRdC+hZ80S/5G9qFmKYmCmIMVws= +github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= +github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= +github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From d25209c366a036befa01595e27384673898c0296 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 5 Jun 2022 17:35:37 +0800 Subject: [PATCH 381/612] fix #1520 --- coolq/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 7d99bf012..1eeb6be18 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -870,7 +870,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType if msgTime == 0 { msgTime = ts.Unix() } - name := e.Get("data.name").Str + name := e.Get("data.[name,nickname].0").Str c := e.Get("data.content") if c.IsArray() { nested := false From e4c73d59a5cba3d2c2fa80ff2602d95a5cc9e618 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 5 Jun 2022 19:04:39 +0800 Subject: [PATCH 382/612] fix #1527 --- server/http.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/server/http.go b/server/http.go index 710610dee..3fd7c3779 100644 --- a/server/http.go +++ b/server/http.go @@ -363,15 +363,16 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { } req.Header = header res, err = c.client.Do(req) - if err != nil { - if i < c.MaxRetries { - log.Warnf("上报 Event 数据到 %v 失败: %v 将进行第 %d 次重试", c.addr, err, i+1) - } else { - log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上限", e.JSONBytes(), c.addr, err) - return - } - time.Sleep(time.Millisecond * time.Duration(c.RetriesInterval)) + if err == nil { + break + } + if i < c.MaxRetries { + log.Warnf("上报 Event 数据到 %v 失败: %v 将进行第 %d 次重试", c.addr, err, i+1) + } else { + log.Warnf("上报 Event 数据 %s 到 %v 失败: %v 停止上报:已达重试上限", e.JSONBytes(), c.addr, err) + return } + time.Sleep(time.Millisecond * time.Duration(c.RetriesInterval)) } defer res.Body.Close() From 12391f8df386ab623e460b388cc6d2a9552682d4 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 5 Jun 2022 21:34:48 +0800 Subject: [PATCH 383/612] ci: change release to draft --- .goreleaser.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.goreleaser.yml b/.goreleaser.yml index cd9d92c3d..c37988653 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -3,6 +3,9 @@ env: before: hooks: - go mod tidy +release: + draft: true + discussion_category_name: General builds: - id: nowin env: From 23eea9188f0b87afc999e8a9773c5ee199a76b6f Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 5 Jun 2022 21:41:49 +0800 Subject: [PATCH 384/612] ci: update go version --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3963c8d6e..a2fb331b1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: '1.17' + go-version: '1.18' - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 From 7d97216612153fc1a951b0153cd69c59eeba2c33 Mon Sep 17 00:00:00 2001 From: Cinte Date: Mon, 6 Jun 2022 12:38:50 +0800 Subject: [PATCH 385/612] =?UTF-8?q?fix:=20data=E6=96=87=E4=BB=B6=E5=A4=B9?= =?UTF-8?q?=E6=9D=83=E9=99=90=E9=97=AE=E9=A2=98=20(#1529)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: data文件夹权限问题 当权限644时候,data文件夹会由于没有执行权限而无法创建后续的图片...等一系列文件夹。 * 0o744->0o755 --- cmd/gocq/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 1cb85caee..dacaead6c 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -81,7 +81,7 @@ func Main() { mkCacheDir := func(path string, _type string) { if !global.PathExists(path) { - if err := os.MkdirAll(path, 0o644); err != nil { + if err := os.MkdirAll(path, 0o755); err != nil { log.Fatalf("创建%s缓存文件夹失败: %v", _type, err) } } From b013f662093376d372d598560552b26b94bf9ece Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 6 Jun 2022 22:40:09 +0800 Subject: [PATCH 386/612] fix #1514 --- modules/mime/mime.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/mime/mime.go b/modules/mime/mime.go index dbaa831a8..f6bfa4baa 100644 --- a/modules/mime/mime.go +++ b/modules/mime/mime.go @@ -31,6 +31,9 @@ func checkImage(r io.ReadSeeker) (ok bool, t string) { if base.SkipMimeScan { return true, "" } + if r == nil { + return false, "image/nil-stream" + } t = scan(r) switch t { case "image/bmp", "image/gif", "image/jpeg", "image/png", "image/webp": From a4a8e94b279d6acce603ecce453e4b3de42a0b37 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 11 Jun 2022 18:14:22 +0800 Subject: [PATCH 387/612] update dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4476534d4..4a62380f1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a + github.com/Mrs4s/MiraiGo v0.0.0-20220606142526-b56c61f5b4d6 github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 diff --git a/go.sum b/go.sum index 265b5fa3d..98540b92d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a h1:DVrEVvLNKb53wTTduRdC+hZ80S/5G9qFmKYmCmIMVws= -github.com/Mrs4s/MiraiGo v0.0.0-20220605085305-ae33763fe10a/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= +github.com/Mrs4s/MiraiGo v0.0.0-20220606142526-b56c61f5b4d6 h1:nDIxbI1lLfX4LsslhY5pfYhpb9LRprin59eTEnL8aWU= +github.com/Mrs4s/MiraiGo v0.0.0-20220606142526-b56c61f5b4d6/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= From babf35e495b204ec1e918ed9cb269e692851d520 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 13 Jun 2022 22:31:38 +0800 Subject: [PATCH 388/612] coolq: remove redundant message type Fixes #1534 --- coolq/converter.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/coolq/converter.go b/coolq/converter.go index d185b64c8..e1053572a 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -71,13 +71,12 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) *event { typ = "message_sent/group/normal" } gm := global.MSG{ - "anonymous": nil, - "font": 0, - "group_id": m.GroupCode, - "message": ToFormattedMessage(m.Elements, source), - "message_type": "group", - "message_seq": m.Id, - "raw_message": cqm, + "anonymous": nil, + "font": 0, + "group_id": m.GroupCode, + "message": ToFormattedMessage(m.Elements, source), + "message_seq": m.Id, + "raw_message": cqm, "sender": global.MSG{ "age": 0, "area": "", From 7e24f8b6e62c8635a895bb248a576f692fb5cb5d Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 14 Jun 2022 17:31:09 +0800 Subject: [PATCH 389/612] fix buffer race --- coolq/bot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index 7723ea1eb..17722488a 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -595,10 +595,10 @@ func (bot *CQBot) dispatch(ev *event) { for _, f := range bot.events { go func(fn func(*Event)) { defer func() { - wg.Done() if pan := recover(); pan != nil { log.Warnf("处理事件 %v 时出现错误: %v \n%s", event.JSONString(), pan, debug.Stack()) } + wg.Done() }() start := time.Now() From 7349fd4b82a7e06fe423cfc755bac497a349fb0f Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 14 Jun 2022 17:39:56 +0800 Subject: [PATCH 390/612] update dep fix #1538 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4a62380f1..bd31bcfac 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220606142526-b56c61f5b4d6 + github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 diff --git a/go.sum b/go.sum index 98540b92d..755c91736 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220606142526-b56c61f5b4d6 h1:nDIxbI1lLfX4LsslhY5pfYhpb9LRprin59eTEnL8aWU= -github.com/Mrs4s/MiraiGo v0.0.0-20220606142526-b56c61f5b4d6/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= +github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c h1:LGJoAua1bYxx28255h02owv66v9eWkSwcZx/jEO79Z0= +github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= From f900fd62fb732e4a9a6f3ec96f9c3546e58b1eaa Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 15 Jun 2022 16:02:52 +0800 Subject: [PATCH 391/612] feat: routing api by version & inject version field --- cmd/api-generator/main.go | 245 ++++++++++++++++++++++++++++---------- coolq/api.go | 43 ++++--- coolq/api_v12.go | 23 ++++ coolq/bot.go | 2 +- modules/api/api.go | 49 +++++--- modules/api/caller.go | 6 +- server/http.go | 20 +++- server/websocket.go | 2 +- 8 files changed, 284 insertions(+), 106 deletions(-) create mode 100644 coolq/api_v12.go diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index cf3a2367e..64d8a0681 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -22,38 +22,102 @@ type Param struct { } type Router struct { - Func string - Path []string - Params []Param + Func string + Version []uint16 + Path []string + PathV11 []string // v11 only + PathV12 []string // v12 only + Params []Param } type generator struct { out io.Writer } +const ( + PathAll = 0 + PathV11 = 11 + PathV12 = 12 +) + func (g *generator) WriteString(s string) { io.WriteString(g.out, s) } func (g *generator) generate(routers []Router) { + var actions []string // for onebot v12 get_supported_actions + for _, router := range routers { + if len(router.PathV12) > 0 { + actions = append(actions, router.PathV12...) + } + if len(router.Path) > 0 { + actions = append(actions, router.Path...) + } + } + for i := range actions { + actions[i] = `"` + actions[i] + `"` + } + + // TODO: v12 和 all 的 switch-case 由常量改为数组寻址, 以利用 get_supported_actions g.WriteString("// Code generated by cmd/api-generator. DO NOT EDIT.\n\n") g.WriteString("package api\n\nimport (\n\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/coolq\"\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/global\"\n") g.WriteString(")\n\n") - g.WriteString(`func (c *Caller) call(action string, p Getter) global.MSG { - switch action { - default: - return coolq.Failed(404, "API_NOT_FOUND", "API不存在")` + "\n") + g.WriteString(fmt.Sprintf(`func (c *Caller) call(action string, version uint16, p Getter) global.MSG { + if version == 12 { + if action == "get_supported_actions" { + return coolq.OK([]string{%v}) + } + switch action { +`, strings.Join(actions, ","))) + for _, router := range routers { + g.router(router, PathV12) + } + io.WriteString(g.out, `}}`) + io.WriteString(g.out, "\n") + g.WriteString(`if version == 11 { + switch action { + `) + for _, router := range routers { + g.router(router, PathV11) + } + io.WriteString(g.out, `}}`) + io.WriteString(g.out, "\n") + io.WriteString(g.out, "switch action {\n") for _, router := range routers { - g.router(router) + g.router(router, PathAll) } - io.WriteString(g.out, ` }}`) + io.WriteString(g.out, `}`) + io.WriteString(g.out, "\n") + io.WriteString(g.out, "return coolq.Failed(404, \"API_NOT_FOUND\", \"API不存在\")}") } -func (g *generator) router(router Router) { +func (g *generator) router(router Router, pathVersion int) { + /* + checkVersion := func(v uint16) bool { + for _, ver := range router.Version { + if ver == v { + return true + } + } + return false + } + */ + + path := router.Path + if pathVersion == PathV11 { + path = router.PathV11 + } + if pathVersion == PathV12 { + path = router.PathV12 + } + if len(path) == 0 { + return + } + g.WriteString(`case `) - for i, p := range router.Path { + for i, p := range path { if i != 0 { g.WriteString(`, `) } @@ -61,7 +125,19 @@ func (g *generator) router(router Router) { } g.WriteString(":\n") + if len(router.Version) == 1 { // 目前来说只需要判断一个版本的情况 + check := make([]string, 0, len(router.Version)) + for _, ver := range router.Version { + check = append(check, fmt.Sprintf("version != %v", ver)) + } + fmt.Fprintf(g.out, "if %v {\n", strings.Join(check, " && ")) + fmt.Fprintf(g.out, "return coolq.Failed(405, \"VERSION_ERROR\", \"API版本不匹配\")}\n") + } + for i, p := range router.Params { + if p.Name == "version" { + continue + } if p.Default == "" { v := "p.Get(" + strconv.Quote(p.Name) + ")" fmt.Fprintf(g.out, "p%d := %s\n", i, conv(v, p.Type)) @@ -73,10 +149,14 @@ func (g *generator) router(router Router) { } g.WriteString("\t\treturn c.bot." + router.Func + "(") - for i := range router.Params { + for i, p := range router.Params { if i != 0 { g.WriteString(", ") } + if p.Name == "version" { + fmt.Fprintf(g.out, "version") + continue + } fmt.Fprintf(g.out, "p%d", i) } g.WriteString(")\n") @@ -100,6 +180,8 @@ func conv(v, t string) string { return v + ".Uint()" case "uint32": return "uint32(" + v + ".Uint())" + case "uint16": + return "uint16(" + v + ".Uint())" } } @@ -108,74 +190,109 @@ func main() { src := flag.String("path", "", "source file") flag.Parse() fset := token.NewFileSet() - file, err := parser.ParseFile(fset, *src, nil, parser.ParseComments) - if err != nil { - panic(err) - } - - for _, decl := range file.Decls { - switch decl := decl.(type) { - case *ast.FuncDecl: - if !decl.Name.IsExported() || decl.Recv == nil { - continue - } - if st, ok := decl.Recv.List[0].Type.(*ast.StarExpr); !ok || st.X.(*ast.Ident).Name != "CQBot" { - continue - } - router := Router{Func: decl.Name.Name} + for _, s := range strings.Split(*src, ",") { + file, err := parser.ParseFile(fset, s, nil, parser.ParseComments) + if err != nil { + panic(err) + } - // compute params - for _, p := range decl.Type.Params.List { - var typ string - switch t := p.Type.(type) { - case *ast.Ident: - typ = t.Name - case *ast.SelectorExpr: - typ = t.X.(*ast.Ident).Name + "." + t.Sel.Name + for _, decl := range file.Decls { + switch decl := decl.(type) { + case *ast.FuncDecl: + if !decl.Name.IsExported() || decl.Recv == nil { + continue } - for _, name := range p.Names { - router.Params = append(router.Params, Param{Name: snakecase(name.Name), Type: typ}) + if st, ok := decl.Recv.List[0].Type.(*ast.StarExpr); !ok || st.X.(*ast.Ident).Name != "CQBot" { + continue } - } + router := Router{Func: decl.Name.Name} - for _, comment := range decl.Doc.List { - annotation, args := match(comment.Text) - switch annotation { - case "route": - for _, route := range strings.Split(args, ",") { - router.Path = append(router.Path, unquote(route)) + // compute params + for _, p := range decl.Type.Params.List { + var typ string + switch t := p.Type.(type) { + case *ast.Ident: + typ = t.Name + case *ast.SelectorExpr: + typ = t.X.(*ast.Ident).Name + "." + t.Sel.Name } - case "default": - for name, value := range parseMap(args, "=") { - for i, p := range router.Params { - if p.Name == name { - router.Params[i].Default = convDefault(value, p.Type) + for _, name := range p.Names { + router.Params = append(router.Params, Param{Name: snakecase(name.Name), Type: typ}) + } + } + + for _, comment := range decl.Doc.List { + annotation, args := match(comment.Text) + switch annotation { + case "route": + for _, route := range strings.Split(args, ",") { + router.Path = append(router.Path, unquote(route)) + } + case "route11": + for _, route := range strings.Split(args, ",") { + router.PathV11 = append(router.PathV11, unquote(route)) + } + case "route12": + for _, route := range strings.Split(args, ",") { + router.PathV12 = append(router.PathV12, unquote(route)) + } + case "default": + for name, value := range parseMap(args, "=") { + for i, p := range router.Params { + if p.Name == name { + router.Params[i].Default = convDefault(value, p.Type) + } } } - } - case "rename": - for name, value := range parseMap(args, "->") { - for i, p := range router.Params { - if p.Name == name { - router.Params[i].Name = value + case "rename": + for name, value := range parseMap(args, "->") { + for i, p := range router.Params { + if p.Name == name { + router.Params[i].Name = value + } + } + } + case "version": + version := strings.Split(args, ",") + for _, v := range version { + if i, err := strconv.ParseUint(v, 10, 16); err == nil { + router.Version = append(router.Version, uint16(i)) } } } + sort.Slice(router.Path, func(i, j int) bool { + return router.Path[i] < router.Path[j] + }) + sort.Slice(router.PathV11, func(i, j int) bool { + return router.PathV11[i] < router.PathV11[j] + }) + sort.Slice(router.PathV12, func(i, j int) bool { + return router.PathV12[i] < router.PathV12[j] + }) + } + if router.Path != nil || router.PathV11 != nil || router.PathV12 != nil { + routers = append(routers, router) + } else { + println(decl.Name.Name) } - sort.Slice(router.Path, func(i, j int) bool { - return router.Path[i] < router.Path[j] - }) - } - if router.Path != nil { - routers = append(routers, router) - } else { - println(decl.Name.Name) } } } sort.Slice(routers, func(i, j int) bool { - return routers[i].Path[0] < routers[j].Path[0] + path := func(r Router) string { + if r.Path != nil { + return r.Path[0] + } + if r.PathV11 != nil { + return r.PathV11[0] + } + if r.PathV12 != nil { + return r.PathV12[0] + } + return "" + } + return path(routers[i]) < path(routers[j]) }) out := new(bytes.Buffer) diff --git a/coolq/api.go b/coolq/api.go index 1eeb6be18..113b18183 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/segmentio/asm/base64" "github.com/Mrs4s/MiraiGo/binary" @@ -25,7 +26,6 @@ import ( "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -47,7 +47,8 @@ var defaultPageToken = guildMemberPageToken{ // CQGetLoginInfo 获取登录号信息 // // https://git.io/Jtz1I -// @route(get_login_info) +// @route11(get_login_info) +// @route12(get_self_info) func (bot *CQBot) CQGetLoginInfo() global.MSG { return OK(global.MSG{"user_id": bot.Client.Uin, "nickname": bot.Client.Nickname}) } @@ -655,7 +656,7 @@ func (bot *CQBot) CQGroupFileDeleteFile(groupID int64, id string, busID int32) g // // https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D-%E9%9A%90%E8%97%8F-api // @route(.get_word_slices) -func (bot *CQBot) CQGetWordSlices(content string) global.MSG { +func (bot *CQBot) CQGetWordSlices(content string, version uint16) global.MSG { slices, err := bot.Client.GetWordSegmentation(content) if err != nil { return Failed(100, "WORD_SEGMENTATION_API_ERROR", err.Error()) @@ -1342,7 +1343,8 @@ func (bot *CQBot) CQGetGroupHonorInfo(groupID int64, t string) global.MSG { // CQGetStrangerInfo 获取陌生人信息 // // https://git.io/Jtz17 -// @route(get_stranger_info) +// @route11(get_stranger_info) +// @route12(get_user_info) func (bot *CQBot) CQGetStrangerInfo(userID int64) global.MSG { info, err := bot.Client.GetSummaryInfo(userID) if err != nil { @@ -1755,7 +1757,7 @@ func (bot *CQBot) CQGetOnlineClients(noCache bool) global.MSG { // CQCanSendImage 检查是否可以发送图片(此处永远返回true) // // https://git.io/Jtz1N -// @route(can_send_image) +// @route11(can_send_image) func (bot *CQBot) CQCanSendImage() global.MSG { return OK(global.MSG{"yes": true}) } @@ -1763,7 +1765,7 @@ func (bot *CQBot) CQCanSendImage() global.MSG { // CQCanSendRecord 检查是否可以发送语音(此处永远返回true) // // https://git.io/Jtz1x -// @route(can_send_record) +// @route11(can_send_record) func (bot *CQBot) CQCanSendRecord() global.MSG { return OK(global.MSG{"yes": true}) } @@ -1831,15 +1833,22 @@ func (bot *CQBot) CQSetGroupAnonymousBan(groupID int64, flag string, duration in // // https://git.io/JtzMe // @route(get_status) -func (bot *CQBot) CQGetStatus() global.MSG { +func (bot *CQBot) CQGetStatus(version uint16) global.MSG { + if version == 11 { + return OK(global.MSG{ + "app_initialized": true, + "app_enabled": true, + "plugins_good": nil, + "app_good": true, + "online": bot.Client.Online.Load(), + "good": bot.Client.Online.Load(), + "stat": bot.Client.GetStatistics(), + }) + } return OK(global.MSG{ - "app_initialized": true, - "app_enabled": true, - "plugins_good": nil, - "app_good": true, - "online": bot.Client.Online.Load(), - "good": bot.Client.Online.Load(), - "stat": bot.Client.GetStatistics(), + "online": bot.Client.Online.Load(), + "good": bot.Client.Online.Load(), + "stat": bot.Client.GetStatistics(), }) } @@ -1917,7 +1926,7 @@ func (bot *CQBot) CQCheckURLSafely(url string) global.MSG { // CQGetVersionInfo 获取版本信息 // // https://git.io/JtwUs -// @route(get_version_info) +// @route11(get_version_info) func (bot *CQBot) CQGetVersionInfo() global.MSG { wd, _ := os.Getwd() return OK(global.MSG{ @@ -2044,7 +2053,7 @@ func (bot *CQBot) CQReloadEventFilter(file string) global.MSG { // OK 生成成功返回值 func OK(data interface{}) global.MSG { - return global.MSG{"data": data, "retcode": 0, "status": "ok"} + return global.MSG{"data": data, "retcode": 0, "status": "ok", "message": ""} } // Failed 生成失败返回值 @@ -2056,7 +2065,7 @@ func Failed(code int, msg ...string) global.MSG { if len(msg) > 1 { w = msg[1] } - return global.MSG{"data": nil, "retcode": code, "msg": m, "wording": w, "status": "failed"} + return global.MSG{"data": nil, "retcode": code, "msg": m, "wording": w, "message": w, "status": "failed"} } func limitedString(str string) string { diff --git a/coolq/api_v12.go b/coolq/api_v12.go new file mode 100644 index 000000000..5b4f0a60f --- /dev/null +++ b/coolq/api_v12.go @@ -0,0 +1,23 @@ +package coolq + +import ( + "runtime" + + "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +// CQGetVersion 获取版本信息 OneBotV12 +// +// https://git.io/JtwUs +// @route12(get_version) +func (bot *CQBot) CQGetVersion() global.MSG { + return OK(global.MSG{ + "impl": "go_cqhttp", + "platform": "qq", + "version": base.Version, + "onebot_version": 12, + "runtime_version": runtime.Version(), + "runtime_os": runtime.GOOS, + }) +} diff --git a/coolq/bot.go b/coolq/bot.go index 17722488a..9cb7ebb92 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -111,7 +111,7 @@ func NewQQBot(cli *client.QQClient) *CQBot { for { <-t.C bot.dispatchEvent("meta_event/heartbeat", global.MSG{ - "status": bot.CQGetStatus()["data"], + "status": bot.CQGetStatus(11)["data"], "interval": base.HeartbeatInterval.Milliseconds(), }) } diff --git a/modules/api/api.go b/modules/api/api.go index ae4e92876..656d40b04 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -7,13 +7,40 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) -func (c *Caller) call(action string, p Getter) global.MSG { +func (c *Caller) call(action string, version uint16, p Getter) global.MSG { + if version == 12 { + if action == "get_supported_actions" { + return coolq.OK([]string{".get_word_slices", ".handle_quick_operation", ".ocr_image", "ocr_image", "_get_group_notice", "_get_model_show", "_send_group_notice", "_set_model_show", "check_url_safely", "create_group_file_folder", "create_guild_role", "delete_essence_msg", "delete_friend", "delete_group_file", "delete_group_folder", "delete_guild_role", "delete_msg", "delete_unidirectional_friend", "download_file", "get_essence_msg_list", "get_forward_msg", "get_friend_list", "get_group_at_all_remain", "get_group_file_system_info", "get_group_file_url", "get_group_files_by_folder", "get_group_honor_info", "get_group_info", "get_group_list", "get_group_member_info", "get_group_member_list", "get_group_msg_history", "get_group_root_files", "get_group_system_msg", "get_guild_channel_list", "get_guild_list", "get_guild_member_list", "get_guild_member_profile", "get_guild_meta_by_guest", "get_guild_msg", "get_guild_roles", "get_guild_service_profile", "get_image", "get_self_info", "get_msg", "get_online_clients", "get_status", "get_user_info", "get_topic_channel_feeds", "get_unidirectional_friend_list", "get_version", "mark_msg_as_read", "qidian_get_account_info", "reload_event_filter", "send_forward_msg", "send_group_forward_msg", "send_group_msg", "send_group_sign", "send_guild_channel_msg", "send_msg", "send_private_forward_msg", "send_private_msg", "set_essence_msg", "set_friend_add_request", "set_group_add_request", "set_group_admin", "set_group_anonymous_ban", "set_group_ban", "set_group_card", "set_group_kick", "set_group_leave", "set_group_name", "set_group_portrait", "set_group_special_title", "set_group_whole_ban", "set_guild_member_role", "set_qq_profile", "update_guild_role", "upload_group_file"}) + } + switch action { + case "get_self_info": + return c.bot.CQGetLoginInfo() + case "get_user_info": + p0 := p.Get("user_id").Int() + return c.bot.CQGetStrangerInfo(p0) + case "get_version": + return c.bot.CQGetVersion() + } + } + if version == 11 { + switch action { + case "can_send_image": + return c.bot.CQCanSendImage() + case "can_send_record": + return c.bot.CQCanSendRecord() + case "get_login_info": + return c.bot.CQGetLoginInfo() + case "get_stranger_info": + p0 := p.Get("user_id").Int() + return c.bot.CQGetStrangerInfo(p0) + case "get_version_info": + return c.bot.CQGetVersionInfo() + } + } switch action { - default: - return coolq.Failed(404, "API_NOT_FOUND", "API不存在") case ".get_word_slices": p0 := p.Get("content").String() - return c.bot.CQGetWordSlices(p0) + return c.bot.CQGetWordSlices(p0, version) case ".handle_quick_operation": p0 := p.Get("context") p1 := p.Get("operation") @@ -36,10 +63,6 @@ func (c *Caller) call(action string, p Getter) global.MSG { p0 := p.Get("model").String() p1 := p.Get("model_show").String() return c.bot.CQSetModelShow(p0, p1) - case "can_send_image": - return c.bot.CQCanSendImage() - case "can_send_record": - return c.bot.CQCanSendRecord() case "check_url_safely": p0 := p.Get("url").String() return c.bot.CQCheckURLSafely(p0) @@ -166,8 +189,6 @@ func (c *Caller) call(action string, p Getter) global.MSG { case "get_image": p0 := p.Get("file").String() return c.bot.CQGetImage(p0) - case "get_login_info": - return c.bot.CQGetLoginInfo() case "get_msg": p0 := int32(p.Get("message_id").Int()) return c.bot.CQGetMessage(p0) @@ -175,18 +196,13 @@ func (c *Caller) call(action string, p Getter) global.MSG { p0 := p.Get("no_cache").Bool() return c.bot.CQGetOnlineClients(p0) case "get_status": - return c.bot.CQGetStatus() - case "get_stranger_info": - p0 := p.Get("user_id").Int() - return c.bot.CQGetStrangerInfo(p0) + return c.bot.CQGetStatus(version) case "get_topic_channel_feeds": p0 := p.Get("guild_id").Uint() p1 := p.Get("channel_id").Uint() return c.bot.CQGetTopicChannelFeeds(p0, p1) case "get_unidirectional_friend_list": return c.bot.CQGetUnidirectionalFriendList() - case "get_version_info": - return c.bot.CQGetVersionInfo() case "mark_msg_as_read": p0 := int32(p.Get("message_id").Int()) return c.bot.CQMarkMessageAsRead(p0) @@ -338,4 +354,5 @@ func (c *Caller) call(action string, p Getter) global.MSG { p3 := p.Get("folder").String() return c.bot.CQUploadGroupFile(p0, p1, p2, p3) } + return coolq.Failed(404, "API_NOT_FOUND", "API不存在") } diff --git a/modules/api/caller.go b/modules/api/caller.go index ee4fea19e..7105fd422 100644 --- a/modules/api/caller.go +++ b/modules/api/caller.go @@ -8,7 +8,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) -//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -path=./../../coolq/api.go +//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -path=./../../coolq/api.go,./../../coolq/api_v12.go // Getter 参数获取 type Getter interface { @@ -25,13 +25,13 @@ type Caller struct { } // Call specific API -func (c *Caller) Call(action string, p Getter) global.MSG { +func (c *Caller) Call(action string, version uint16, p Getter) global.MSG { for _, fn := range c.handlers { if ret := fn(action, p); ret != nil { return ret } } - return c.call(action, p) + return c.call(action, version, p) } // Use add handlers to the API caller diff --git a/server/http.go b/server/http.go index 3fd7c3779..612fefdd4 100644 --- a/server/http.go +++ b/server/http.go @@ -34,6 +34,7 @@ import ( // HTTPServer HTTP通信相关配置 type HTTPServer struct { Disabled bool `yaml:"disabled"` + Version uint16 `yaml:"version"` Address string `yaml:"address"` Host string `yaml:"host"` Port int `yaml:"port"` @@ -57,6 +58,7 @@ type httpServerPost struct { type httpServer struct { api *api.Caller accessToken string + version uint16 } // HTTPClient 反向HTTP上报客户端 @@ -81,6 +83,7 @@ type httpCtx struct { const httpDefault = ` - http: # HTTP 通信设置 address: 0.0.0.0:5700 # HTTP监听地址 + version: 11 # OneBot协议版本, 支持 11/12 timeout: 5 # 反向 HTTP 超时时间, 单位秒,<5 时将被忽略 long-polling: # 长轮询拓展 enabled: false # 是否开启 @@ -150,6 +153,13 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request contentType := request.Header.Get("Content-Type") switch request.Method { case http.MethodPost: + // todo: msg pack + if s.version == 12 && strings.Contains(contentType, "application/msgpack") { + log.Warnf("请求 %v 数据类型暂不支持: MsgPack", request.RequestURI) + writer.WriteHeader(http.StatusUnsupportedMediaType) + return + } + if strings.Contains(contentType, "application/json") { body, err := io.ReadAll(request.Body) if err != nil { @@ -190,12 +200,12 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request if request.URL.Path == "/" { action := strings.TrimSuffix(ctx.Get("action").Str, "_async") log.Debugf("HTTPServer接收到API调用: %v", action) - response = s.api.Call(action, ctx.Get("params")) + response = s.api.Call(action, s.version, ctx.Get("params")) } else { action := strings.TrimPrefix(request.URL.Path, "/") action = strings.TrimSuffix(action, "_async") log.Debugf("HTTPServer接收到API调用: %v", action) - response = s.api.Call(action, &ctx) + response = s.api.Call(action, s.version, &ctx) } writer.Header().Set("Content-Type", "application/json; charset=utf-8") @@ -245,9 +255,11 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) { case conf.Disabled: return } - + if conf.Version != 11 && conf.Version != 12 { + conf.Version = 11 + } network, addr := "tcp", conf.Address - s := &httpServer{accessToken: conf.AccessToken} + s := &httpServer{accessToken: conf.AccessToken, version: conf.Version} switch { case conf.Address != "": uri, err := url.Parse(conf.Address) diff --git a/server/websocket.go b/server/websocket.go index f6000172f..696928c22 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -470,7 +470,7 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { j := gjson.Parse(utils.B2S(payload)) t := strings.TrimSuffix(j.Get("action").Str, "_async") log.Debugf("WS接收到API调用: %v 参数: %v", t, j.Get("params").Raw) - ret := c.apiCaller.Call(t, j.Get("params")) + ret := c.apiCaller.Call(t, 11, j.Get("params")) if j.Get("echo").Exists() { ret["echo"] = j.Get("echo").Value() } From 847ef6d415a19182ee40161b6352333e325e6a0b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Jun 2022 08:04:25 +0000 Subject: [PATCH 392/612] ci(chore): Fix stylings --- coolq/api.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 113b18183..039ed995c 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -14,9 +14,10 @@ import ( "strings" "time" - "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/segmentio/asm/base64" + "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" From c66d612b9a512f480232f359b733b5bcef42c7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Wed, 15 Jun 2022 22:43:04 +0800 Subject: [PATCH 393/612] =?UTF-8?q?feat:=20=E8=BD=AC=E5=8F=91=E6=97=B6?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=85=8D=E7=BD=AE=E6=98=AF=E5=90=A6@all?= =?UTF-8?q?=EF=BC=8C=E6=96=B9=E4=BE=BF=E5=9C=A8=E6=AF=8F=E5=A4=A9@all?= =?UTF-8?q?=E6=AC=A1=E6=95=B0=E4=BD=BF=E7=94=A8=E5=AE=8C=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E5=80=99=EF=BC=8C=E4=BB=8D=E5=8F=AF=E4=BB=A5=E5=8F=91=E9=80=81?= =?UTF-8?q?=E5=A4=8D=E8=AF=BB=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/config.go | 1 + qqrobot/qq_robot.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/qqrobot/config.go b/qqrobot/config.go index b0e3ea360..68717d784 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -129,6 +129,7 @@ type RuleConfig struct { ForwardToGroups []int64 `toml:"forward_to_groups"` // 将消息转发到该QQ群列表 RepeatToGroups []int64 `toml:"repeat_to_groups"` // 将消息复读到该QQ群列表 RepeatToGroupTypes []string `toml:"repeat_to_group_types"` // 复读适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + RepeatAtAll bool `toml:"repeat_at_all"` // 复读时,在开头加上@全体 FoodSiteURLList []string `toml:"food_site_url_list"` // 美食图片来源网站列表 FoodDescription string `toml:"food_description"` // 美食描述,参数:$food_name$=食物名字 RevokeMessage bool `toml:"revoke_message"` // 是否撤回该条消息 diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index b3ffb4527..9a2630221 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -572,7 +572,7 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } } for idx, repeatMessages := range r.getForwardMessagesList(m, true) { - if idx == 0 { + if config.RepeatAtAll && idx == 0 { repeatMessages.Elements = append([]message.IMessageElement{ // 第一条转发的消息加上 @all message.AtAll(), From 672dafdb9d9069162883e1f3f1620f7070ffc102 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Fri, 17 Jun 2022 15:39:35 +0800 Subject: [PATCH 394/612] feat: cross version ID converter & audio/mention segment support --- cmd/api-generator/main.go | 11 +++++- coolq/api.go | 28 ++++++------- coolq/api_v12.go | 9 +++++ coolq/converter.go | 10 +++++ coolq/cqcode.go | 7 +++- modules/api/api.go | 83 ++++++++++++++++++++++----------------- 6 files changed, 93 insertions(+), 55 deletions(-) diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index 64d8a0681..18f96dc13 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -65,6 +65,9 @@ func (g *generator) generate(routers []Router) { g.WriteString("\"github.com/Mrs4s/go-cqhttp/global\"\n") g.WriteString(")\n\n") g.WriteString(fmt.Sprintf(`func (c *Caller) call(action string, version uint16, p Getter) global.MSG { + var converter coolq.IDConverter = func(id any) any { + return coolq.ConvertIDWithVersion(id,version) + } if version == 12 { if action == "get_supported_actions" { return coolq.OK([]string{%v}) @@ -135,7 +138,7 @@ func (g *generator) router(router Router, pathVersion int) { } for i, p := range router.Params { - if p.Name == "version" { + if p.Name == "version" || p.Name == "converter" { continue } if p.Default == "" { @@ -157,6 +160,10 @@ func (g *generator) router(router Router, pathVersion int) { fmt.Fprintf(g.out, "version") continue } + if p.Name == "converter" { + fmt.Fprintf(g.out, "converter") + continue + } fmt.Fprintf(g.out, "p%d", i) } g.WriteString(")\n") @@ -166,7 +173,7 @@ func conv(v, t string) string { switch t { default: panic("unknown type: " + t) - case "gjson.Result": + case "gjson.Result", "IDConverter": return v case "int64": return v + ".Int()" diff --git a/coolq/api.go b/coolq/api.go index 039ed995c..4be82b842 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -328,13 +328,13 @@ func (bot *CQBot) CQGetTopicChannelFeeds(guildID, channelID uint64) global.MSG { // // https://git.io/Jtz1L // @route(get_friend_list) -func (bot *CQBot) CQGetFriendList() global.MSG { +func (bot *CQBot) CQGetFriendList(version uint16) global.MSG { fs := make([]global.MSG, 0, len(bot.Client.FriendList)) for _, f := range bot.Client.FriendList { fs = append(fs, global.MSG{ "nickname": f.Nickname, "remark": f.Remark, - "user_id": f.Uin, + "user_id": ConvertIDWithVersion(f.Uin, version), }) } return OK(fs) @@ -400,14 +400,14 @@ func (bot *CQBot) CQDeleteFriend(uin int64) global.MSG { // // https://git.io/Jtz1t // @route(get_group_list) -func (bot *CQBot) CQGetGroupList(noCache bool) global.MSG { +func (bot *CQBot) CQGetGroupList(noCache bool, converter IDConverter) global.MSG { gs := make([]global.MSG, 0, len(bot.Client.GroupList)) if noCache { _ = bot.Client.ReloadGroupList() } for _, g := range bot.Client.GroupList { gs = append(gs, global.MSG{ - "group_id": g.Code, + "group_id": converter(g.Code), "group_name": g.Name, "group_create_time": g.GroupCreateTime, "group_level": g.GroupLevel, @@ -422,7 +422,7 @@ func (bot *CQBot) CQGetGroupList(noCache bool) global.MSG { // // https://git.io/Jtz1O // @route(get_group_info) -func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool) global.MSG { +func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool, converter IDConverter) global.MSG { group := bot.Client.FindGroup(groupID) if group == nil || noCache { group, _ = bot.Client.GetGroupInfo(groupID) @@ -436,7 +436,7 @@ func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool) global.MSG { for _, g := range info { if g.Code == groupID { return OK(global.MSG{ - "group_id": g.Code, + "group_id": converter(g.Code), "group_name": g.Name, "group_memo": g.Memo, "group_create_time": 0, @@ -448,7 +448,7 @@ func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool) global.MSG { } } else { return OK(global.MSG{ - "group_id": group.Code, + "group_id": converter(group.Code), "group_name": group.Name, "group_create_time": group.GroupCreateTime, "group_level": group.GroupLevel, @@ -657,7 +657,7 @@ func (bot *CQBot) CQGroupFileDeleteFile(groupID int64, id string, busID int32) g // // https://docs.go-cqhttp.org/api/#%E8%8E%B7%E5%8F%96%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8D-%E9%9A%90%E8%97%8F-api // @route(.get_word_slices) -func (bot *CQBot) CQGetWordSlices(content string, version uint16) global.MSG { +func (bot *CQBot) CQGetWordSlices(content string) global.MSG { slices, err := bot.Client.GetWordSegmentation(content) if err != nil { return Failed(100, "WORD_SEGMENTATION_API_ERROR", err.Error()) @@ -670,7 +670,7 @@ func (bot *CQBot) CQGetWordSlices(content string, version uint16) global.MSG { // CQSendMessage 发送消息 // -// @route(send_msg) +// @route11(send_msg) // @rename(m->message) func (bot *CQBot) CQSendMessage(groupID, userID int64, m gjson.Result, messageType string, autoEscape bool) global.MSG { switch { @@ -688,7 +688,7 @@ func (bot *CQBot) CQSendMessage(groupID, userID int64, m gjson.Result, messageTy // CQSendForwardMessage 发送合并转发消息 // -// @route(send_forward_msg) +// @route11(send_forward_msg) // @rename(m->messages) func (bot *CQBot) CQSendForwardMessage(groupID, userID int64, m gjson.Result, messageType string) global.MSG { switch { @@ -707,7 +707,7 @@ func (bot *CQBot) CQSendForwardMessage(groupID, userID int64, m gjson.Result, me // CQSendGroupMessage 发送群消息 // // https://git.io/Jtz1c -// @route(send_group_msg) +// @route11(send_group_msg) // @rename(m->message) func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape bool) global.MSG { group := bot.Client.FindGroup(groupID) @@ -930,7 +930,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType // CQSendGroupForwardMessage 扩展API-发送合并转发(群) // // https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4 -// @route(send_group_forward_msg) +// @route11(send_group_forward_msg) // @rename(m->messages) func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) global.MSG { if m.Type != gjson.JSON { @@ -954,7 +954,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa // CQSendPrivateForwardMessage 扩展API-发送合并转发(好友) // // https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4 -// @route(send_private_forward_msg) +// @route11(send_private_forward_msg) // @rename(m->messages) func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) global.MSG { if m.Type != gjson.JSON { @@ -975,7 +975,7 @@ func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) glob // CQSendPrivateMessage 发送私聊消息 // // https://git.io/Jtz1l -// @route(send_private_msg) +// @route11(send_private_msg) // @rename(m->message) func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) global.MSG { var elem []message.IMessageElement diff --git a/coolq/api_v12.go b/coolq/api_v12.go index 5b4f0a60f..997606b3f 100644 --- a/coolq/api_v12.go +++ b/coolq/api_v12.go @@ -5,6 +5,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/tidwall/gjson" ) // CQGetVersion 获取版本信息 OneBotV12 @@ -21,3 +22,11 @@ func (bot *CQBot) CQGetVersion() global.MSG { "runtime_os": runtime.GOOS, }) } + +// CQSendMessageV12 发送消息 +// +// @route12(send_message) +// @rename(m->message) +func (bot *CQBot) CQSendMessageV12(groupID, userID, detailType string, m gjson.Result) global.MSG { + return OK(nil) +} diff --git a/coolq/converter.go b/coolq/converter.go index e1053572a..22d9e673e 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -1,6 +1,7 @@ package coolq import ( + "fmt" "strconv" "strings" @@ -13,6 +14,8 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) +type IDConverter func(id any) any + func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG { sex := "unknown" if m.Gender == 1 { // unknown = 0xff @@ -221,3 +224,10 @@ func toStringMessage(m []message.IMessageElement, source message.Source) string func fU64(v uint64) string { return strconv.FormatUint(v, 10) } + +func ConvertIDWithVersion(v any, version uint16) any { + if version == 12 { + return fmt.Sprint(v) + } + return v +} diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 18c70eece..df1dce009 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -832,7 +832,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return nil, err } return &message.VoiceElement{Data: base.ResampleSilk(data)}, nil - case "record": + case "record", "audio": f := d["file"] data, err := global.FindFile(f, d["cache"], global.VoicePath) if err != nil { @@ -858,7 +858,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return &message.AnimatedSticker{ID: int32(id)}, nil } return message.NewFace(int32(id)), nil - case "at": + case "mention_all": + d["qq"] = "all" + fallthrough + case "at", "mention": qq := d["qq"] if qq == "all" { return message.AtAll(), nil diff --git a/modules/api/api.go b/modules/api/api.go index 656d40b04..113708156 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -8,9 +8,12 @@ import ( ) func (c *Caller) call(action string, version uint16, p Getter) global.MSG { + var converter coolq.IDConverter = func(id any) any { + return coolq.ConvertIDWithVersion(id, version) + } if version == 12 { if action == "get_supported_actions" { - return coolq.OK([]string{".get_word_slices", ".handle_quick_operation", ".ocr_image", "ocr_image", "_get_group_notice", "_get_model_show", "_send_group_notice", "_set_model_show", "check_url_safely", "create_group_file_folder", "create_guild_role", "delete_essence_msg", "delete_friend", "delete_group_file", "delete_group_folder", "delete_guild_role", "delete_msg", "delete_unidirectional_friend", "download_file", "get_essence_msg_list", "get_forward_msg", "get_friend_list", "get_group_at_all_remain", "get_group_file_system_info", "get_group_file_url", "get_group_files_by_folder", "get_group_honor_info", "get_group_info", "get_group_list", "get_group_member_info", "get_group_member_list", "get_group_msg_history", "get_group_root_files", "get_group_system_msg", "get_guild_channel_list", "get_guild_list", "get_guild_member_list", "get_guild_member_profile", "get_guild_meta_by_guest", "get_guild_msg", "get_guild_roles", "get_guild_service_profile", "get_image", "get_self_info", "get_msg", "get_online_clients", "get_status", "get_user_info", "get_topic_channel_feeds", "get_unidirectional_friend_list", "get_version", "mark_msg_as_read", "qidian_get_account_info", "reload_event_filter", "send_forward_msg", "send_group_forward_msg", "send_group_msg", "send_group_sign", "send_guild_channel_msg", "send_msg", "send_private_forward_msg", "send_private_msg", "set_essence_msg", "set_friend_add_request", "set_group_add_request", "set_group_admin", "set_group_anonymous_ban", "set_group_ban", "set_group_card", "set_group_kick", "set_group_leave", "set_group_name", "set_group_portrait", "set_group_special_title", "set_group_whole_ban", "set_guild_member_role", "set_qq_profile", "update_guild_role", "upload_group_file"}) + return coolq.OK([]string{".get_word_slices", ".handle_quick_operation", ".ocr_image", "ocr_image", "_get_group_notice", "_get_model_show", "_send_group_notice", "_set_model_show", "check_url_safely", "create_group_file_folder", "create_guild_role", "delete_essence_msg", "delete_friend", "delete_group_file", "delete_group_folder", "delete_guild_role", "delete_msg", "delete_unidirectional_friend", "download_file", "get_essence_msg_list", "get_forward_msg", "get_friend_list", "get_group_at_all_remain", "get_group_file_system_info", "get_group_file_url", "get_group_files_by_folder", "get_group_honor_info", "get_group_info", "get_group_list", "get_group_member_info", "get_group_member_list", "get_group_msg_history", "get_group_root_files", "get_group_system_msg", "get_guild_channel_list", "get_guild_list", "get_guild_member_list", "get_guild_member_profile", "get_guild_meta_by_guest", "get_guild_msg", "get_guild_roles", "get_guild_service_profile", "get_image", "get_self_info", "get_msg", "get_online_clients", "get_status", "get_user_info", "get_topic_channel_feeds", "get_unidirectional_friend_list", "get_version", "mark_msg_as_read", "qidian_get_account_info", "reload_event_filter", "send_group_sign", "send_guild_channel_msg", "send_message", "set_essence_msg", "set_friend_add_request", "set_group_add_request", "set_group_admin", "set_group_anonymous_ban", "set_group_ban", "set_group_card", "set_group_kick", "set_group_leave", "set_group_name", "set_group_portrait", "set_group_special_title", "set_group_whole_ban", "set_guild_member_role", "set_qq_profile", "update_guild_role", "upload_group_file"}) } switch action { case "get_self_info": @@ -20,6 +23,12 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { return c.bot.CQGetStrangerInfo(p0) case "get_version": return c.bot.CQGetVersion() + case "send_message": + p0 := p.Get("group_id").String() + p1 := p.Get("user_id").String() + p2 := p.Get("detail_type").String() + p3 := p.Get("message") + return c.bot.CQSendMessageV12(p0, p1, p2, p3) } } if version == 11 { @@ -35,12 +44,44 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { return c.bot.CQGetStrangerInfo(p0) case "get_version_info": return c.bot.CQGetVersionInfo() + case "send_forward_msg": + p0 := p.Get("group_id").Int() + p1 := p.Get("user_id").Int() + p2 := p.Get("messages") + p3 := p.Get("message_type").String() + return c.bot.CQSendForwardMessage(p0, p1, p2, p3) + case "send_group_forward_msg": + p0 := p.Get("group_id").Int() + p1 := p.Get("messages") + return c.bot.CQSendGroupForwardMessage(p0, p1) + case "send_group_msg": + p0 := p.Get("group_id").Int() + p1 := p.Get("message") + p2 := p.Get("auto_escape").Bool() + return c.bot.CQSendGroupMessage(p0, p1, p2) + case "send_msg": + p0 := p.Get("group_id").Int() + p1 := p.Get("user_id").Int() + p2 := p.Get("message") + p3 := p.Get("message_type").String() + p4 := p.Get("auto_escape").Bool() + return c.bot.CQSendMessage(p0, p1, p2, p3, p4) + case "send_private_forward_msg": + p0 := p.Get("user_id").Int() + p1 := p.Get("messages") + return c.bot.CQSendPrivateForwardMessage(p0, p1) + case "send_private_msg": + p0 := p.Get("user_id").Int() + p1 := p.Get("group_id").Int() + p2 := p.Get("message") + p3 := p.Get("auto_escape").Bool() + return c.bot.CQSendPrivateMessage(p0, p1, p2, p3) } } switch action { case ".get_word_slices": p0 := p.Get("content").String() - return c.bot.CQGetWordSlices(p0, version) + return c.bot.CQGetWordSlices(p0) case ".handle_quick_operation": p0 := p.Get("context") p1 := p.Get("operation") @@ -115,7 +156,7 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { p0 := p.Get("[message_id,id].0").String() return c.bot.CQGetForwardMessage(p0) case "get_friend_list": - return c.bot.CQGetFriendList() + return c.bot.CQGetFriendList(version) case "get_group_at_all_remain": p0 := p.Get("group_id").Int() return c.bot.CQGetAtAllRemain(p0) @@ -138,10 +179,10 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { case "get_group_info": p0 := p.Get("group_id").Int() p1 := p.Get("no_cache").Bool() - return c.bot.CQGetGroupInfo(p0, p1) + return c.bot.CQGetGroupInfo(p0, p1, converter) case "get_group_list": p0 := p.Get("no_cache").Bool() - return c.bot.CQGetGroupList(p0) + return c.bot.CQGetGroupList(p0, converter) case "get_group_member_info": p0 := p.Get("group_id").Int() p1 := p.Get("user_id").Int() @@ -211,21 +252,6 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { case "reload_event_filter": p0 := p.Get("file").String() return c.bot.CQReloadEventFilter(p0) - case "send_forward_msg": - p0 := p.Get("group_id").Int() - p1 := p.Get("user_id").Int() - p2 := p.Get("messages") - p3 := p.Get("message_type").String() - return c.bot.CQSendForwardMessage(p0, p1, p2, p3) - case "send_group_forward_msg": - p0 := p.Get("group_id").Int() - p1 := p.Get("messages") - return c.bot.CQSendGroupForwardMessage(p0, p1) - case "send_group_msg": - p0 := p.Get("group_id").Int() - p1 := p.Get("message") - p2 := p.Get("auto_escape").Bool() - return c.bot.CQSendGroupMessage(p0, p1, p2) case "send_group_sign": p0 := p.Get("group_id").Int() return c.bot.CQSendGroupSign(p0) @@ -235,23 +261,6 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { p2 := p.Get("message") p3 := p.Get("auto_escape").Bool() return c.bot.CQSendGuildChannelMessage(p0, p1, p2, p3) - case "send_msg": - p0 := p.Get("group_id").Int() - p1 := p.Get("user_id").Int() - p2 := p.Get("message") - p3 := p.Get("message_type").String() - p4 := p.Get("auto_escape").Bool() - return c.bot.CQSendMessage(p0, p1, p2, p3, p4) - case "send_private_forward_msg": - p0 := p.Get("user_id").Int() - p1 := p.Get("messages") - return c.bot.CQSendPrivateForwardMessage(p0, p1) - case "send_private_msg": - p0 := p.Get("user_id").Int() - p1 := p.Get("group_id").Int() - p2 := p.Get("message") - p3 := p.Get("auto_escape").Bool() - return c.bot.CQSendPrivateMessage(p0, p1, p2, p3) case "set_essence_msg": p0 := int32(p.Get("message_id").Int()) return c.bot.CQSetEssenceMessage(p0) From 23d594be2917b3449dac20a23818e04ec8cb0e99 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 20 Jun 2022 16:11:31 +0800 Subject: [PATCH 395/612] coolq: support upload_private_file pc client can't receive file sent by this api. --- coolq/api.go | 24 ++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- modules/api/api.go | 5 +++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 1eeb6be18..9a6c15036 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -601,6 +601,30 @@ func (bot *CQBot) CQUploadGroupFile(groupID int64, file, name, folder string) gl return OK(nil) } +// CQUploadPrivateFile 扩展API-上传私聊文件 +// +// @route(upload_private_file) +func (bot *CQBot) CQUploadPrivateFile(userID int64, file, name string) global.MSG { + target := message.Source{ + SourceType: message.SourcePrivate, + PrimaryID: userID, + } + fileBody, err := os.Open(file) + if err != nil { + log.Warnf("上传私聊文件 %v 失败: %+v", file, err) + return Failed(100, "OPEN_FILE_ERROR", "打开文件失败") + } + localFile := &client.LocalFile{ + FileName: name, + Body: fileBody, + } + if err := bot.Client.UploadFile(target, localFile); err != nil { + log.Warnf("上传私聊 %v 文件 %v 失败: %+v", userID, file, err) + return Failed(100, "FILE_SYSTEM_UPLOAD_API_ERROR", err.Error()) + } + return OK(nil) +} + // CQGroupFileCreateFolder 拓展API-创建群文件文件夹 // // @route(create_group_file_folder) diff --git a/go.mod b/go.mod index bd31bcfac..cd0ec4267 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c + github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 diff --git a/go.sum b/go.sum index 755c91736..87b6e7abd 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c h1:LGJoAua1bYxx28255h02owv66v9eWkSwcZx/jEO79Z0= -github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= +github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb h1:UAfqKJ+xXbJObUQQAl1TYYwRcLBPYfy67D25lAD4Nl8= +github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= diff --git a/modules/api/api.go b/modules/api/api.go index ae4e92876..c57ce2619 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -337,5 +337,10 @@ func (c *Caller) call(action string, p Getter) global.MSG { p2 := p.Get("name").String() p3 := p.Get("folder").String() return c.bot.CQUploadGroupFile(p0, p1, p2, p3) + case "upload_private_file": + p0 := p.Get("user_id").Int() + p1 := p.Get("file").String() + p2 := p.Get("name").String() + return c.bot.CQUploadPrivateFile(p0, p1, p2) } } From 86f5b7f5f5aab6a09368d1417a25791223ec6b98 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 20 Jun 2022 16:11:31 +0800 Subject: [PATCH 396/612] coolq: support upload_private_file pc client can't receive file sent by this api. --- coolq/api.go | 24 ++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- modules/api/api.go | 5 +++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 4be82b842..4ea617d55 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -603,6 +603,30 @@ func (bot *CQBot) CQUploadGroupFile(groupID int64, file, name, folder string) gl return OK(nil) } +// CQUploadPrivateFile 扩展API-上传私聊文件 +// +// @route(upload_private_file) +func (bot *CQBot) CQUploadPrivateFile(userID int64, file, name string) global.MSG { + target := message.Source{ + SourceType: message.SourcePrivate, + PrimaryID: userID, + } + fileBody, err := os.Open(file) + if err != nil { + log.Warnf("上传私聊文件 %v 失败: %+v", file, err) + return Failed(100, "OPEN_FILE_ERROR", "打开文件失败") + } + localFile := &client.LocalFile{ + FileName: name, + Body: fileBody, + } + if err := bot.Client.UploadFile(target, localFile); err != nil { + log.Warnf("上传私聊 %v 文件 %v 失败: %+v", userID, file, err) + return Failed(100, "FILE_SYSTEM_UPLOAD_API_ERROR", err.Error()) + } + return OK(nil) +} + // CQGroupFileCreateFolder 拓展API-创建群文件文件夹 // // @route(create_group_file_folder) diff --git a/go.mod b/go.mod index bd31bcfac..cd0ec4267 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c + github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 diff --git a/go.sum b/go.sum index 755c91736..87b6e7abd 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c h1:LGJoAua1bYxx28255h02owv66v9eWkSwcZx/jEO79Z0= -github.com/Mrs4s/MiraiGo v0.0.0-20220614093722-87ff2f95910c/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= +github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb h1:UAfqKJ+xXbJObUQQAl1TYYwRcLBPYfy67D25lAD4Nl8= +github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= diff --git a/modules/api/api.go b/modules/api/api.go index 113708156..2f14cc608 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -362,6 +362,11 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { p2 := p.Get("name").String() p3 := p.Get("folder").String() return c.bot.CQUploadGroupFile(p0, p1, p2, p3) + case "upload_private_file": + p0 := p.Get("user_id").Int() + p1 := p.Get("file").String() + p2 := p.Get("name").String() + return c.bot.CQUploadPrivateFile(p0, p1, p2) } return coolq.Failed(404, "API_NOT_FOUND", "API不存在") } From ae7fefad137f2a309361e124fb9605657431a4b3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 21 Jun 2022 13:55:41 +0800 Subject: [PATCH 397/612] coolq: fix upload_private_file pc client now can receive file sent by this api. --- cmd/gocq/login.go | 3 +-- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index bc191461a..90be038b3 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -192,8 +192,7 @@ func loginResponseProcessor(res *client.LoginResponse) error { msg := res.ErrorMessage if strings.Contains(msg, "版本") { msg = "密码错误或账号被冻结" - } - if strings.Contains(msg, "冻结") { + } else if strings.Contains(msg, "冻结") { log.Fatalf("账号被冻结") } log.Warnf("登录失败: %v", msg) diff --git a/go.mod b/go.mod index cd0ec4267..cace35587 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb + github.com/Mrs4s/MiraiGo v0.0.0-20220621055318-d1a66c42b67e github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 diff --git a/go.sum b/go.sum index 87b6e7abd..365a2331e 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb h1:UAfqKJ+xXbJObUQQAl1TYYwRcLBPYfy67D25lAD4Nl8= -github.com/Mrs4s/MiraiGo v0.0.0-20220620032456-d09215e943bb/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= +github.com/Mrs4s/MiraiGo v0.0.0-20220621055318-d1a66c42b67e h1:xPHtwF8AYB2N055t1sNPC6+2FMy2dujV2Rp7Bf928w4= +github.com/Mrs4s/MiraiGo v0.0.0-20220621055318-d1a66c42b67e/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= From 2a0babad99a9f565c64ca8106391aebedd2b8cf6 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 21 Jun 2022 21:26:33 +0800 Subject: [PATCH 398/612] feat: slider captcha processor --- cmd/gocq/login.go | 37 ++++++++++++++++++++++++++++++++++++- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 90be038b3..8f72712fd 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -3,6 +3,7 @@ package gocq import ( "bufio" "bytes" + "fmt" "image" "image/png" "os" @@ -10,9 +11,11 @@ import ( "time" "github.com/Mrs4s/MiraiGo/client" + "github.com/Mrs4s/MiraiGo/utils" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" "github.com/Mrs4s/go-cqhttp/global" ) @@ -142,7 +145,19 @@ func loginResponseProcessor(res *client.LoginResponse) error { var text string switch res.Error { case client.SliderNeededError: - log.Warnf("登录需要滑条验证码, 请使用手机QQ扫描二维码以继续登录.") + log.Warnf("登录需要滑条验证码, 请选择验证方式: ") + log.Warnf("1. 使用浏览器抓取滑条并登录") + log.Warnf("2. 使用手机QQ扫码验证 (需要手Q和gocq在同一网络下).") + log.Warn("请输入(1 - 2) (将在10秒后自动选择1):") + text = readLineTimeout(time.Second*10, "1") + if strings.Contains(text, "1") { + ticket := sliderCaptchaProcessor(res.VerifyUrl) + if ticket == "" { + os.Exit(0) + } + res, err = cli.SubmitTicket(ticket) + continue + } cli.Disconnect() cli.Release() cli = client.NewClientEmpty() @@ -202,3 +217,23 @@ func loginResponseProcessor(res *client.LoginResponse) error { } } } + +func sliderCaptchaProcessor(u string) string { + id := utils.RandomString(8) + log.Warnf("请前往该地址验证 -> %v", strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id))) + start := time.Now() + for time.Now().Sub(start).Minutes() < 2 { + time.Sleep(time.Second) + data, err := global.GetBytes("https://captcha.go-cqhttp.org/captcha/ticket?id=" + id) + if err != nil { + log.Warnf("获取 Ticket 时出现错误: %v", err) + return "" + } + g := gjson.ParseBytes(data) + if g.Get("ticket").Exists() { + return g.Get("ticket").String() + } + } + log.Warnf("验证超时") + return "" +} diff --git a/go.mod b/go.mod index cace35587..db48f45ad 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220621055318-d1a66c42b67e + github.com/Mrs4s/MiraiGo v0.0.0-20220621083050-ae8c187aa59d github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 diff --git a/go.sum b/go.sum index 365a2331e..d70e91683 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220621055318-d1a66c42b67e h1:xPHtwF8AYB2N055t1sNPC6+2FMy2dujV2Rp7Bf928w4= -github.com/Mrs4s/MiraiGo v0.0.0-20220621055318-d1a66c42b67e/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= +github.com/Mrs4s/MiraiGo v0.0.0-20220621083050-ae8c187aa59d h1:Cq8HMtyL3PRpvOynuwi9WSdek2+5UTOd0zJ+JTq5hPM= +github.com/Mrs4s/MiraiGo v0.0.0-20220621083050-ae8c187aa59d/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= From 177ba9d8c2b1d86301f0bf3e0714710fe3c40af4 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 21 Jun 2022 21:29:41 +0800 Subject: [PATCH 399/612] make lint happy --- cmd/gocq/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 8f72712fd..20c10e140 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -222,7 +222,7 @@ func sliderCaptchaProcessor(u string) string { id := utils.RandomString(8) log.Warnf("请前往该地址验证 -> %v", strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id))) start := time.Now() - for time.Now().Sub(start).Minutes() < 2 { + for time.Since(start).Minutes() < 2 { time.Sleep(time.Second) data, err := global.GetBytes("https://captcha.go-cqhttp.org/captcha/ticket?id=" + id) if err != nil { From 7707e7854cc81fcdb31922e67c194f60a772cddb Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 22 Jun 2022 10:01:54 +0800 Subject: [PATCH 400/612] feat: add `vip_level` in get_stranger_info AntiVIP10Bot is ready. :innocent: --- coolq/api.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 9a6c15036..82a811859 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1388,6 +1388,7 @@ func (bot *CQBot) CQGetStrangerInfo(userID int64) global.MSG { "age": info.Age, "level": info.Level, "login_days": info.LoginDays, + "vip_level": info.VipLevel, }) } diff --git a/go.mod b/go.mod index db48f45ad..5b2085198 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220621083050-ae8c187aa59d + github.com/Mrs4s/MiraiGo v0.0.0-20220622015746-24ee0103e7de github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 diff --git a/go.sum b/go.sum index d70e91683..6bdf5100d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220621083050-ae8c187aa59d h1:Cq8HMtyL3PRpvOynuwi9WSdek2+5UTOd0zJ+JTq5hPM= -github.com/Mrs4s/MiraiGo v0.0.0-20220621083050-ae8c187aa59d/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= +github.com/Mrs4s/MiraiGo v0.0.0-20220622015746-24ee0103e7de h1:senbiV71dgsYt60BWLEHWwvKFCYUcIzGQ/fmXRaOeQ4= +github.com/Mrs4s/MiraiGo v0.0.0-20220622015746-24ee0103e7de/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= From 06b43dd666a4c68c7f6f76031e3de2639a4b0efd Mon Sep 17 00:00:00 2001 From: Akiba Date: Wed, 22 Jun 2022 22:51:17 +0800 Subject: [PATCH 401/612] fix: download block ua --- global/net.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global/net.go b/global/net.go index c72000a2b..601123229 100644 --- a/global/net.go +++ b/global/net.go @@ -184,7 +184,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int, req.Header.Set(k, v) } - if _, ok := headers["User-Agent"]; ok { + if _, ok := headers["User-Agent"]; !ok { req.Header["User-Agent"] = []string{UserAgent} } req.Header.Set("range", "bytes="+strconv.FormatInt(block.BeginOffset, 10)+"-"+strconv.FormatInt(block.EndOffset, 10)) From 1d859dc3730d5b9161dc4b586472d6338e22429b Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 24 Jun 2022 10:00:18 +0800 Subject: [PATCH 402/612] doc: document new http/ws host config --- docs/config.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/config.md b/docs/config.md index d352b61eb..38f128786 100644 --- a/docs/config.md +++ b/docs/config.md @@ -84,9 +84,8 @@ servers: # HTTP 通信设置 - http: # 服务端监听地址 - host: 127.0.0.1 - # 服务端监听端口 - port: 5700 + # 如需指定监听ipv4, 可使用 `address: tcp4://0.0.0.0:5700` (ipv6同理) + address: 0.0.0.0:5700 # 反向HTTP超时时间, 单位秒 # 最小值为5,小于5将会忽略本项设置 timeout: 5 @@ -102,9 +101,8 @@ servers: # 正向WS设置 - ws: # 正向WS服务器监听地址 - host: 127.0.0.1 - # 正向WS服务器监听端口 - port: 6700 + # 如需指定监听ipv4, 可使用 `address: tcp4://0.0.0.0:6700` (ipv6同理) + address: 0.0.0.0:6700 middlewares: <<: *default # 引用默认中间件 From 71ba266a8c7cfea0a81f90ffbca59cc9027f23d8 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Fri, 24 Jun 2022 10:15:29 +0800 Subject: [PATCH 403/612] doc: document some new API For #1009 --- docs/cqhttp.md | 53 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/docs/cqhttp.md b/docs/cqhttp.md index 71d7d201a..9d5f3fe19 100644 --- a/docs/cqhttp.md +++ b/docs/cqhttp.md @@ -491,6 +491,18 @@ Type: `tts` 示例: `[CQ:tts,text=这是一条测试消息]` +### 猜拳消息 + +Type: `rps` + +参数: + +| 参数名 | 类型 | 说明 | +|---------|-----|------------------| +| `value` | int | 0:石头, 1:剪刀, 2:布 | + +示例: `[CQ:rps,value=0]` + ## API ### 设置群名 @@ -613,15 +625,16 @@ Type: `tts` } ```` -### 发送合并转发(群) +### 发送合并转发(群/私聊) -终结点: `/send_group_forward_msg` +终结点: `/send_group_forward_msg`, `send_private_forward_msg`, `send_forward_msg` **参数** -| 字段 | 类型 | 说明 | -| ---------- | -------------- | ---------------------------- | -| `group_id` | int64 | 群号 | +| 字段 | 类型 | 说明 | +|------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `group_id` | int64 | 群号 | +| `user_id` | int64 | 私聊QQ号 | | `messages` | forward node[] | 自定义转发消息, 具体看 [CQCode](https://github.com/Mrs4s/go-cqhttp/blob/master/docs/cqhttp.md#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E6%B6%88%E6%81%AF%E8%8A%82%E7%82%B9) | 响应数据 @@ -883,6 +896,36 @@ Type: `tts` > 在不提供 `folder` 参数的情况下默认上传到根目录 > 只能上传本地文件, 需要上传 `http` 文件的话请先调用 `download_file` API下载 +### 上传私聊文件 + +终结点: `/upload_group_file` + +**参数** + +| 字段 | 类型 | 说明 | +|-----------|--------|--------| +| `user_id` | int64 | 接收者id | +| `file` | string | 本地文件路径 | +| `name` | string | 储存名称 | + +> 只能上传本地文件, 需要上传 `http` 文件的话请先调用 `download_file` API下载 + +### 设置 QQ 个人资料 + +终结点: `/set_qq_profile` + +**参数** + +| 字段 | 类型 | 说明 | +|-----------------|--------|------| +| `nickname` | int64 | 昵称 | +| `company` | string | 公司 | +| `email` | string | 邮箱 | +| `college` | string | 大学 | +| `personal_note` | string | 个人签名 | + +> 所有参数字段都为可选。 + ### 获取状态 终结点: `/get_status` From 2402bbedb091aa181b1f61669958fe02020e33d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E8=91=89=E6=9D=8F?= Date: Sat, 25 Jun 2022 10:04:29 +0800 Subject: [PATCH 404/612] doc: replace ob11 doc link (#1555) --- README.md | 108 ++++++++++++++++++++++---------------------- docs/EventFilter.md | 4 +- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 2cd173250..bf0fea1e2 100644 --- a/README.md +++ b/README.md @@ -86,18 +86,18 @@ go-cqhttp 兼容 [OneBot-v11](https://github.com/botuniverse/onebot-11) 绝大 | [CQ:xml] | [XML 消息] | | [CQ:json] | [JSON 消息] | -[qq 表情]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#qq-%E8%A1%A8%E6%83%85 -[语音]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E8%AF%AD%E9%9F%B3 -[短视频]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E7%9F%AD%E8%A7%86%E9%A2%91 -[@某人]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E6%9F%90%E4%BA%BA -[链接分享]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E9%93%BE%E6%8E%A5%E5%88%86%E4%BA%AB -[音乐分享]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E9%9F%B3%E4%B9%90%E5%88%86%E4%BA%AB- -[音乐自定义分享]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E9%9F%B3%E4%B9%90%E8%87%AA%E5%AE%9A%E4%B9%89%E5%88%86%E4%BA%AB- -[回复]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E5%9B%9E%E5%A4%8D -[合并转发]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91- -[合并转发节点]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E8%8A%82%E7%82%B9- -[xml 消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#xml-%E6%B6%88%E6%81%AF -[json 消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md#json-%E6%B6%88%E6%81%AF +[qq 表情]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#qq-%E8%A1%A8%E6%83%85 +[语音]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E8%AF%AD%E9%9F%B3 +[短视频]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E7%9F%AD%E8%A7%86%E9%A2%91 +[@某人]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E6%9F%90%E4%BA%BA +[链接分享]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E9%93%BE%E6%8E%A5%E5%88%86%E4%BA%AB +[音乐分享]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E9%9F%B3%E4%B9%90%E5%88%86%E4%BA%AB- +[音乐自定义分享]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E9%9F%B3%E4%B9%90%E8%87%AA%E5%AE%9A%E4%B9%89%E5%88%86%E4%BA%AB- +[回复]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E5%9B%9E%E5%A4%8D +[合并转发]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91- +[合并转发节点]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91%E8%8A%82%E7%82%B9- +[xml 消息]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#xml-%E6%B6%88%E6%81%AF +[json 消息]: https://github.com/botuniverse/onebot-11/blob/master/message/segment.md#json-%E6%B6%88%E6%81%AF #### 拓展 CQ 码及与 OneBot 标准有略微差异的 CQ 码 @@ -154,33 +154,33 @@ go-cqhttp 兼容 [OneBot-v11](https://github.com/botuniverse/onebot-11) 绝大 | /set_restart | [重启 go-cqhttp] | | /.handle_quick_operation | [对事件执行快速操作] | -[发送私聊消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_private_msg-%E5%8F%91%E9%80%81%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF -[发送群消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_group_msg-%E5%8F%91%E9%80%81%E7%BE%A4%E6%B6%88%E6%81%AF -[发送消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#send_msg-%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF -[撤回信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#delete_msg-%E6%92%A4%E5%9B%9E%E6%B6%88%E6%81%AF -[群组踢人]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_kick-%E7%BE%A4%E7%BB%84%E8%B8%A2%E4%BA%BA -[群组单人禁言]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_ban-%E7%BE%A4%E7%BB%84%E5%8D%95%E4%BA%BA%E7%A6%81%E8%A8%80 -[群组全员禁言]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_whole_ban-%E7%BE%A4%E7%BB%84%E5%85%A8%E5%91%98%E7%A6%81%E8%A8%80 -[群组设置管理员]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_admin-%E7%BE%A4%E7%BB%84%E8%AE%BE%E7%BD%AE%E7%AE%A1%E7%90%86%E5%91%98 -[设置群名片(群备注)]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_card-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D%E7%89%87%E7%BE%A4%E5%A4%87%E6%B3%A8 -[设置群名]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_name-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D -[退出群组]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_leave-%E9%80%80%E5%87%BA%E7%BE%A4%E7%BB%84 -[设置群组专属头衔]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_special_title-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E7%BB%84%E4%B8%93%E5%B1%9E%E5%A4%B4%E8%A1%94 -[处理加好友请求]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_friend_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82 -[处理加群请求/邀请]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_group_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7 -[获取登录号信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_login_info-%E8%8E%B7%E5%8F%96%E7%99%BB%E5%BD%95%E5%8F%B7%E4%BF%A1%E6%81%AF -[获取陌生人信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_stranger_info-%E8%8E%B7%E5%8F%96%E9%99%8C%E7%94%9F%E4%BA%BA%E4%BF%A1%E6%81%AF -[获取好友列表]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_friend_list-%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B%E5%88%97%E8%A1%A8 -[获取群信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E4%BF%A1%E6%81%AF -[获取群列表]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%88%97%E8%A1%A8 -[获取群成员信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF -[获取群成员列表]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_member_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8 -[获取群荣誉信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_group_honor_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E8%8D%A3%E8%AA%89%E4%BF%A1%E6%81%AF -[检查是否可以发送图片]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_image-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E5%9B%BE%E7%89%87 -[检查是否可以发送语音]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#can_send_record-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E8%AF%AD%E9%9F%B3 -[获取版本信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#get_version_info-%E8%8E%B7%E5%8F%96%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF -[重启 go-cqhttp]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/public.md#set_restart-%E9%87%8D%E5%90%AF-onebot-%E5%AE%9E%E7%8E%B0 -[对事件执行快速操作]: https://github.com/howmanybots/onebot/blob/master/v11/specs/api/hidden.md#handle_quick_operation-%E5%AF%B9%E4%BA%8B%E4%BB%B6%E6%89%A7%E8%A1%8C%E5%BF%AB%E9%80%9F%E6%93%8D%E4%BD%9C +[发送私聊消息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#send_private_msg-%E5%8F%91%E9%80%81%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF +[发送群消息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#send_group_msg-%E5%8F%91%E9%80%81%E7%BE%A4%E6%B6%88%E6%81%AF +[发送消息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#send_msg-%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF +[撤回信息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#delete_msg-%E6%92%A4%E5%9B%9E%E6%B6%88%E6%81%AF +[群组踢人]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_kick-%E7%BE%A4%E7%BB%84%E8%B8%A2%E4%BA%BA +[群组单人禁言]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_ban-%E7%BE%A4%E7%BB%84%E5%8D%95%E4%BA%BA%E7%A6%81%E8%A8%80 +[群组全员禁言]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_whole_ban-%E7%BE%A4%E7%BB%84%E5%85%A8%E5%91%98%E7%A6%81%E8%A8%80 +[群组设置管理员]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_admin-%E7%BE%A4%E7%BB%84%E8%AE%BE%E7%BD%AE%E7%AE%A1%E7%90%86%E5%91%98 +[设置群名片(群备注)]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_card-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D%E7%89%87%E7%BE%A4%E5%A4%87%E6%B3%A8 +[设置群名]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_name-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E5%90%8D +[退出群组]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_leave-%E9%80%80%E5%87%BA%E7%BE%A4%E7%BB%84 +[设置群组专属头衔]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_special_title-%E8%AE%BE%E7%BD%AE%E7%BE%A4%E7%BB%84%E4%B8%93%E5%B1%9E%E5%A4%B4%E8%A1%94 +[处理加好友请求]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_friend_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82 +[处理加群请求/邀请]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_group_add_request-%E5%A4%84%E7%90%86%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7 +[获取登录号信息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_login_info-%E8%8E%B7%E5%8F%96%E7%99%BB%E5%BD%95%E5%8F%B7%E4%BF%A1%E6%81%AF +[获取陌生人信息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_stranger_info-%E8%8E%B7%E5%8F%96%E9%99%8C%E7%94%9F%E4%BA%BA%E4%BF%A1%E6%81%AF +[获取好友列表]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_friend_list-%E8%8E%B7%E5%8F%96%E5%A5%BD%E5%8F%8B%E5%88%97%E8%A1%A8 +[获取群信息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E4%BF%A1%E6%81%AF +[获取群列表]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E5%88%97%E8%A1%A8 +[获取群成员信息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_member_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E4%BF%A1%E6%81%AF +[获取群成员列表]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_member_list-%E8%8E%B7%E5%8F%96%E7%BE%A4%E6%88%90%E5%91%98%E5%88%97%E8%A1%A8 +[获取群荣誉信息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_group_honor_info-%E8%8E%B7%E5%8F%96%E7%BE%A4%E8%8D%A3%E8%AA%89%E4%BF%A1%E6%81%AF +[检查是否可以发送图片]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#can_send_image-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E5%9B%BE%E7%89%87 +[检查是否可以发送语音]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#can_send_record-%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%8F%AF%E4%BB%A5%E5%8F%91%E9%80%81%E8%AF%AD%E9%9F%B3 +[获取版本信息]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#get_version_info-%E8%8E%B7%E5%8F%96%E7%89%88%E6%9C%AC%E4%BF%A1%E6%81%AF +[重启 go-cqhttp]: https://github.com/botuniverse/onebot-11/blob/master/api/public.md#set_restart-%E9%87%8D%E5%90%AF-onebot-%E5%AE%9E%E7%8E%B0 +[对事件执行快速操作]: https://github.com/botuniverse/onebot-11/blob/master/api/hidden.md#handle_quick_operation-%E5%AF%B9%E4%BA%8B%E4%BB%B6%E6%89%A7%E8%A1%8C%E5%BF%AB%E9%80%9F%E6%93%8D%E4%BD%9C #### 拓展 API 及与 OneBot 标准有略微差异的 API @@ -239,21 +239,21 @@ go-cqhttp 兼容 [OneBot-v11](https://github.com/botuniverse/onebot-11) 绝大 | 请求事件 | [加好友请求] | | 请求事件 | [加群请求/邀请] | -[私聊信息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF -[群消息]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#%E7%BE%A4%E6%B6%88%E6%81%AF -[群文件上传]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0 -[群管理员变动]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E7%AE%A1%E7%90%86%E5%91%98%E5%8F%98%E5%8A%A8 -[群成员减少]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E5%87%8F%E5%B0%91 -[群成员增加]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E5%A2%9E%E5%8A%A0 -[群禁言]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E7%A6%81%E8%A8%80 -[好友添加]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E5%A5%BD%E5%8F%8B%E6%B7%BB%E5%8A%A0 -[群消息撤回]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E -[好友消息撤回]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E5%A5%BD%E5%8F%8B%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E -[群内戳一戳]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E5%86%85%E6%88%B3%E4%B8%80%E6%88%B3 -[群红包运气王]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E7%BA%A2%E5%8C%85%E8%BF%90%E6%B0%94%E7%8E%8B -[群成员荣誉变更]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E8%8D%A3%E8%AA%89%E5%8F%98%E6%9B%B4 -[加好友请求]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/request.md#%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82 -[加群请求/邀请]: https://github.com/howmanybots/onebot/blob/master/v11/specs/event/request.md#%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7 +[私聊信息]: https://github.com/botuniverse/onebot-11/blob/master/event/message.md#%E7%A7%81%E8%81%8A%E6%B6%88%E6%81%AF +[群消息]: https://github.com/botuniverse/onebot-11/blob/master/event/message.md#%E7%BE%A4%E6%B6%88%E6%81%AF +[群文件上传]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0 +[群管理员变动]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E7%AE%A1%E7%90%86%E5%91%98%E5%8F%98%E5%8A%A8 +[群成员减少]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E5%87%8F%E5%B0%91 +[群成员增加]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E5%A2%9E%E5%8A%A0 +[群禁言]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E7%A6%81%E8%A8%80 +[好友添加]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E5%A5%BD%E5%8F%8B%E6%B7%BB%E5%8A%A0 +[群消息撤回]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E +[好友消息撤回]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E5%A5%BD%E5%8F%8B%E6%B6%88%E6%81%AF%E6%92%A4%E5%9B%9E +[群内戳一戳]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E5%86%85%E6%88%B3%E4%B8%80%E6%88%B3 +[群红包运气王]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E7%BA%A2%E5%8C%85%E8%BF%90%E6%B0%94%E7%8E%8B +[群成员荣誉变更]: https://github.com/botuniverse/onebot-11/blob/master/event/notice.md#%E7%BE%A4%E6%88%90%E5%91%98%E8%8D%A3%E8%AA%89%E5%8F%98%E6%9B%B4 +[加好友请求]: https://github.com/botuniverse/onebot-11/blob/master/event/request.md#%E5%8A%A0%E5%A5%BD%E5%8F%8B%E8%AF%B7%E6%B1%82 +[加群请求/邀请]: https://github.com/botuniverse/onebot-11/blob/master/event/request.md#%E5%8A%A0%E7%BE%A4%E8%AF%B7%E6%B1%82%E9%82%80%E8%AF%B7 #### 拓展 Event diff --git a/docs/EventFilter.md b/docs/EventFilter.md index 9226b7abe..6911f59e7 100644 --- a/docs/EventFilter.md +++ b/docs/EventFilter.md @@ -163,9 +163,9 @@ ## 过滤时的事件数据对象 -过滤器在go-cqhttp构建好事件数据后运行,各事件的数据字段见[OneBot标准]( https://github.com/howmanybots/onebot/blob/master/v11/specs/event/README.md )。 +过滤器在go-cqhttp构建好事件数据后运行,各事件的数据字段见[OneBot标准]( https://github.com/botuniverse/onebot-11/blob/master/event/README.md )。 这里有几点需要注意: -- `message` 字段在运行过滤器时和上报信息类型相同(见 [消息格式]( https://github.com/howmanybots/onebot/blob/master/v11/specs/message/array.md )) +- `message` 字段在运行过滤器时和上报信息类型相同(见 [消息格式]( https://github.com/botuniverse/onebot-11/blob/master/message/array.md )) - `raw_message` 字段为未经**CQ码**处理的原始消息字符串,这意味着其中可能会出现形如 `[CQ:face,id=123]` 的 CQ 码 From f772996418d6e2b14c510fb879eecc8350996d2e Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 25 Jun 2022 17:40:15 +0800 Subject: [PATCH 405/612] fix #1556 --- coolq/api.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 82a811859..a16636e86 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -873,17 +873,17 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType } if e.Get("data.id").Exists() { i := e.Get("data.id").Int() - m, _ := db.GetGroupMessageByGlobalID(int32(i)) + m, _ := db.GetMessageByGlobalID(int32(i)) if m != nil { - msgTime := m.Attribute.Timestamp + msgTime := m.GetAttribute().Timestamp if msgTime == 0 { msgTime = ts.Unix() } return &message.ForwardNode{ - SenderId: m.Attribute.SenderUin, - SenderName: m.Attribute.SenderName, + SenderId: m.GetAttribute().SenderUin, + SenderName: m.GetAttribute().SenderName, Time: int32(msgTime), - Message: resolveElement(bot.ConvertContentMessage(m.Content, message.SourceGroup)), + Message: resolveElement(bot.ConvertContentMessage(m.GetContent(), message.SourceGroup)), } } log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str) From 7e750352c4e16339393e4ea531e0f46dfee3d957 Mon Sep 17 00:00:00 2001 From: Akiba Date: Sat, 25 Jun 2022 21:06:37 +0800 Subject: [PATCH 406/612] fix: #1558 --- coolq/api.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index a16636e86..7b5b95faf 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -875,6 +875,12 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType i := e.Get("data.id").Int() m, _ := db.GetMessageByGlobalID(int32(i)) if m != nil { + mSource := func() message.SourceType { + if m.GetType() == "group" { + return message.SourceGroup + } + return message.SourcePrivate + }() msgTime := m.GetAttribute().Timestamp if msgTime == 0 { msgTime = ts.Unix() @@ -883,7 +889,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType SenderId: m.GetAttribute().SenderUin, SenderName: m.GetAttribute().SenderName, Time: int32(msgTime), - Message: resolveElement(bot.ConvertContentMessage(m.GetContent(), message.SourceGroup)), + Message: resolveElement(bot.ConvertContentMessage(m.GetContent(), mSource)), } } log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str) @@ -916,7 +922,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType } } } - content := bot.ConvertObjectMessage(c, message.SourceGroup) + content := bot.ConvertObjectMessage(c, sourceType) if uin != 0 && name != "" && len(content) > 0 { return &message.ForwardNode{ SenderId: uin, From a0fba6ad540e5cfdd9e06c8b90fb81260faa0ded Mon Sep 17 00:00:00 2001 From: Akiba Date: Sat, 25 Jun 2022 21:21:16 +0800 Subject: [PATCH 407/612] fix: #1557 --- coolq/api.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 7b5b95faf..e1186df15 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -832,7 +832,12 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType groupID := target source := message.Source{SourceType: sourceType, PrimaryID: target} if sourceType == message.SourcePrivate { - groupID = 0 + // ios 设备的合并转发来源群号不能为 0 + if len(bot.Client.GroupList) == 0 { + groupID = 1 + } else { + groupID = bot.Client.GroupList[1].Uin + } } builder := bot.Client.NewForwardMessageBuilder(groupID) @@ -875,12 +880,10 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType i := e.Get("data.id").Int() m, _ := db.GetMessageByGlobalID(int32(i)) if m != nil { - mSource := func() message.SourceType { - if m.GetType() == "group" { - return message.SourceGroup - } - return message.SourcePrivate - }() + mSource := message.SourcePrivate + if m.GetType() == "group" { + mSource = message.SourceGroup + } msgTime := m.GetAttribute().Timestamp if msgTime == 0 { msgTime = ts.Unix() From 551a475c1c7236510e11f0caca53b4fcf2d59d28 Mon Sep 17 00:00:00 2001 From: xiangxiangxiong9 <1296976552@qq.com> Date: Sun, 26 Jun 2022 14:50:11 +0800 Subject: [PATCH 408/612] =?UTF-8?q?=E7=94=A8upload=5Fgroup=5Ffile=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E7=A7=81=E8=81=8A=E6=96=87=E4=BB=B6=3F=E4=B8=8D?= =?UTF-8?q?=E5=BA=94=E8=AF=A5=E6=98=AFupload=5Fprivate=5Ffile=E5=90=97qwq?= =?UTF-8?q?=20(#1560)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/cqhttp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cqhttp.md b/docs/cqhttp.md index 9d5f3fe19..3b30884fe 100644 --- a/docs/cqhttp.md +++ b/docs/cqhttp.md @@ -898,7 +898,7 @@ Type: `rps` ### 上传私聊文件 -终结点: `/upload_group_file` +终结点: `/upload_private_file` **参数** From 36bf579e0f42f6363dfa9d2894d1e623baa5452a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E8=91=89=E6=9D=8F?= Date: Sat, 9 Jul 2022 12:57:53 +0800 Subject: [PATCH 409/612] feat: del group notice (#1567) * update dep * feat: _del_group_notice * doc: get_group_notice && del_group_notice --- coolq/api.go | 17 +++++++++++++ docs/cqhttp.md | 63 +++++++++++++++++++++++++++++++++++++++++++--- go.mod | 4 +-- go.sum | 8 +++--- modules/api/api.go | 4 +++ 5 files changed, 87 insertions(+), 9 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index e1186df15..63307f84c 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1114,6 +1114,23 @@ func (bot *CQBot) CQSetGroupMemo(groupID int64, msg, img string) global.MSG { return Failed(100, "GROUP_NOT_FOUND", "群聊不存在") } +// CQDelGroupMemo 扩展API-删除群公告 +// @route(_del_group_notice) +// @rename(fid->notice_id) +func (bot *CQBot) CQDelGroupMemo(groupID int64, fid string) global.MSG { + if g := bot.Client.FindGroup(groupID); g != nil { + if g.SelfPermission() == client.Member { + return Failed(100, "PERMISSION_DENIED", "权限不足") + } + err := bot.Client.DelGroupNotice(groupID, fid) + if err != nil { + return Failed(100, "DELETE_NOTICE_ERROR", err.Error()) + } + return OK(nil) + } + return Failed(100, "GROUP_NOT_FOUND", "群聊不存在") +} + // CQSetGroupKick 群组踢人 // // https://git.io/Jtz1V diff --git a/docs/cqhttp.md b/docs/cqhttp.md index 3b30884fe..2f6dbb345 100644 --- a/docs/cqhttp.md +++ b/docs/cqhttp.md @@ -42,6 +42,8 @@ - [设置群名](#设置群名) - [获取用户VIP信息](#获取用户vip信息) - [发送群公告](#发送群公告) +- [获取群公告](#获取群公告) +- [删除群公告](#删除群公告) - [设置精华消息](#设置精华消息) - [移出精华消息](#移出精华消息) - [获取精华消息列表](#获取精华消息列表) @@ -244,7 +246,8 @@ Type: `node` | `seq` | message | 具体消息 | 用于自定义消息 | 特殊说明: **需要使用单独的API `/send_group_forward_msg` 发送,并且由于消息段较为复杂,仅支持Array形式入参。 如果引用消息和自定义消息同时出现,实际查看顺序将取消息段顺序. -另外按 [Onebot v11](https://github.com/botuniverse/onebot-11/blob/master/message/array.md) 文档说明, `data` 应全为字符串, 但由于需要接收`message` 类型的消息, 所以 *仅限此Type的content字段* 支持Array套娃** +另外按 [Onebot v11](https://github.com/botuniverse/onebot-11/blob/master/message/array.md) 文档说明, `data` 应全为字符串, +但由于需要接收`message` 类型的消息, 所以 *仅限此Type的content字段* 支持Array套娃** 示例: @@ -627,7 +630,7 @@ Type: `rps` ### 发送合并转发(群/私聊) -终结点: `/send_group_forward_msg`, `send_private_forward_msg`, `send_forward_msg` +终结点: `/send_group_forward_msg`, `send_private_forward_msg`, `send_forward_msg` **参数** @@ -1108,13 +1111,67 @@ JSON数组: `该 API 没有响应数据` +### 获取群公告 + +终结点: `/_get_group_notice` + +**参数** + +| 字段名 | 数据类型 | 默认值 | 说明 | +| ---------- | -------- | ------ | -------- | +| `group_id` | int64 | | 群号 | + +**响应数据** + +数组信息: + +| 字段名 | 数据类型 | 默认值 | 说明 | +|----------------|--------| ------ |-------| +| `notice_id` | string | | 公告id | +| `sender_id` | string | | 发布者id | +| `publish_time` | string | | 发布时间 | +| `message` | GroupNoticeMessage | | 公告id | + +响应示例 + +```json +{ + "data": [ + { + "notice_id": "8850de2e00000000cc6bbd628a150c00", + "sender_id": 1111111, + "publish_time": 1656581068, + "message": { + "text": "这是一条公告", + "images": [] + } + } + ], + "retcode": 0, + "status": "ok" +} +``` + +### 删除群公告 + +终结点: `/_del_group_notice` + +**参数** + +| 字段名 | 数据类型 | 默认值 | 说明 | +|-------------| -------- | ------ |------| +| `group_id` | int64 | | 群号 | +| `notice_id` | string | | 公告id | + +`该 API 没有响应数据` + ### 获取单向好友列表 终结点: `/get_unidirectional_friend_list` **响应数据** -数组信息: +数组信息: | 字段 | 类型 | 说明 | | ------------- | ------ | -------- | diff --git a/go.mod b/go.mod index 5b2085198..e9908ea3a 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220622015746-24ee0103e7de + github.com/Mrs4s/MiraiGo v0.0.0-20220630160133-a39b3fdd962f github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 @@ -26,7 +26,7 @@ require ( ) require ( - github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a // indirect + github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/go-stack/stack v1.8.0 // indirect diff --git a/go.sum b/go.sum index 6bdf5100d..62f01ce80 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220622015746-24ee0103e7de h1:senbiV71dgsYt60BWLEHWwvKFCYUcIzGQ/fmXRaOeQ4= -github.com/Mrs4s/MiraiGo v0.0.0-20220622015746-24ee0103e7de/go.mod h1:mZp8Lt7uqLCUwSLouB2yuiP467Cwl4mnG9IMAaXUKA0= -github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a h1:WIfEWYj82oEuPtm5pqlyQmCJCoiw00C6ugZFqHA0cC8= -github.com/RomiChan/protobuf v0.1.1-0.20220602121309-9e3b8cbefd7a/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= +github.com/Mrs4s/MiraiGo v0.0.0-20220630160133-a39b3fdd962f h1:PHOwN3/cEL/zoBpcJJXwx1mJ1NL901zrt2mvlQUO5BA= +github.com/Mrs4s/MiraiGo v0.0.0-20220630160133-a39b3fdd962f/go.mod h1:Ow7nlaVS5FztyjrTlTRSG7HLpTNmgINMluHRCgKunDI= +github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0 h1:+UGPBYVjssFsdahLJIiNPwpmmwgl/OaVdv1oc5NonC0= +github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= diff --git a/modules/api/api.go b/modules/api/api.go index c57ce2619..e13f8c80e 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -21,6 +21,10 @@ func (c *Caller) call(action string, p Getter) global.MSG { case ".ocr_image", "ocr_image": p0 := p.Get("image").String() return c.bot.CQOcrImage(p0) + case "_del_group_notice": + p0 := p.Get("group_id").Int() + p1 := p.Get("notice_id").String() + return c.bot.CQDelGroupMemo(p0, p1) case "_get_group_notice": p0 := p.Get("group_id").Int() return c.bot.CQGetGroupMemo(p0) From 7a979c862f72e2e166b5d75c6319602f59568326 Mon Sep 17 00:00:00 2001 From: yuanyan3060 <1846865993@qq.com> Date: Wed, 13 Jul 2022 02:16:25 +0800 Subject: [PATCH 410/612] =?UTF-8?q?fix=20a=20bug=20=E4=BD=BF=E7=94=A8mongo?= =?UTF-8?q?db=E6=97=B6=20image=E7=9A=84=20subtype=E4=BC=9A=E8=A2=AB?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E4=B8=BANumberLong=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/cqcode.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 18c70eece..750557686 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -733,7 +733,12 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message case *message.GroupImageElement: img.Flash = flash img.EffectID = id - img.ImageBizType = message.ImageBizType(data["subType"].(uint32)) + switch data["subType"].(type) { + case int64: + img.ImageBizType = message.ImageBizType(data["subType"].(int64)) + default: + img.ImageBizType = message.ImageBizType(data["subType"].(uint32)) + } case *message.FriendImageElement: img.Flash = flash } From 59a7790130a5f242c033eb1566bed801647765fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 19 Jul 2022 20:23:48 +0800 Subject: [PATCH 411/612] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=94=B9=E4=B8=BA=E8=A7=A3=E6=9E=90raw=E6=96=87?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E5=8F=AF=E4=BB=A5=E4=BD=BF=E7=94=A8=E6=9B=B4?= =?UTF-8?q?=E5=A4=9A=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 88 ++++++++++++++++++++++++++++------------ qqrobot/config.go | 18 ++++---- qqrobot/qq_robot.go | 4 +- qqrobot/qq_robot_test.go | 2 +- 4 files changed, 74 insertions(+), 38 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 3efff39f6..dd6bafb2c 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -3,6 +3,7 @@ package qqrobot import ( "fmt" "io/ioutil" + "math/rand" "regexp" "sort" "strconv" @@ -19,7 +20,7 @@ import ( func (r *QQRobot) checkUpdates() { for _, rule := range r.Config.NotifyUpdate.Rules { lastVersion := r.CheckUpdateVersionMap[rule.Name] - latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogPage) + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawUrl) if versionLess(lastVersion, latestVersion) { // 版本有更新 r.CheckUpdateVersionMap[rule.Name] = latestVersion @@ -51,7 +52,7 @@ func (r *QQRobot) manualTriggerUpdateNotify(triggerRule *Rule) (replies *message continue } - latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogPage) + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawUrl) if latestVersion == VersionNone { break } @@ -107,32 +108,20 @@ func (r *QQRobot) initCheckUpdateVersionMap() { } logger.Infof(bold(color.Yellow).Render(fmt.Sprintf("将以%v的间隔定期检查配置的项目的版本更新情况", time.Second*time.Duration(r.Config.NotifyUpdate.CheckInterval)))) for _, rule := range r.Config.NotifyUpdate.Rules { - latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogPage) + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawUrl) r.CheckUpdateVersionMap[rule.Name] = latestVersion logger.Infof(bold(color.Yellow).Render(fmt.Sprintf("项目[%v]当前的最新版本为%v, 更新信息如下:\n%v", rule.Name, latestVersion, updateMessage))) } } var regGitVersion = regexp.MustCompile(`([vV][0-9.]+)(\s+\d+\.\d+\.\d+)`) -var regUpdateInfo = regexp.MustCompile(`(更新公告)\s*((\s|\S)+?)`) -var regUpdateMessages = regexp.MustCompile("
  • (.+?)
  • ") +var regUpdateInfo = regexp.MustCompile(`更新公告\s*(?P(\s|\S)+?)\n\n`) // VersionNone 默认版本号 var VersionNone = "v0.0.0" -// GithubMirrorSites github的镜像站 -var GithubMirrorSites = []string{ - "hub.fastgit.org", - "github.com.cnpmjs.org", -} - -func (r *QQRobot) getLatestGitVersion(gitChangelogPage string) (latestVersion string, updateMessage string) { - urls := make([]string, 0, len(GithubMirrorSites)+1) - // 先尝试国内镜像,最后尝试直接访问 - for _, mirrorSite := range GithubMirrorSites { - urls = append(urls, strings.ReplaceAll(gitChangelogPage, "github.com", mirrorSite)) - } - urls = append(urls, gitChangelogPage) +func (r *QQRobot) getLatestGitVersion(gitChangelogRawUrl string) (latestVersion string, updateMessage string) { + urls := generateMirrorGithubRawUrls(gitChangelogRawUrl) for _, url := range urls { latestVersion, updateMessage = r._getLatestGitVersion(url) @@ -144,6 +133,60 @@ func (r *QQRobot) getLatestGitVersion(gitChangelogPage string) (latestVersion st return } +// 形如 https://github.com/fzls/djc_helper/raw/master/CHANGELOG.MD +var regRawUrl = regexp.MustCompile(`https://github.com/(?P\w+)/(?P\w+)/raw/(?P\w+)/(?P[\w\W]+)`) + +func generateMirrorGithubRawUrls(gitChangelogRawUrl string) []string { + match := regRawUrl.FindStringSubmatch(gitChangelogRawUrl) + if match == nil { + return []string{gitChangelogRawUrl} + } + owner := match[regRawUrl.SubexpIndex("owner")] + repoName := match[regRawUrl.SubexpIndex("repo_name")] + branchName := match[regRawUrl.SubexpIndex("branch_name")] + filepathInRepo := match[regRawUrl.SubexpIndex("filepath_in_repo")] + + var urls []string + + // 先加入比较快的几个镜像 + urls = append(urls, "https://raw.iqiq.io/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://raw.連接.台灣/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://raw-gh.gcdn.mirr.one/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + + // 随机乱序,确保均匀分布请求 + rand.Shuffle(len(urls), func(i, j int) { + urls[i], urls[j] = urls[j], urls[i] + }) + + // 然后加入几个慢的镜像和源站 + urls = append(urls, "https://cdn.staticaly.com/gh/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://gcore.jsdelivr.net/gh/{owner}/{repo_name}@{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://fastly.jsdelivr.net/gh/{owner}/{repo_name}@{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://raw.fastgit.org/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://ghproxy.com/https://raw.githubusercontent.com/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://ghproxy.futils.com/https://github.com/{owner}/{repo_name}/blob/{branch_name}/{filepath_in_repo}") + + // 最后加入原始地址和一些不可达的 + urls = append(urls, "https://raw.githubusercontents.com/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://github.com/{owner}/{repo_name}/raw/{branch_name}/{filepath_in_repo}") + + // 替换占位符为实际值 + placeholderToValue := map[string]string{ + "{owner}": owner, + "{repo_name}": repoName, + "{branch_name}": branchName, + "{filepath_in_repo}": filepathInRepo, + } + for idx := 0; idx < len(urls); idx++ { + for placeholder, value := range placeholderToValue { + urls[idx] = strings.ReplaceAll(urls[idx], placeholder, value) + } + } + + // 返回最终结果 + return urls +} + func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) { resp, err := r.httpClient.Get(gitChangelogPage) if err != nil { @@ -180,14 +223,7 @@ func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) // 解析更新信息 var updateMessage string if match := regUpdateInfo.FindStringSubmatch(htmlText); match != nil { - messagesText := match[2] - if matches := regUpdateMessages.FindAllStringSubmatch(messagesText, -1); matches != nil { - var messages []string - for idx, match := range matches { - messages = append(messages, fmt.Sprintf("%v. %v", idx+1, match[1])) - } - updateMessage = strings.Join(messages, "\n") - } + updateMessage = match[regUpdateInfo.SubexpIndex("update_message")] } return versions[0], updateMessage diff --git a/qqrobot/config.go b/qqrobot/config.go index 68717d784..d000658b4 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -138,7 +138,7 @@ type RuleConfig struct { TimePeriods []TimePeriod `toml:"time_periods"` // 适用该规则的时间段(前者包含,后者不包含) TriggerRuleCount int64 `toml:"trigger_rule_count"` // TriggerRuleDuration内触发的规则数目是否超过该数目 TriggerRuleDuration int64 `toml:"trigger_rule_duration"` // 判定恶意触发机器人规则的时间周期(秒) - GitChangelogPage string `toml:"git_changelog_page"` // 某git仓库的changelog的url,若设定,则将请求这个网页,从中解析出最新的版本号和更新信息,并替换到GuideContent中的$git_version$和$update_message$ + GitChangelogRawUrl string `toml:"git_changelog_raw_url"` // 某git仓库的changelog的raw url,若设定,则将请求这个网页,从中解析出最新的版本号和更新信息,并替换到GuideContent中的$git_version$和$update_message$ GuideContentHasPermission string `toml:"guide_content_has_permission"` // 当有权限触发该指令时的回复 GuideContentHasNoPermission string `toml:"guide_content_has_no_permission"` // 当无权限触发该指令时的回复 TargetUpdateRuleName string `toml:"target_update_rule_name"` // 目标更新规则的名称 @@ -184,14 +184,14 @@ type NotifyUpdateConfig struct { // NotifyUpdateRule 通知更新规则 type NotifyUpdateRule struct { - Name string `toml:"name"` // 名称 - NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 - NotifyGroupTypes []string `toml:"notify_group_types"` // 通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 - Message string `toml:"message"` // 通知的消息,参数:$git_version$=最新版本, $update_message$=更新信息 - ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 - GitChangelogPage string `toml:"git_changelog_page"` // git仓库的changelog的url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ - AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 需要at的qq列表 - AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 是否需要@全体成员 + Name string `toml:"name"` // 名称 + NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 + NotifyGroupTypes []string `toml:"notify_group_types"` // 通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + Message string `toml:"message"` // 通知的消息,参数:$git_version$=最新版本, $update_message$=更新信息 + ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 + GitChangelogRawUrl string `toml:"git_changelog_raw_url"` // git仓库的changelog的raw url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ + AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 需要at的qq列表 + AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 是否需要@全体成员 } // NotifySettleConfig 通知结算配置 diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 9a2630221..dc8cb22a3 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -488,8 +488,8 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { switch config.Action { case actionTypeGuide: guideContent = strings.ReplaceAll(guideContent, templateargsMutetime, strconv.FormatInt(muteTime, 10)) - if config.GitChangelogPage != "" { - latestVersion, updateMessage := r.getLatestGitVersion(config.GitChangelogPage) + if config.GitChangelogRawUrl != "" { + latestVersion, updateMessage := r.getLatestGitVersion(config.GitChangelogRawUrl) guideContent = strings.ReplaceAll(guideContent, templateargsGitversion, latestVersion) guideContent = strings.ReplaceAll(guideContent, templateargsUpdatemessage, updateMessage) } diff --git a/qqrobot/qq_robot_test.go b/qqrobot/qq_robot_test.go index 3db7f8857..f60c9bf19 100644 --- a/qqrobot/qq_robot_test.go +++ b/qqrobot/qq_robot_test.go @@ -40,7 +40,7 @@ func Test_convertChineseNumber(t *testing.T) { func Test_getLatestGitVersion(t *testing.T) { qqRobot := NewQQRobot(nil, "") - version, updateMessage := qqRobot.getLatestGitVersion("https://github.com/fzls/djc_helper/blob/master/CHANGELOG.MD") + version, updateMessage := qqRobot.getLatestGitVersion("https://github.com/fzls/djc_helper/raw/master/CHANGELOG.MD") t.Logf("version=%v, updateMessage如下:\n%v", version, updateMessage) } From 3a727042fbd3ba46d30f7504b53180dd0ae512f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 19 Jul 2022 20:58:44 +0800 Subject: [PATCH 412/612] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E6=9B=B4=E6=96=B0=E7=BE=A4=E6=96=87=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=96=B9=E4=BE=BF=E6=9C=89=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=97=B6=E8=87=AA=E5=8A=A8=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E5=88=B0=E7=BE=A4=E9=87=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index dd6bafb2c..8e7144046 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -46,6 +46,36 @@ func (r *QQRobot) checkUpdates() { } } +func (r *QQRobot) updateFileInGroup(groupID int64, localFilePath string, uploadFileName string, oldVersionKeyWords string) { + logger.Infof("开始更新 群 %v 的 %v 文件,旧版本关键词为 %v,新版本路径为 %v", groupID, uploadFileName, oldVersionKeyWords, localFilePath) + + // 获取群文件信息 + fs, err := r.cqBot.Client.GetGroupFileSystem(groupID) + if err != nil { + logger.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) + return + } + files, _, err := fs.Root() + if err != nil { + logger.Warnf("获取群 %v 根目录文件失败: %v", groupID, err) + return + } + + // 移除之前版本 + for _, file := range files { + if strings.Contains(file.FileName, oldVersionKeyWords) { + logger.Infof("找到了目标文件=%v", file) + + res := fs.DeleteFile("", file.FileId, file.BusId) + logger.Infof("删除群 %v 文件 %v(%v) 结果为 %v", groupID, file.FileName, file.FileId, res) + } + } + + // 上传新版本 + err = fs.UploadFile(localFilePath, uploadFileName, "/") + logger.Warnf("上传群 %v 文件 %v 结果为 %v", groupID, uploadFileName, err) +} + func (r *QQRobot) manualTriggerUpdateNotify(triggerRule *Rule) (replies *message.SendingMessage) { for _, rule := range r.Config.NotifyUpdate.Rules { if rule.Name != triggerRule.Config.TargetUpdateRuleName { From 7e7d467d13015b415296e04ca3c1e02d7ac66177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 19 Jul 2022 22:07:56 +0800 Subject: [PATCH 413/612] =?UTF-8?q?feat:=20=E6=A3=80=E6=9F=A5=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=8F=AF=E9=85=8D=E7=BD=AE=E4=B8=8B=E8=BD=BD=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E7=9A=84python=E8=84=9A=E6=9C=AC=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=EF=BC=8C=E4=BB=8E=E8=80=8C=E5=9C=A8=E6=9C=89=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=97=B6=E8=87=AA=E5=8A=A8=E4=BB=8E=E7=BD=91?= =?UTF-8?q?=E7=9B=98=E4=B8=8B=E8=BD=BD=E5=88=B0=E6=9C=AC=E5=9C=B0=EF=BC=8C?= =?UTF-8?q?=E7=84=B6=E5=90=8E=E4=B8=8A=E4=BC=A0=E5=88=B0=E5=90=84=E4=B8=AA?= =?UTF-8?q?=E7=BE=A4=E7=9A=84=E7=BE=A4=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 47 ++++++++++++++++++++++++++++++++++++ qqrobot/check_update_test.go | 26 ++++++++++++++++++++ qqrobot/config.go | 18 ++++++++------ 3 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 qqrobot/check_update_test.go diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 8e7144046..9ac8c1ebe 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -1,9 +1,12 @@ package qqrobot import ( + "encoding/json" "fmt" "io/ioutil" "math/rand" + "os/exec" + "path/filepath" "regexp" "sort" "strconv" @@ -11,12 +14,17 @@ import ( "time" "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/go-cqhttp/global" "github.com/gookit/color" logger "github.com/sirupsen/logrus" ) // 2021/10/02 5:21 by fzls +type PythonDownloadNewVersionResult struct { + Filepath string `json:"downloaded_path"` +} + func (r *QQRobot) checkUpdates() { for _, rule := range r.Config.NotifyUpdate.Rules { lastVersion := r.CheckUpdateVersionMap[rule.Name] @@ -42,10 +50,49 @@ func (r *QQRobot) checkUpdates() { logger.Infof("【%v】 %v groupID=%v replies=%v", rule.Name, nowStr, groupID, replies) } logger.Infof("check update %v, from %v to %v", rule.Name, lastVersion, latestVersion) + + if interpreter, script := rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath; interpreter != "" && script != "" && global.PathExists(interpreter) && global.PathExists(script) { + + logger.Infof("通知完毕,开始更新新版本到各个群中") + oldVersionKeywords := "DNF蚊子腿小助手_v" + + logger.Infof("开始调用配置的更新命令来获取新版本: %v %v", interpreter, script) + newVersionFilePath, err := downloadNewVersionUsingPythonScript(interpreter, script) + if err != nil { + logger.Warnf("下载新版本失败, err=%v", err) + continue + } + + uploadFileName := filepath.Base(newVersionFilePath) + + for _, groupID := range rule.NotifyGroups { + logger.Infof("开始上传 %v 到 群 %v", uploadFileName, groupID) + r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords) + } + } else { + logger.Infof("更新规则 %v 未配置更新python脚本,或者对应脚本不存在,将不会尝试下载并上传新版本到群文件", rule.Name) + } } } } +func downloadNewVersionUsingPythonScript(pythonInterpreterPath string, pythonScriptPath string) (string, error) { + cmd := exec.Command(pythonInterpreterPath, pythonScriptPath) + cmd.Dir = filepath.Dir(pythonScriptPath) + out, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("调用python脚本 %v 下载新版本失败,err=%v", pythonScriptPath, err) + } + + var result PythonDownloadNewVersionResult + err = json.Unmarshal(out, &result) + if err != nil { + return "", fmt.Errorf("解析python返回的结果失败, out=%v, err=%v", string(out), err) + } + + return result.Filepath, nil +} + func (r *QQRobot) updateFileInGroup(groupID int64, localFilePath string, uploadFileName string, oldVersionKeyWords string) { logger.Infof("开始更新 群 %v 的 %v 文件,旧版本关键词为 %v,新版本路径为 %v", groupID, uploadFileName, oldVersionKeyWords, localFilePath) diff --git a/qqrobot/check_update_test.go b/qqrobot/check_update_test.go new file mode 100644 index 000000000..cf391e3ec --- /dev/null +++ b/qqrobot/check_update_test.go @@ -0,0 +1,26 @@ +package qqrobot + +import ( + "testing" +) + +// 2022/07/19 20:11 by fzls + +func Test_generateMirrorGithubRawUrls(t *testing.T) { + got := generateMirrorGithubRawUrls("https://github.com/fzls/djc_helper/raw/master/CHANGELOG.MD") + if len(got) <= 1 { + t.Errorf("generateMirrorGithubRawUrls failed") + } +} + +func Test_downloadNewVersionUsingPythonScript(t *testing.T) { + pythonInterpreter := "D:\\_coding\\python\\djc_helper\\.venv_dev\\Scripts\\python.exe" + pythonScript := "D:/_coding/python/djc_helper/download_latest_version.py" + got, err := downloadNewVersionUsingPythonScript(pythonInterpreter, pythonScript) + if err != nil { + t.Errorf("downloadNewVersionUsingPythonScript err=%v", err) + } + if got == "" { + t.Errorf("downloadNewVersionUsingPythonScript got = empty") + } +} diff --git a/qqrobot/config.go b/qqrobot/config.go index d000658b4..188ce302c 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -184,14 +184,16 @@ type NotifyUpdateConfig struct { // NotifyUpdateRule 通知更新规则 type NotifyUpdateRule struct { - Name string `toml:"name"` // 名称 - NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 - NotifyGroupTypes []string `toml:"notify_group_types"` // 通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 - Message string `toml:"message"` // 通知的消息,参数:$git_version$=最新版本, $update_message$=更新信息 - ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 - GitChangelogRawUrl string `toml:"git_changelog_raw_url"` // git仓库的changelog的raw url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ - AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 需要at的qq列表 - AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 是否需要@全体成员 + Name string `toml:"name"` // 名称 + NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 + NotifyGroupTypes []string `toml:"notify_group_types"` // 通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + Message string `toml:"message"` // 通知的消息,参数:$git_version$=最新版本, $update_message$=更新信息 + ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 + GitChangelogRawUrl string `toml:"git_changelog_raw_url"` // git仓库的changelog的raw url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ + AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 需要at的qq列表 + AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 是否需要@全体成员 + DownloadNewVersionPythonInterpreterPath string `toml:"download_new_version_python_interpreter_path"` // 下载新版本的所需的python解释器路径 + DownloadNewVersionPythonScriptPath string `toml:"download_new_version_python_script_path"` // 下载新版本的python脚本路径。将会在stdout中输出json回复 {"downloaded_path": "xxx"}。如果未配置,则不会尝试下载新版本并上传到群文件 } // NotifySettleConfig 通知结算配置 From 33555638f0348de42e0d40fbc4513268457ff810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 19 Jul 2022 23:15:27 +0800 Subject: [PATCH 414/612] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E8=A7=A6=E5=8F=91=E4=B8=8A=E4=BC=A0=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=9A=84=E6=8C=87=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 39 ++++++++++--------- qqrobot/config.go | 84 ++++++++++++++++++++++------------------- qqrobot/qq_robot.go | 6 +++ 3 files changed, 73 insertions(+), 56 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 9ac8c1ebe..02368fbd6 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -51,28 +51,33 @@ func (r *QQRobot) checkUpdates() { } logger.Infof("check update %v, from %v to %v", rule.Name, lastVersion, latestVersion) - if interpreter, script := rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath; interpreter != "" && script != "" && global.PathExists(interpreter) && global.PathExists(script) { + r.updateNewVersionInGroup(rule.Name, rule.NotifyGroups, rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath) + } + } +} - logger.Infof("通知完毕,开始更新新版本到各个群中") - oldVersionKeywords := "DNF蚊子腿小助手_v" +func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interpreter string, script string) { + if interpreter != "" && script != "" && global.PathExists(interpreter) && global.PathExists(script) { + logger.Infof("开始更新新版本到各个群中: %v", groups) + oldVersionKeywords := "DNF蚊子腿小助手_v" - logger.Infof("开始调用配置的更新命令来获取新版本: %v %v", interpreter, script) - newVersionFilePath, err := downloadNewVersionUsingPythonScript(interpreter, script) - if err != nil { - logger.Warnf("下载新版本失败, err=%v", err) - continue - } + logger.Infof("开始调用配置的更新命令来获取新版本: %v %v", interpreter, script) + newVersionFilePath, err := downloadNewVersionUsingPythonScript(interpreter, script) + if err != nil { + logger.Warnf("下载新版本失败, err=%v", err) + return + } - uploadFileName := filepath.Base(newVersionFilePath) + uploadFileName := filepath.Base(newVersionFilePath) - for _, groupID := range rule.NotifyGroups { - logger.Infof("开始上传 %v 到 群 %v", uploadFileName, groupID) - r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords) - } - } else { - logger.Infof("更新规则 %v 未配置更新python脚本,或者对应脚本不存在,将不会尝试下载并上传新版本到群文件", rule.Name) - } + for _, groupID := range groups { + logger.Infof("开始上传 %v 到 群 %v", uploadFileName, groupID) + r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords) + // 广播消息间强行间隔一秒 + time.Sleep(time.Second) } + } else { + logger.Infof("%v: 未配置更新python脚本,或者对应脚本不存在,将不会尝试下载并上传新版本到群文件", ctx) } } diff --git a/qqrobot/config.go b/qqrobot/config.go index 188ce302c..8d9309225 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -88,6 +88,7 @@ const ( actiontypeAichat ActionType = "ai_chat" actiontypeManaualTriggerUpdateNotify ActionType = "manaual_trigger_update_notify" actiontypeRepeater ActionType = "repeater" + actiontypeUpdateNewVersion ActionType = "update_new_version" ) var ( @@ -103,45 +104,49 @@ var ( // RuleConfig 规则配置 type RuleConfig struct { - Name string `toml:"name"` // 规则名称 - Type RuleType `toml:"type"` // 规则类别 - RawGroupIds []int64 `toml:"group_ids"` // 适用的QQ群ID列表 - GroupTypes []string `toml:"group_types"` // 适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 - GroupIds map[int64]struct{} `toml:"-"` // - RawKeywords []string `toml:"keywords"` // 适用的关键词列表 - KeywordRegexes []*regexp.Regexp `toml:"-"` // 适用的关键词的正则表达式列表 - RawExcludeKeywords []string `toml:"exclude_keywords"` // 需要过滤的关键词列表 - ExcludeKeywordRegexes []*regexp.Regexp `toml:"-"` // 需要过滤的关键词的正则表达式列表 - AtQQs []int64 `toml:"at_qqs"` // 需要判定at的qq列表 - ExcludeQQs []int64 `toml:"exclude_qqs"` // 排除的QQ列表 - ExcludeAdmin bool `toml:"exclude_admin"` // 是否排除管理员 - Action ActionType `toml:"action"` // 动作 - SendOnJoin bool `toml:"send_on_join"` // 是否在入群时发送 - AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 当触发该规则时,需要at的qq列表 - AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 当触发该规则时,是否需要@全体成员 - GuideContent string `toml:"guide_content"` // 内容 - ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 - ImageURLList []string `toml:"image_url_list"` // 图片URL列表,若有,则会额外附加图片 - RandomImageUrls []string `toml:"random_image_urls"` // 若配置,则从中随机一个作为图片发送,同时ImageUrl配置会被覆盖 - CD int64 `toml:"cd"` // cd时长(秒),0表示不设定,若设定,在cd内触发规则时,若设置了cd内回复内容,则回复该内容,否则视为未触发 - GuideContentInCD string `toml:"guide_content_in_cd"` // cd内触发规则时的回复内容 - ForwardToQQs []int64 `toml:"forward_to_qqs"` // 将消息转发到该QQ列表 - ForwardToGroups []int64 `toml:"forward_to_groups"` // 将消息转发到该QQ群列表 - RepeatToGroups []int64 `toml:"repeat_to_groups"` // 将消息复读到该QQ群列表 - RepeatToGroupTypes []string `toml:"repeat_to_group_types"` // 复读适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 - RepeatAtAll bool `toml:"repeat_at_all"` // 复读时,在开头加上@全体 - FoodSiteURLList []string `toml:"food_site_url_list"` // 美食图片来源网站列表 - FoodDescription string `toml:"food_description"` // 美食描述,参数:$food_name$=食物名字 - RevokeMessage bool `toml:"revoke_message"` // 是否撤回该条消息 - MuteTime int64 `toml:"mute_time"` // 禁言时间,为0则表示不禁言(单位为秒) - ParseMuteTime bool `toml:"parse_mute_time"` // 是否从消息从解析想要被禁言的时间 - TimePeriods []TimePeriod `toml:"time_periods"` // 适用该规则的时间段(前者包含,后者不包含) - TriggerRuleCount int64 `toml:"trigger_rule_count"` // TriggerRuleDuration内触发的规则数目是否超过该数目 - TriggerRuleDuration int64 `toml:"trigger_rule_duration"` // 判定恶意触发机器人规则的时间周期(秒) - GitChangelogRawUrl string `toml:"git_changelog_raw_url"` // 某git仓库的changelog的raw url,若设定,则将请求这个网页,从中解析出最新的版本号和更新信息,并替换到GuideContent中的$git_version$和$update_message$ - GuideContentHasPermission string `toml:"guide_content_has_permission"` // 当有权限触发该指令时的回复 - GuideContentHasNoPermission string `toml:"guide_content_has_no_permission"` // 当无权限触发该指令时的回复 - TargetUpdateRuleName string `toml:"target_update_rule_name"` // 目标更新规则的名称 + Name string `toml:"name"` // 规则名称 + Type RuleType `toml:"type"` // 规则类别 + RawGroupIds []int64 `toml:"group_ids"` // 适用的QQ群ID列表 + GroupTypes []string `toml:"group_types"` // 适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + GroupIds map[int64]struct{} `toml:"-"` // + RawKeywords []string `toml:"keywords"` // 适用的关键词列表 + KeywordRegexes []*regexp.Regexp `toml:"-"` // 适用的关键词的正则表达式列表 + RawExcludeKeywords []string `toml:"exclude_keywords"` // 需要过滤的关键词列表 + ExcludeKeywordRegexes []*regexp.Regexp `toml:"-"` // 需要过滤的关键词的正则表达式列表 + AtQQs []int64 `toml:"at_qqs"` // 需要判定at的qq列表 + ExcludeQQs []int64 `toml:"exclude_qqs"` // 排除的QQ列表 + ExcludeAdmin bool `toml:"exclude_admin"` // 是否排除管理员 + Action ActionType `toml:"action"` // 动作 + SendOnJoin bool `toml:"send_on_join"` // 是否在入群时发送 + AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 当触发该规则时,需要at的qq列表 + AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 当触发该规则时,是否需要@全体成员 + GuideContent string `toml:"guide_content"` // 内容 + ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 + ImageURLList []string `toml:"image_url_list"` // 图片URL列表,若有,则会额外附加图片 + RandomImageUrls []string `toml:"random_image_urls"` // 若配置,则从中随机一个作为图片发送,同时ImageUrl配置会被覆盖 + CD int64 `toml:"cd"` // cd时长(秒),0表示不设定,若设定,在cd内触发规则时,若设置了cd内回复内容,则回复该内容,否则视为未触发 + GuideContentInCD string `toml:"guide_content_in_cd"` // cd内触发规则时的回复内容 + ForwardToQQs []int64 `toml:"forward_to_qqs"` // 将消息转发到该QQ列表 + ForwardToGroups []int64 `toml:"forward_to_groups"` // 将消息转发到该QQ群列表 + RepeatToGroups []int64 `toml:"repeat_to_groups"` // 将消息复读到该QQ群列表 + RepeatToGroupTypes []string `toml:"repeat_to_group_types"` // 复读适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + RepeatAtAll bool `toml:"repeat_at_all"` // 复读时,在开头加上@全体 + UpdateNewVersionToGroups []int64 `toml:"update_new_version_to_groups"` // 更新新版本到该QQ群列表 + UpdateNewVersionToGroupTypes []string `toml:"update_new_version_to_group_types"` // 更新新版本适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + DownloadNewVersionPythonInterpreterPath string `toml:"download_new_version_python_interpreter_path"` // 下载新版本的所需的python解释器路径 + DownloadNewVersionPythonScriptPath string `toml:"download_new_version_python_script_path"` // 下载新版本的python脚本路径。将会在stdout中输出json回复 {"downloaded_path": "xxx"}。如果未配置,则不会尝试下载新版本并上传到群文件 + FoodSiteURLList []string `toml:"food_site_url_list"` // 美食图片来源网站列表 + FoodDescription string `toml:"food_description"` // 美食描述,参数:$food_name$=食物名字 + RevokeMessage bool `toml:"revoke_message"` // 是否撤回该条消息 + MuteTime int64 `toml:"mute_time"` // 禁言时间,为0则表示不禁言(单位为秒) + ParseMuteTime bool `toml:"parse_mute_time"` // 是否从消息从解析想要被禁言的时间 + TimePeriods []TimePeriod `toml:"time_periods"` // 适用该规则的时间段(前者包含,后者不包含) + TriggerRuleCount int64 `toml:"trigger_rule_count"` // TriggerRuleDuration内触发的规则数目是否超过该数目 + TriggerRuleDuration int64 `toml:"trigger_rule_duration"` // 判定恶意触发机器人规则的时间周期(秒) + GitChangelogRawUrl string `toml:"git_changelog_raw_url"` // 某git仓库的changelog的raw url,若设定,则将请求这个网页,从中解析出最新的版本号和更新信息,并替换到GuideContent中的$git_version$和$update_message$ + GuideContentHasPermission string `toml:"guide_content_has_permission"` // 当有权限触发该指令时的回复 + GuideContentHasNoPermission string `toml:"guide_content_has_no_permission"` // 当无权限触发该指令时的回复 + TargetUpdateRuleName string `toml:"target_update_rule_name"` // 目标更新规则的名称 } // TimePeriod 时间规则 @@ -259,6 +264,7 @@ func (c *Config) Init() { } rule.RepeatToGroups = c.mergeGroupTypesIntoGroups(rule.RepeatToGroups, rule.RepeatToGroupTypes) + rule.UpdateNewVersionToGroups = c.mergeGroupTypesIntoGroups(rule.UpdateNewVersionToGroups, rule.UpdateNewVersionToGroupTypes) } for idx := range c.NotifyUpdate.Rules { diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index dc8cb22a3..554430ffb 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -595,6 +595,12 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } else { replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) } + case actiontypeUpdateNewVersion: + if isAdmin { + r.updateNewVersionInGroup("手动触发更新新版本", config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath) + } else { + replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) + } default: if hitKeyWords { replies.Append(message.NewText(fmt.Sprintf( From 305184e4c45b7a537dffb2a8278a3c6cc3d8be03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 22 Jul 2022 14:41:40 +0800 Subject: [PATCH 415/612] =?UTF-8?q?feat:=20QQ=E9=9F=B3=E4=B9=90=E7=9A=84?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=8C=82=E4=BA=86=EF=BC=8C=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E7=BD=91=E6=98=93=E4=BA=91=E9=9F=B3=E4=B9=90=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/command.go b/qqrobot/command.go index 84d2a006e..33e85e2fd 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -102,7 +102,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms // full_match|听歌关键词|musicName musicName := match[2] - musicElem, err := r.makeMusicShareElement(musicName, message.QQMusic) + musicElem, err := r.makeMusicShareElement(musicName, message.CloudMusic) if err != nil { return fmt.Sprintf("没有找到歌曲:%v", musicName), nil, nil } From 42fa896c71c4e2bbce1d48dc93ccb6c2034a9f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 24 Jul 2022 17:42:42 +0800 Subject: [PATCH 416/612] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E6=97=B6?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA=E6=97=A5=E5=BF=97=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E5=8A=A0=E7=9B=B4=E8=A7=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 02368fbd6..7c155a3d3 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -37,6 +37,9 @@ func (r *QQRobot) checkUpdates() { if lastVersion == VersionNone { continue } + + logger.Infof("%v 版本有更新 %v => %v, 开始通知各个群以及上传群文件", rule.Name, lastVersion, latestVersion) + replies := r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) nowStr := r.currentTime() for _, groupID := range rule.NotifyGroups { From 4a40847490a38540b482061a58cb0e9019c8825b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 24 Jul 2022 17:51:45 +0800 Subject: [PATCH 417/612] =?UTF-8?q?feat:=20=E4=B8=8A=E4=BC=A0=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E6=94=B9=E4=B8=BA=E5=9C=A8=E4=BB=A3=E7=A0=81=E9=87=8C?= =?UTF-8?q?=E5=A4=84=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 554430ffb..029c75bdf 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -597,7 +597,9 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } case actiontypeUpdateNewVersion: if isAdmin { + r.cqBot.SendGroupMessage(groupID, message.NewSendingMessage().Append(message.NewText("开始执行上传指令,可能需要一点时间~"))) r.updateNewVersionInGroup("手动触发更新新版本", config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath) + replies.Append(message.NewText("指令执行成功,可查看各个群看看是否上传成功。如果失败,可查看机器人的服务器日志排查")) } else { replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) } From 203cc8723564674051dc4a5620ac2667d3d3be49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 11 Aug 2022 16:30:50 +0800 Subject: [PATCH 418/612] =?UTF-8?q?feat:=20=E8=A7=A3=E6=9E=90=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E8=84=9A=E6=9C=AC=E7=9A=84=E6=B5=81=E7=A8=8B=E9=80=82?= =?UTF-8?q?=E9=85=8D=E6=96=B0=E7=9A=84=E8=BF=94=E5=9B=9E=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 10 ++++++++-- qqrobot/check_update_test.go | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 7c155a3d3..39a1f17ca 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -92,10 +92,16 @@ func downloadNewVersionUsingPythonScript(pythonInterpreterPath string, pythonScr return "", fmt.Errorf("调用python脚本 %v 下载新版本失败,err=%v", pythonScriptPath, err) } + // 现在脚本基于github来下载,中间需要二次压缩,导致会出现一些多余的字符串,因此这里需要将结果部分提取出来 + output := string(out) + boundaryMark := "$$boundary$$" + jsonResult := strings.Split(output, boundaryMark)[1] + jsonResult = strings.TrimSpace(jsonResult) + var result PythonDownloadNewVersionResult - err = json.Unmarshal(out, &result) + err = json.Unmarshal([]byte(jsonResult), &result) if err != nil { - return "", fmt.Errorf("解析python返回的结果失败, out=%v, err=%v", string(out), err) + return "", fmt.Errorf("解析python返回的结果失败, jsonResult=%v, err=%v", jsonResult, err) } return result.Filepath, nil diff --git a/qqrobot/check_update_test.go b/qqrobot/check_update_test.go index cf391e3ec..5cad57f7e 100644 --- a/qqrobot/check_update_test.go +++ b/qqrobot/check_update_test.go @@ -14,8 +14,8 @@ func Test_generateMirrorGithubRawUrls(t *testing.T) { } func Test_downloadNewVersionUsingPythonScript(t *testing.T) { - pythonInterpreter := "D:\\_coding\\python\\djc_helper\\.venv_dev\\Scripts\\python.exe" - pythonScript := "D:/_coding/python/djc_helper/download_latest_version.py" + pythonInterpreter := "D:\\_codes\\Python\\djc_helper_public\\.venv_dev\\Scripts\\python.exe" + pythonScript := "D:\\_codes\\Python\\djc_helper_public\\download_latest_version.py" got, err := downloadNewVersionUsingPythonScript(pythonInterpreter, pythonScript) if err != nil { t.Errorf("downloadNewVersionUsingPythonScript err=%v", err) From eeae0162bb1f8f8b0d92d90db626c3e1b72dd702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 11 Aug 2022 16:47:21 +0800 Subject: [PATCH 419/612] fix: lint --- qqrobot/check_update.go | 32 ++++++------- qqrobot/config.go | 4 +- qqrobot/qinglong/query_test.go | 82 +++++++++++++++++----------------- qqrobot/qq_robot.go | 4 +- 4 files changed, 62 insertions(+), 60 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 39a1f17ca..7adc0db8c 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -16,11 +16,13 @@ import ( "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/go-cqhttp/global" "github.com/gookit/color" + "github.com/pkg/errors" logger "github.com/sirupsen/logrus" ) // 2021/10/02 5:21 by fzls +// PythonDownloadNewVersionResult 使用python脚本下载最新版本的结果 type PythonDownloadNewVersionResult struct { Filepath string `json:"downloaded_path"` } @@ -28,7 +30,7 @@ type PythonDownloadNewVersionResult struct { func (r *QQRobot) checkUpdates() { for _, rule := range r.Config.NotifyUpdate.Rules { lastVersion := r.CheckUpdateVersionMap[rule.Name] - latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawUrl) + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawURL) if versionLess(lastVersion, latestVersion) { // 版本有更新 r.CheckUpdateVersionMap[rule.Name] = latestVersion @@ -89,7 +91,7 @@ func downloadNewVersionUsingPythonScript(pythonInterpreterPath string, pythonScr cmd.Dir = filepath.Dir(pythonScriptPath) out, err := cmd.Output() if err != nil { - return "", fmt.Errorf("调用python脚本 %v 下载新版本失败,err=%v", pythonScriptPath, err) + return "", errors.Errorf("调用python脚本 %v 下载新版本失败,err=%v", pythonScriptPath, err) } // 现在脚本基于github来下载,中间需要二次压缩,导致会出现一些多余的字符串,因此这里需要将结果部分提取出来 @@ -101,7 +103,7 @@ func downloadNewVersionUsingPythonScript(pythonInterpreterPath string, pythonScr var result PythonDownloadNewVersionResult err = json.Unmarshal([]byte(jsonResult), &result) if err != nil { - return "", fmt.Errorf("解析python返回的结果失败, jsonResult=%v, err=%v", jsonResult, err) + return "", errors.Errorf("解析python返回的结果失败, jsonResult=%v, err=%v", jsonResult, err) } return result.Filepath, nil @@ -143,7 +145,7 @@ func (r *QQRobot) manualTriggerUpdateNotify(triggerRule *Rule) (replies *message continue } - latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawUrl) + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawURL) if latestVersion == VersionNone { break } @@ -199,7 +201,7 @@ func (r *QQRobot) initCheckUpdateVersionMap() { } logger.Infof(bold(color.Yellow).Render(fmt.Sprintf("将以%v的间隔定期检查配置的项目的版本更新情况", time.Second*time.Duration(r.Config.NotifyUpdate.CheckInterval)))) for _, rule := range r.Config.NotifyUpdate.Rules { - latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawUrl) + latestVersion, updateMessage := r.getLatestGitVersion(rule.GitChangelogRawURL) r.CheckUpdateVersionMap[rule.Name] = latestVersion logger.Infof(bold(color.Yellow).Render(fmt.Sprintf("项目[%v]当前的最新版本为%v, 更新信息如下:\n%v", rule.Name, latestVersion, updateMessage))) } @@ -211,8 +213,8 @@ var regUpdateInfo = regexp.MustCompile(`更新公告\s*(?P(\s|\S // VersionNone 默认版本号 var VersionNone = "v0.0.0" -func (r *QQRobot) getLatestGitVersion(gitChangelogRawUrl string) (latestVersion string, updateMessage string) { - urls := generateMirrorGithubRawUrls(gitChangelogRawUrl) +func (r *QQRobot) getLatestGitVersion(gitChangelogRawURL string) (latestVersion string, updateMessage string) { + urls := generateMirrorGithubRawUrls(gitChangelogRawURL) for _, url := range urls { latestVersion, updateMessage = r._getLatestGitVersion(url) @@ -225,17 +227,17 @@ func (r *QQRobot) getLatestGitVersion(gitChangelogRawUrl string) (latestVersion } // 形如 https://github.com/fzls/djc_helper/raw/master/CHANGELOG.MD -var regRawUrl = regexp.MustCompile(`https://github.com/(?P\w+)/(?P\w+)/raw/(?P\w+)/(?P[\w\W]+)`) +var regRawURL = regexp.MustCompile(`https://github.com/(?P\w+)/(?P\w+)/raw/(?P\w+)/(?P[\w\W]+)`) -func generateMirrorGithubRawUrls(gitChangelogRawUrl string) []string { - match := regRawUrl.FindStringSubmatch(gitChangelogRawUrl) +func generateMirrorGithubRawUrls(gitChangelogRawURL string) []string { + match := regRawURL.FindStringSubmatch(gitChangelogRawURL) if match == nil { - return []string{gitChangelogRawUrl} + return []string{gitChangelogRawURL} } - owner := match[regRawUrl.SubexpIndex("owner")] - repoName := match[regRawUrl.SubexpIndex("repo_name")] - branchName := match[regRawUrl.SubexpIndex("branch_name")] - filepathInRepo := match[regRawUrl.SubexpIndex("filepath_in_repo")] + owner := match[regRawURL.SubexpIndex("owner")] + repoName := match[regRawURL.SubexpIndex("repo_name")] + branchName := match[regRawURL.SubexpIndex("branch_name")] + filepathInRepo := match[regRawURL.SubexpIndex("filepath_in_repo")] var urls []string diff --git a/qqrobot/config.go b/qqrobot/config.go index 8d9309225..3bfe6e5b9 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -143,7 +143,7 @@ type RuleConfig struct { TimePeriods []TimePeriod `toml:"time_periods"` // 适用该规则的时间段(前者包含,后者不包含) TriggerRuleCount int64 `toml:"trigger_rule_count"` // TriggerRuleDuration内触发的规则数目是否超过该数目 TriggerRuleDuration int64 `toml:"trigger_rule_duration"` // 判定恶意触发机器人规则的时间周期(秒) - GitChangelogRawUrl string `toml:"git_changelog_raw_url"` // 某git仓库的changelog的raw url,若设定,则将请求这个网页,从中解析出最新的版本号和更新信息,并替换到GuideContent中的$git_version$和$update_message$ + GitChangelogRawURL string `toml:"git_changelog_raw_url"` // 某git仓库的changelog的raw url,若设定,则将请求这个网页,从中解析出最新的版本号和更新信息,并替换到GuideContent中的$git_version$和$update_message$ GuideContentHasPermission string `toml:"guide_content_has_permission"` // 当有权限触发该指令时的回复 GuideContentHasNoPermission string `toml:"guide_content_has_no_permission"` // 当无权限触发该指令时的回复 TargetUpdateRuleName string `toml:"target_update_rule_name"` // 目标更新规则的名称 @@ -194,7 +194,7 @@ type NotifyUpdateRule struct { NotifyGroupTypes []string `toml:"notify_group_types"` // 通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 Message string `toml:"message"` // 通知的消息,参数:$git_version$=最新版本, $update_message$=更新信息 ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 - GitChangelogRawUrl string `toml:"git_changelog_raw_url"` // git仓库的changelog的raw url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ + GitChangelogRawURL string `toml:"git_changelog_raw_url"` // git仓库的changelog的raw url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ AtQQsOnTrigger []int64 `toml:"at_qqs_on_trigger"` // 需要at的qq列表 AtAllOnTrigger bool `toml:"at_all_on_trigger"` // 是否需要@全体成员 DownloadNewVersionPythonInterpreterPath string `toml:"download_new_version_python_interpreter_path"` // 下载新版本的所需的python解释器路径 diff --git a/qqrobot/qinglong/query_test.go b/qqrobot/qinglong/query_test.go index 7b5d746f5..9c20f6417 100644 --- a/qqrobot/qinglong/query_test.go +++ b/qqrobot/qinglong/query_test.go @@ -1,71 +1,71 @@ package qinglong import ( - "net/url" - "path/filepath" - "testing" + "net/url" + "path/filepath" + "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" ) // 2021/12/13 20:33 by fzls func TestQueryCookieInfo(t *testing.T) { - var info *JdCookieInfo + var info *JdCookieInfo - // 无参数 - info = QueryCookieInfo("") - assert.Nil(t, info) + // 无参数 + info = QueryCookieInfo("") + assert.Nil(t, info) - // pin - info = QueryCookieInfo("pin_1") - assert.NotNil(t, info) + // pin + info = QueryCookieInfo("pin_1") + assert.NotNil(t, info) - // 备注 - info = QueryCookieInfo("测试账号-1") - assert.NotNil(t, info) + // 备注 + info = QueryCookieInfo("测试账号-1") + assert.NotNil(t, info) - // 仅env中存在的账号,使用pin - info = QueryCookieInfo("pin_3") - assert.NotNil(t, info) + // 仅env中存在的账号,使用pin + info = QueryCookieInfo("pin_3") + assert.NotNil(t, info) - // 不存在的账号 - info = QueryCookieInfo("not exists") - assert.Nil(t, info) + // 不存在的账号 + info = QueryCookieInfo("not exists") + assert.Nil(t, info) } func TestQueryChartPath(t *testing.T) { - info := QueryCookieInfo("pin_1") - chartPath := QueryChartPath(info) - expected, _ := filepath.Abs(getPath("log/.bean_chart/chart_pin_1.jpeg")) - assert.Equal(t, expected, chartPath) + info := QueryCookieInfo("pin_1") + chartPath := QueryChartPath(info) + expected, _ := filepath.Abs(getPath("log/.bean_chart/chart_pin_1.jpeg")) + assert.Equal(t, expected, chartPath) } func TestQuerySummary(t *testing.T) { - info := QueryCookieInfo("pin_1") - assert.NotEmpty(t, QuerySummary(info)) + info := QueryCookieInfo("pin_1") + assert.NotEmpty(t, QuerySummary(info)) - info = QueryCookieInfo("pin_3") - assert.Empty(t, QuerySummary(info)) + info = QueryCookieInfo("pin_3") + assert.Empty(t, QuerySummary(info)) - // 即使所有日志都不包含农场,也应返回最后一个不为空的日志解析结果 - info = QueryCookieInfo(url.QueryEscape("pin_5")) - assert.NotEmpty(t, QuerySummary(info)) + // 即使所有日志都不包含农场,也应返回最后一个不为空的日志解析结果 + info = QueryCookieInfo(url.QueryEscape("pin_5")) + assert.NotEmpty(t, QuerySummary(info)) } func TestQueryCookieExpired(t *testing.T) { - info := QueryCookieInfo("pin_1") - assert.NotEmpty(t, QueryCookieExpired(info)) + info := QueryCookieInfo("pin_1") + assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("pin_2") - assert.NotEmpty(t, QueryCookieExpired(info)) + info = QueryCookieInfo("pin_2") + assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("pin_3") - assert.NotEmpty(t, QueryCookieExpired(info)) + info = QueryCookieInfo("pin_3") + assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo(url.QueryEscape("中文pin")) - assert.NotEmpty(t, QueryCookieExpired(info)) + info = QueryCookieInfo(url.QueryEscape("中文pin")) + assert.NotEmpty(t, QueryCookieExpired(info)) - info = QueryCookieInfo("pin_99999") - assert.Empty(t, QueryCookieExpired(info)) + info = QueryCookieInfo("pin_99999") + assert.Empty(t, QueryCookieExpired(info)) } diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 029c75bdf..528414759 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -488,8 +488,8 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { switch config.Action { case actionTypeGuide: guideContent = strings.ReplaceAll(guideContent, templateargsMutetime, strconv.FormatInt(muteTime, 10)) - if config.GitChangelogRawUrl != "" { - latestVersion, updateMessage := r.getLatestGitVersion(config.GitChangelogRawUrl) + if config.GitChangelogRawURL != "" { + latestVersion, updateMessage := r.getLatestGitVersion(config.GitChangelogRawURL) guideContent = strings.ReplaceAll(guideContent, templateargsGitversion, latestVersion) guideContent = strings.ReplaceAll(guideContent, templateargsUpdatemessage, updateMessage) } From ed93231ca34cd4e3ae0661f1bbf74ebf8df53a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 11 Aug 2022 16:52:41 +0800 Subject: [PATCH 420/612] =?UTF-8?q?feat:=20=E5=90=8C=E6=AD=A5python?= =?UTF-8?q?=E7=9A=84github=20raw=E9=95=9C=E5=83=8F=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 7adc0db8c..5db3a74c2 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -242,9 +242,15 @@ func generateMirrorGithubRawUrls(gitChangelogRawURL string) []string { var urls []string // 先加入比较快的几个镜像 + urls = append(urls, "https://hk1.monika.love/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") urls = append(urls, "https://raw.iqiq.io/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") - urls = append(urls, "https://raw.連接.台灣/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") urls = append(urls, "https://raw-gh.gcdn.mirr.one/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://raw.fastgit.org/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://raw.githubusercontents.com/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://gcore.jsdelivr.net/gh/{owner}/{repo_name}@{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://kgithub.com/{owner}/{repo_name}/raw/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://cdn.staticaly.com/gh/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://ghproxy.com/https://raw.githubusercontent.com/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") // 随机乱序,确保均匀分布请求 rand.Shuffle(len(urls), func(i, j int) { @@ -252,15 +258,10 @@ func generateMirrorGithubRawUrls(gitChangelogRawURL string) []string { }) // 然后加入几个慢的镜像和源站 - urls = append(urls, "https://cdn.staticaly.com/gh/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") - urls = append(urls, "https://gcore.jsdelivr.net/gh/{owner}/{repo_name}@{branch_name}/{filepath_in_repo}") urls = append(urls, "https://fastly.jsdelivr.net/gh/{owner}/{repo_name}@{branch_name}/{filepath_in_repo}") - urls = append(urls, "https://raw.fastgit.org/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") - urls = append(urls, "https://ghproxy.com/https://raw.githubusercontent.com/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") - urls = append(urls, "https://ghproxy.futils.com/https://github.com/{owner}/{repo_name}/blob/{branch_name}/{filepath_in_repo}") + urls = append(urls, "https://cdn.jsdelivr.net/gh/{owner}/{repo_name}@{branch_name}/{filepath_in_repo}") // 最后加入原始地址和一些不可达的 - urls = append(urls, "https://raw.githubusercontents.com/{owner}/{repo_name}/{branch_name}/{filepath_in_repo}") urls = append(urls, "https://github.com/{owner}/{repo_name}/raw/{branch_name}/{filepath_in_repo}") // 替换占位符为实际值 From 813881c60984e8e93d1872003c44e9ef571fe9c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 11 Aug 2022 17:05:48 +0800 Subject: [PATCH 421/612] fix: lint --- global/net.go | 3 +-- qqrobot/check_settlements.go | 4 ++-- qqrobot/check_update.go | 7 ++++--- qqrobot/food.go | 4 ++-- qqrobot/music.go | 6 +++--- qqrobot/qinglong/expired.go | 4 ++-- qqrobot/qinglong/query.go | 6 +++--- qqrobot/qinglong/summary.go | 4 ++-- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/global/net.go b/global/net.go index c72000a2b..e7ebc80a4 100644 --- a/global/net.go +++ b/global/net.go @@ -5,7 +5,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -52,7 +51,7 @@ func GetBytes(url string) ([]byte, error) { defer func() { _ = reader.Close() }() - return ioutil.ReadAll(reader) + return io.ReadAll(reader) } // DownloadFile 将给定URL对应的文件下载至给定Path diff --git a/qqrobot/check_settlements.go b/qqrobot/check_settlements.go index 880f7aa2f..c40a8fc8f 100644 --- a/qqrobot/check_settlements.go +++ b/qqrobot/check_settlements.go @@ -2,7 +2,7 @@ package qqrobot import ( "encoding/json" - "io/ioutil" + "io" "strings" "time" @@ -109,7 +109,7 @@ func (r *QQRobot) getLatestSettleInfo() (*SettleInfo, error) { } defer resp.Body.Close() - bytesData, _ := ioutil.ReadAll(resp.Body) + bytesData, _ := io.ReadAll(resp.Body) var settleResponse SettleResponse err = json.Unmarshal(bytesData, &settleResponse) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 5db3a74c2..c44c22625 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -3,7 +3,7 @@ package qqrobot import ( "encoding/json" "fmt" - "io/ioutil" + "io" "math/rand" "os/exec" "path/filepath" @@ -14,10 +14,11 @@ import ( "time" "github.com/Mrs4s/MiraiGo/message" - "github.com/Mrs4s/go-cqhttp/global" "github.com/gookit/color" "github.com/pkg/errors" logger "github.com/sirupsen/logrus" + + "github.com/Mrs4s/go-cqhttp/global" ) // 2021/10/02 5:21 by fzls @@ -290,7 +291,7 @@ func (r *QQRobot) _getLatestGitVersion(gitChangelogPage string) (string, string) defer resp.Body.Close() // 获取网页内容 - bytesData, err := ioutil.ReadAll(resp.Body) + bytesData, err := io.ReadAll(resp.Body) if err != nil { logger.Debugf("getLatestGitVersion gitChangelogPage=%v err=%v", gitChangelogPage, err) return VersionNone, "" diff --git a/qqrobot/food.go b/qqrobot/food.go index a8448e75d..b1fa4c981 100644 --- a/qqrobot/food.go +++ b/qqrobot/food.go @@ -3,7 +3,7 @@ package qqrobot import ( "encoding/json" "fmt" - "io/ioutil" + "io" "math/rand" "regexp" "strconv" @@ -66,7 +66,7 @@ func (r *QQRobot) tryFetchMoreFoodImages(rule *Rule, foodSiteURL string) error { defer resp.Body.Close() // 获取网页内容 - bytesData, _ := ioutil.ReadAll(resp.Body) + bytesData, _ := io.ReadAll(resp.Body) // 解析出所有美食图片 var foodImages []FoodImage diff --git a/qqrobot/music.go b/qqrobot/music.go index 4426348d4..cc9019ff5 100644 --- a/qqrobot/music.go +++ b/qqrobot/music.go @@ -1,7 +1,7 @@ package qqrobot import ( - "io/ioutil" + "io" "net/http" "net/url" @@ -62,7 +62,7 @@ func (r *QQRobot) queryNeteaseMusic(musicName string) string { if err != nil { return "0" } - data, err := ioutil.ReadAll(res.Body) + data, err := io.ReadAll(res.Body) _ = res.Body.Close() if err != nil { return "0" @@ -124,6 +124,6 @@ func netGet(url string, header http.Header) []byte { return nil } defer res.Body.Close() - result, _ := ioutil.ReadAll(res.Body) + result, _ := io.ReadAll(res.Body) return result } diff --git a/qqrobot/qinglong/expired.go b/qqrobot/qinglong/expired.go index 90b77f3a0..4af519ceb 100644 --- a/qqrobot/qinglong/expired.go +++ b/qqrobot/qinglong/expired.go @@ -1,7 +1,7 @@ package qinglong import ( - "io/ioutil" + "os" "strings" "github.com/Mrs4s/go-cqhttp/global" @@ -12,7 +12,7 @@ func parseCookieExpired(info *JdCookieInfo, logFilePath string) (result string, return "", false, true } - contentBytes, err := ioutil.ReadFile(logFilePath) + contentBytes, err := os.ReadFile(logFilePath) if err != nil { return "", false, true } diff --git a/qqrobot/qinglong/query.go b/qqrobot/qinglong/query.go index d1de8773b..8a3816d3f 100644 --- a/qqrobot/qinglong/query.go +++ b/qqrobot/qinglong/query.go @@ -2,7 +2,7 @@ package qinglong import ( "fmt" - "io/ioutil" + "os" "path/filepath" "sort" "strings" @@ -92,7 +92,7 @@ func QuerySummary(info *JdCookieInfo) string { func querySummary(info *JdCookieInfo, logFile string) string { summaryDir := getPath(logFile) - logFiles, err := ioutil.ReadDir(summaryDir) + logFiles, err := os.ReadDir(summaryDir) if err != nil { logger.Errorf("read log dir failed, err=%v", err) return "" @@ -125,7 +125,7 @@ func QueryCookieExpired(info *JdCookieInfo) string { } checkCookieDir := getPath("log/ccwav_QLScript2_jd_CheckCK") - logFiles, err := ioutil.ReadDir(checkCookieDir) + logFiles, err := os.ReadDir(checkCookieDir) if err != nil { logger.Errorf("read log dir failed, err=%v", err) return "" diff --git a/qqrobot/qinglong/summary.go b/qqrobot/qinglong/summary.go index 79e5edf5c..04bc2925d 100644 --- a/qqrobot/qinglong/summary.go +++ b/qqrobot/qinglong/summary.go @@ -2,7 +2,7 @@ package qinglong import ( "fmt" - "io/ioutil" + "os" "strings" "github.com/Mrs4s/go-cqhttp/global" @@ -13,7 +13,7 @@ func parseSummary(info *JdCookieInfo, logFilePath string) string { return "" } - contentBytes, err := ioutil.ReadFile(logFilePath) + contentBytes, err := os.ReadFile(logFilePath) if err != nil { return "" } From 4bb791952fc8142c162e3b958e73aab49da6b398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 11 Aug 2022 17:22:01 +0800 Subject: [PATCH 422/612] =?UTF-8?q?feat:=20=E5=87=BA=E9=94=99=E6=97=B6?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=89=93=E5=8D=B0=E8=84=9A=E6=9C=AC=E8=BE=93?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index c44c22625..55ea67374 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -92,7 +92,7 @@ func downloadNewVersionUsingPythonScript(pythonInterpreterPath string, pythonScr cmd.Dir = filepath.Dir(pythonScriptPath) out, err := cmd.Output() if err != nil { - return "", errors.Errorf("调用python脚本 %v 下载新版本失败,err=%v", pythonScriptPath, err) + return "", errors.Errorf("调用python脚本 %v 下载新版本失败,err=%v, out=%v", pythonScriptPath, err, string(out)) } // 现在脚本基于github来下载,中间需要二次压缩,导致会出现一些多余的字符串,因此这里需要将结果部分提取出来 From f41dfa0def73f37999d61d67fe81a12b21267351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 11 Aug 2022 17:24:24 +0800 Subject: [PATCH 423/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E7=AC=A6=E5=90=88=E9=A2=84=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 55ea67374..c5cd80a8f 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -98,7 +98,12 @@ func downloadNewVersionUsingPythonScript(pythonInterpreterPath string, pythonScr // 现在脚本基于github来下载,中间需要二次压缩,导致会出现一些多余的字符串,因此这里需要将结果部分提取出来 output := string(out) boundaryMark := "$$boundary$$" - jsonResult := strings.Split(output, boundaryMark)[1] + parts := strings.Split(output, boundaryMark) + if len(parts) != 3 { + return "", errors.Errorf("输出格式不符合预期,预期应由 %v 分隔为三部分, output=%v", boundaryMark, output) + } + + jsonResult := parts[1] jsonResult = strings.TrimSpace(jsonResult) var result PythonDownloadNewVersionResult From 4a521e930c52e5cccd33f0112a6999723de19770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 11 Aug 2022 22:58:19 +0800 Subject: [PATCH 424/612] =?UTF-8?q?feat:=20=E6=9C=BA=E5=99=A8=E4=BA=BA?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E9=80=9A=E7=9F=A5=E5=A2=9E=E5=8A=A0=E5=BB=B6?= =?UTF-8?q?=E6=97=B6=E5=A4=84=E7=90=86=EF=BC=8C=E7=A1=AE=E4=BF=9D=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E6=97=B6release=E6=B5=81=E7=A8=8B=E5=B7=B2=E7=BB=8F?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index c5cd80a8f..397177e3c 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -41,23 +41,35 @@ func (r *QQRobot) checkUpdates() { continue } - logger.Infof("%v 版本有更新 %v => %v, 开始通知各个群以及上传群文件", rule.Name, lastVersion, latestVersion) - - replies := r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) - nowStr := r.currentTime() - for _, groupID := range rule.NotifyGroups { - rspID := r.cqBot.SendGroupMessage(groupID, replies) - // 广播消息间强行间隔一秒 - time.Sleep(time.Second) - if rspID == -1 { - logger.Errorf("【%v Failed】 %v groupID=%v replies=%v err=%v", rule.Name, nowStr, groupID, replies, rspID) - continue + // re: 以下是临时措施,应对changelog中的版本更新后平均约 10 分钟后github action的打包release流程才完成的情况 + // undone: 等将新版本改成基于release中的meta信息来获取后,再改成实时通知 + go func() { + logger.Warnf("%v 版本有更新 %v => %v,但目前因获取版本的来源比release早约十分钟,因此在这里等待20分钟后再实际进行通知", rule.Name, lastVersion, latestVersion) + select { + case <-time.After(2 * 10 * time.Minute): + break + case <-r.quitCtx.Done(): + return } - logger.Infof("【%v】 %v groupID=%v replies=%v", rule.Name, nowStr, groupID, replies) - } - logger.Infof("check update %v, from %v to %v", rule.Name, lastVersion, latestVersion) - r.updateNewVersionInGroup(rule.Name, rule.NotifyGroups, rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath) + logger.Infof("%v 版本有更新 %v => %v, 开始通知各个群以及上传群文件", rule.Name, lastVersion, latestVersion) + + replies := r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) + nowStr := r.currentTime() + for _, groupID := range rule.NotifyGroups { + rspID := r.cqBot.SendGroupMessage(groupID, replies) + // 广播消息间强行间隔一秒 + time.Sleep(time.Second) + if rspID == -1 { + logger.Errorf("【%v Failed】 %v groupID=%v replies=%v err=%v", rule.Name, nowStr, groupID, replies, rspID) + continue + } + logger.Infof("【%v】 %v groupID=%v replies=%v", rule.Name, nowStr, groupID, replies) + } + logger.Infof("check update %v, from %v to %v", rule.Name, lastVersion, latestVersion) + + r.updateNewVersionInGroup(rule.Name, rule.NotifyGroups, rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath) + }() } } } From 93074ef4af0c484e8fd81b8b095a1c1f647240d6 Mon Sep 17 00:00:00 2001 From: Shigma Date: Sat, 13 Aug 2022 00:01:04 +0800 Subject: [PATCH 425/612] feat: support manually input ticket --- cmd/gocq/login.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 20c10e140..44f64eb8d 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -151,7 +151,7 @@ func loginResponseProcessor(res *client.LoginResponse) error { log.Warn("请输入(1 - 2) (将在10秒后自动选择1):") text = readLineTimeout(time.Second*10, "1") if strings.Contains(text, "1") { - ticket := sliderCaptchaProcessor(res.VerifyUrl) + ticket := getTicket(res.VerifyUrl) if ticket == "" { os.Exit(0) } @@ -218,9 +218,19 @@ func loginResponseProcessor(res *client.LoginResponse) error { } } -func sliderCaptchaProcessor(u string) string { +func getTicket(u string) (str string) { id := utils.RandomString(8) - log.Warnf("请前往该地址验证 -> %v", strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id))) + log.Warnf("请前往该地址验证 -> %v <- 或输入手动抓取的 ticket:(Enter 提交)", strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id))) + r := make(chan string) + select { + case r <- readLine(): + case r <- sliderCaptchaProcessor(id): + } + str = <-r + return +} + +func sliderCaptchaProcessor(id string) string { start := time.Now() for time.Since(start).Minutes() < 2 { time.Sleep(time.Second) From bf5562b6dd68eac2d1ed72c29c340595e415e435 Mon Sep 17 00:00:00 2001 From: Shigma Date: Sat, 13 Aug 2022 00:56:43 +0800 Subject: [PATCH 426/612] refa: use ticker for fetchCaptcha() --- cmd/gocq/login.go | 50 ++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 44f64eb8d..38770c720 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -221,29 +221,39 @@ func loginResponseProcessor(res *client.LoginResponse) error { func getTicket(u string) (str string) { id := utils.RandomString(8) log.Warnf("请前往该地址验证 -> %v <- 或输入手动抓取的 ticket:(Enter 提交)", strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id))) - r := make(chan string) - select { - case r <- readLine(): - case r <- sliderCaptchaProcessor(id): + manual := make(chan string) + go func() { + str = readLine() + manual <- str + }() + ticker := time.NewTicker(time.Second) + for count := 120; count > 0; count-- { + select { + case <-ticker.C: + str = fetchCaptcha(id) + if str != "" { + ticker.Stop() + return + } + case <-manual: + ticker.Stop() + return + } } - str = <-r - return + ticker.Stop() + log.Warnf("验证超时") + return "" } -func sliderCaptchaProcessor(id string) string { - start := time.Now() - for time.Since(start).Minutes() < 2 { - time.Sleep(time.Second) - data, err := global.GetBytes("https://captcha.go-cqhttp.org/captcha/ticket?id=" + id) - if err != nil { - log.Warnf("获取 Ticket 时出现错误: %v", err) - return "" - } - g := gjson.ParseBytes(data) - if g.Get("ticket").Exists() { - return g.Get("ticket").String() - } +func fetchCaptcha(id string) string { + data, err := global.GetBytes("https://captcha.go-cqhttp.org/captcha/ticket?id=" + id) + if err != nil { + log.Warnf("获取 Ticket 时出现错误: %v", err) + return "" + } + g := gjson.ParseBytes(data) + if g.Get("ticket").Exists() { + return g.Get("ticket").String() } - log.Warnf("验证超时") return "" } From 08b0837cab325dee51845fd271aeafcfb709a870 Mon Sep 17 00:00:00 2001 From: Shigma Date: Sat, 13 Aug 2022 01:04:29 +0800 Subject: [PATCH 427/612] fix: fix string racing --- cmd/gocq/login.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 38770c720..d963d2566 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -221,26 +221,23 @@ func loginResponseProcessor(res *client.LoginResponse) error { func getTicket(u string) (str string) { id := utils.RandomString(8) log.Warnf("请前往该地址验证 -> %v <- 或输入手动抓取的 ticket:(Enter 提交)", strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id))) - manual := make(chan string) + manual := make(chan string, 1) go func() { - str = readLine() - manual <- str + manual <- readLine() }() ticker := time.NewTicker(time.Second) + defer ticker.Stop() for count := 120; count > 0; count-- { select { case <-ticker.C: str = fetchCaptcha(id) if str != "" { - ticker.Stop() return } - case <-manual: - ticker.Stop() + case str = <-manual: return } } - ticker.Stop() log.Warnf("验证超时") return "" } From 933bdee18e6865f13a9ce7be1f8d9b6f8d4a39a7 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 14 Aug 2022 01:40:14 +0800 Subject: [PATCH 428/612] optimize the return value of group-message sending error --- coolq/api.go | 6 +++--- coolq/bot.go | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 63307f84c..81f7dedfe 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -765,9 +765,9 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b } } fixAt(elem) - mid := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem}) - if mid == -1 { - return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") + mid, err := bot.SendGroupMessage(groupID, &message.SendingMessage{Elements: elem}) + if err != nil { + return Failed(100, "SEND_MSG_API_ERROR", err.Error()) } log.Infof("发送群 %v(%v) 的消息: %v (%v)", group.Name, groupID, limitedString(m.String()), mid) return OK(global.MSG{"message_id": mid}) diff --git a/coolq/bot.go b/coolq/bot.go index 17722488a..126de6367 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -248,7 +248,7 @@ func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessage } // SendGroupMessage 发送群消息 -func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int32 { +func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) (int32, error) { newElem := make([]message.IMessageElement, 0, len(m.Elements)) group := bot.Client.FindGroup(groupID) source := message.Source{ @@ -264,14 +264,14 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int mem.Poke() } } - return 0 + return 0, nil case *message.MusicShareElement: ret, err := bot.Client.SendGroupMusicShare(groupID, i) if err != nil { log.Warnf("警告: 群 %v 富文本消息发送失败: %v", groupID, err) - return -1 + return -1, errors.Wrap(err, "send group music share error") } - return bot.InsertGroupMessage(ret) + return bot.InsertGroupMessage(ret), nil case *message.AtElement: if i.Target == 0 && group.SelfPermission() == client.Member { e = message.NewText("@全体成员") @@ -281,16 +281,16 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int } if len(newElem) == 0 { log.Warnf("群消息发送失败: 消息为空.") - return -1 + return -1, errors.New("empty message") } m.Elements = newElem bot.checkMedia(newElem, groupID) ret := bot.Client.SendGroupMessage(groupID, m) if ret == nil || ret.Id == -1 { log.Warnf("群消息发送失败: 账号可能被风控.") - return -1 + return -1, errors.New("send group message failed: blocked by server") } - return bot.InsertGroupMessage(ret) + return bot.InsertGroupMessage(ret), nil } // SendPrivateMessage 发送私聊消息 From aa310246feca412272db75730de7058b7ffd3242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Aug 2022 15:14:42 +0800 Subject: [PATCH 429/612] =?UTF-8?q?feat:=20=E4=B8=8A=E4=BC=A0=E7=BE=A4?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=94=AF=E6=8C=81=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 78 +++++++++++++++++++++++++++++++++++++---- qqrobot/qq_robot.go | 5 ++- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 397177e3c..bdb403f18 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -68,13 +68,13 @@ func (r *QQRobot) checkUpdates() { } logger.Infof("check update %v, from %v to %v", rule.Name, lastVersion, latestVersion) - r.updateNewVersionInGroup(rule.Name, rule.NotifyGroups, rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath) + r.updateNewVersionInGroup(rule.Name, rule.NotifyGroups, rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath, true) }() } } } -func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interpreter string, script string) { +func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interpreter string, script string, needRetry bool) { if interpreter != "" && script != "" && global.PathExists(interpreter) && global.PathExists(script) { logger.Infof("开始更新新版本到各个群中: %v", groups) oldVersionKeywords := "DNF蚊子腿小助手_v" @@ -88,11 +88,51 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete uploadFileName := filepath.Base(newVersionFilePath) - for _, groupID := range groups { - logger.Infof("开始上传 %v 到 群 %v", uploadFileName, groupID) - r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords) - // 广播消息间强行间隔一秒 - time.Sleep(time.Second) + groupsToUpload := groups + failIndex := 1 + retryWaitTime := time.Minute + maxRetryWaitTime := time.Hour + for { + // 尝试上传新版本 + for _, groupID := range groupsToUpload { + logger.Infof("开始上传 %v 到 群 %v", uploadFileName, groupID) + r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords) + // 广播消息间强行间隔一秒 + time.Sleep(time.Second) + } + + // 检查是否上传成功 + var groupsNotUploaded []int64 + for _, groupID := range groupsToUpload { + if !r.hasFileInGroup(groupID, uploadFileName) { + groupsNotUploaded = append(groupsNotUploaded, groupID) + } + } + if len(groupsNotUploaded) == 0 { + logger.Infof("全部上传成功,完毕") + break + } + + logger.Infof("%v 个群未上传成功: %v", len(groupsNotUploaded), groupsNotUploaded) + if !needRetry { + logger.Warnf("当前配置为不需要重试") + break + } + + logger.Infof("第 %v 次上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, retryWaitTime) + select { + case <-time.After(retryWaitTime): + break + case <-r.quitCtx.Done(): + return + } + + groupsToUpload = groupsNotUploaded + failIndex += 1 + retryWaitTime *= 2 + if retryWaitTime >= maxRetryWaitTime { + retryWaitTime = maxRetryWaitTime + } } } else { logger.Infof("%v: 未配置更新python脚本,或者对应脚本不存在,将不会尝试下载并上传新版本到群文件", ctx) @@ -157,6 +197,30 @@ func (r *QQRobot) updateFileInGroup(groupID int64, localFilePath string, uploadF logger.Warnf("上传群 %v 文件 %v 结果为 %v", groupID, uploadFileName, err) } +// hasFileInGroup 指定群中是否有指定名称的文件 +func (r *QQRobot) hasFileInGroup(groupID int64, uploadFileName string) (has bool) { + // 获取群文件信息 + fs, err := r.cqBot.Client.GetGroupFileSystem(groupID) + if err != nil { + logger.Warnf("获取群 %v 文件系统信息失败: %v", groupID, err) + return + } + files, _, err := fs.Root() + if err != nil { + logger.Warnf("获取群 %v 根目录文件失败: %v", groupID, err) + return + } + + // 移除之前版本 + for _, file := range files { + if file.FileName == uploadFileName { + return true + } + } + + return +} + func (r *QQRobot) manualTriggerUpdateNotify(triggerRule *Rule) (replies *message.SendingMessage) { for _, rule := range r.Config.NotifyUpdate.Rules { if rule.Name != triggerRule.Config.TargetUpdateRuleName { diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 528414759..ba57bccb3 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -598,7 +598,10 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { case actiontypeUpdateNewVersion: if isAdmin { r.cqBot.SendGroupMessage(groupID, message.NewSendingMessage().Append(message.NewText("开始执行上传指令,可能需要一点时间~"))) - r.updateNewVersionInGroup("手动触发更新新版本", config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath) + r.updateNewVersionInGroup("手动触发更新新版本", + config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath, + false, + ) replies.Append(message.NewText("指令执行成功,可查看各个群看看是否上传成功。如果失败,可查看机器人的服务器日志排查")) } else { replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) From 61ef142f8b8546d020013001df293bc65346588b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Aug 2022 15:18:50 +0800 Subject: [PATCH 430/612] =?UTF-8?q?feat:=20=E4=B8=8A=E4=BC=A0=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E8=A6=86=E7=9B=96=E5=B7=B2=E6=9C=89=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index bdb403f18..9445e179c 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -96,7 +96,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete // 尝试上传新版本 for _, groupID := range groupsToUpload { logger.Infof("开始上传 %v 到 群 %v", uploadFileName, groupID) - r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords) + r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords, false) // 广播消息间强行间隔一秒 time.Sleep(time.Second) } @@ -167,7 +167,13 @@ func downloadNewVersionUsingPythonScript(pythonInterpreterPath string, pythonScr return result.Filepath, nil } -func (r *QQRobot) updateFileInGroup(groupID int64, localFilePath string, uploadFileName string, oldVersionKeyWords string) { +func (r *QQRobot) updateFileInGroup( + groupID int64, + localFilePath string, + uploadFileName string, + oldVersionKeyWords string, + replaceIfExists bool, +) { logger.Infof("开始更新 群 %v 的 %v 文件,旧版本关键词为 %v,新版本路径为 %v", groupID, uploadFileName, oldVersionKeyWords, localFilePath) // 获取群文件信息 @@ -182,6 +188,16 @@ func (r *QQRobot) updateFileInGroup(groupID int64, localFilePath string, uploadF return } + if !replaceIfExists { + // 如果已经存在该文件,则直接返回 + for _, file := range files { + if file.FileName == uploadFileName { + logger.Infof("群 %v 中已有 %v 文件,且当前配置为不覆盖已有文件,将直接跳过", groupID, uploadFileName) + return + } + } + } + // 移除之前版本 for _, file := range files { if strings.Contains(file.FileName, oldVersionKeyWords) { From 3bd8fbe3e8786d15b1580559c92f1120cef77b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Aug 2022 15:24:56 +0800 Subject: [PATCH 431/612] =?UTF-8?q?feat:=20=E5=8E=9F=E6=9C=89=E7=9A=84?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=AE=8C=E6=AF=95=E6=8F=90=E7=A4=BA=E8=AF=AD?= =?UTF-8?q?=E4=BC=BC=E4=B9=8E=E5=8C=85=E5=90=AB=E6=95=8F=E6=84=9F=E8=AF=8D?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=E6=95=B4=E6=8F=90=E7=A4=BA=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index ba57bccb3..6034afc51 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -602,7 +602,7 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath, false, ) - replies.Append(message.NewText("指令执行成功,可查看各个群看看是否上传成功。如果失败,可查看机器人的服务器日志排查")) + replies.Append(message.NewText("上传完毕,具体情况可查看服务器日志")) } else { replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) } From a09d5e9f1c1f4d4ad3b74e6bd82775a25721c48d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 22 Aug 2022 15:36:24 +0800 Subject: [PATCH 432/612] =?UTF-8?q?feat:=20=E6=9C=80=E5=A4=9A=E9=87=8D?= =?UTF-8?q?=E8=AF=9510=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 9445e179c..254f70906 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -90,6 +90,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete groupsToUpload := groups failIndex := 1 + maxFailTimes := 10 retryWaitTime := time.Minute maxRetryWaitTime := time.Hour for { @@ -119,6 +120,11 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete break } + if failIndex > maxFailTimes { + logger.Warnf("重试次数超过 %v 次,停止重试", maxFailTimes) + break + } + logger.Infof("第 %v 次上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, retryWaitTime) select { case <-time.After(retryWaitTime): From 656dc6b1035c8b6d40c4cc8c2565a15d7785ca50 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 28 Aug 2022 17:33:35 +0800 Subject: [PATCH 433/612] all: fix golangci-lint --- .github/workflows/golint.yml | 6 ------ .golangci.yml | 20 ++++++++------------ coolq/cqcode.go | 8 ++++---- global/net.go | 25 +++++++++++-------------- main.go | 1 + server/http.go | 4 ++-- server/scf.go | 2 +- 7 files changed, 27 insertions(+), 39 deletions(-) diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index fb9cc4d5b..6ab8830aa 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -19,12 +19,6 @@ jobs: with: version: latest - - name: Static Check - uses: dominikh/staticcheck-action@v1.2.0 - with: - install-go: false - version: "2022.1" - - name: Tests run: | go test $(go list ./...) diff --git a/.golangci.yml b/.golangci.yml index 0a87b864b..5f654d69f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,10 +21,8 @@ linters: disable-all: true fast: false enable: - #- bodyclose - #- deadcode - #- depguard - #- dogsled + - bodyclose + - durationcheck - gofmt - goimports - errcheck @@ -32,18 +30,16 @@ linters: - exhaustive - bidichk - gocritic - #- gosimple + - gosimple - govet - ineffassign #- nolintlint - #- rowserrcheck - #- staticcheck - - structcheck - #- stylecheck + - staticcheck + - stylecheck - unconvert - #- unparam - #- unused - - varcheck + - usestdlibvars + - unparam + - unused - whitespace - prealloc - predeclared diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 750557686..283213b8c 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -733,11 +733,11 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message case *message.GroupImageElement: img.Flash = flash img.EffectID = id - switch data["subType"].(type) { + switch sub := data["subType"].(type) { case int64: - img.ImageBizType = message.ImageBizType(data["subType"].(int64)) - default: - img.ImageBizType = message.ImageBizType(data["subType"].(uint32)) + img.ImageBizType = message.ImageBizType(sub) + case uint32: + img.ImageBizType = message.ImageBizType(sub) } case *message.FriendImageElement: img.Flash = flash diff --git a/global/net.go b/global/net.go index 601123229..0327a7e0c 100644 --- a/global/net.go +++ b/global/net.go @@ -5,7 +5,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -52,7 +51,7 @@ func GetBytes(url string) ([]byte, error) { defer func() { _ = reader.Close() }() - return ioutil.ReadAll(reader) + return io.ReadAll(reader) } // DownloadFile 将给定URL对应的文件下载至给定Path @@ -62,7 +61,7 @@ func DownloadFile(url, path string, limit int64, headers map[string]string) erro return err } defer file.Close() - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return err } @@ -112,7 +111,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int, } return errUnsupportedMultiThreading } - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return err } @@ -132,23 +131,21 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int, if resp.StatusCode < 200 || resp.StatusCode >= 300 { return errors.New("response status unsuccessful: " + strconv.FormatInt(int64(resp.StatusCode), 10)) } - if resp.StatusCode == 200 { + if resp.StatusCode == http.StatusOK { if limit > 0 && resp.ContentLength > limit { return ErrOverSize } return copyStream(resp.Body) } - if resp.StatusCode == 206 { + if resp.StatusCode == http.StatusPartialContent { contentLength = resp.ContentLength if limit > 0 && resp.ContentLength > limit { return ErrOverSize } - blockSize := func() int64 { - if contentLength > 1024*1024 { - return (contentLength / int64(threadCount)) - 10 - } - return contentLength - }() + blockSize := contentLength + if contentLength > 1024*1024 { + blockSize = (contentLength / int64(threadCount)) - 10 + } if blockSize == contentLength { return copyStream(resp.Body) } @@ -170,7 +167,7 @@ func DownloadFileMultiThreading(url, path string, limit int64, threadCount int, } // 下载分块 downloadBlock := func(block *BlockMetaData) error { - req, _ := http.NewRequest("GET", url, nil) + req, _ := http.NewRequest(http.MethodGet, url, nil) file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666) if err != nil { return err @@ -291,7 +288,7 @@ func (g *gzipCloser) Close() error { // HTTPGetReadCloser 从 Http url 获取 io.ReadCloser func HTTPGetReadCloser(url string) (io.ReadCloser, error) { - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err } diff --git a/main.go b/main.go index 0cd60791f..fa75deda7 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,4 @@ +// Package main package main import ( diff --git a/server/http.go b/server/http.go index 3fd7c3779..a78102e07 100644 --- a/server/http.go +++ b/server/http.go @@ -356,13 +356,13 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { for i := uint64(0); i <= c.MaxRetries; i++ { // see https://stackoverflow.com/questions/31337891/net-http-http-contentlength-222-with-body-length-0 // we should create a new request for every single post trial - req, err = http.NewRequest("POST", c.addr, bytes.NewReader(e.JSONBytes())) + req, err = http.NewRequest(http.MethodGet, c.addr, bytes.NewReader(e.JSONBytes())) if err != nil { log.Warnf("上报 Event 数据到 %v 时创建请求失败: %v", c.addr, err) return } req.Header = header - res, err = c.client.Do(req) + res, err = c.client.Do(req) // nolint:bodyclose if err == nil { break } diff --git a/server/scf.go b/server/scf.go index 7d95e7f01..517ff1dfc 100644 --- a/server/scf.go +++ b/server/scf.go @@ -65,7 +65,7 @@ func (l *lambdaResponseWriter) flush() error { Body: body, }) - r, _ := http.NewRequest("POST", cli.responseURL, buffer) + r, _ := http.NewRequest(http.MethodPost, cli.responseURL, buffer) do, err := cli.client.Do(r) if err != nil { return err From 65b050c7818ad5d97fc3020b1bf5fa030a13d523 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 29 Aug 2022 18:31:56 +0800 Subject: [PATCH 434/612] ci: update go1.19&MiraiGo --- .github/workflows/ci.yml | 2 +- .github/workflows/golint.yml | 2 +- .github/workflows/release.yml | 2 +- Dockerfile | 2 +- go.mod | 13 ++++++------- go.sum | 27 ++++++++++++++------------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05bc69a27..2e1cee170 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v2.1.3 with: - go-version: 1.18 + go-version: 1.19 - name: Cache downloaded module uses: actions/cache@v2 with: diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index 6ab8830aa..89fffe563 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -12,7 +12,7 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v2.1.3 with: - go-version: 1.18 + go-version: 1.19 - name: golangci-lint uses: golangci/golangci-lint-action@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2fb331b1..e53dc2c11 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: '1.18' + go-version: '1.19' - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 diff --git a/Dockerfile b/Dockerfile index 2f2f1acd7..27e5cfba2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.18-alpine AS builder +FROM golang:1.19-alpine AS builder RUN go env -w GO111MODULE=auto \ && go env -w CGO_ENABLED=0 \ diff --git a/go.mod b/go.mod index e9908ea3a..0de9c8b62 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/Mrs4s/go-cqhttp -go 1.18 +go 1.19 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220630160133-a39b3fdd962f + github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 @@ -14,9 +14,9 @@ require ( github.com/pkg/errors v0.9.1 github.com/segmentio/asm v1.1.3 github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.8.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tidwall/gjson v1.14.0 + github.com/tidwall/gjson v1.14.3 github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 go.mongodb.org/mongo-driver v1.8.3 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 @@ -36,7 +36,7 @@ require ( github.com/klauspost/compress v1.13.6 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/mattn/go-isatty v0.0.14 // indirect - github.com/pierrec/lz4/v4 v4.1.11 // indirect + github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -45,9 +45,8 @@ require ( github.com/xdg-go/scram v1.0.2 // indirect github.com/xdg-go/stringprep v1.0.2 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect golang.org/x/text v0.3.7 // indirect modernc.org/libc v1.8.1 // indirect diff --git a/go.sum b/go.sum index 62f01ce80..47b2da4d0 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220630160133-a39b3fdd962f h1:PHOwN3/cEL/zoBpcJJXwx1mJ1NL901zrt2mvlQUO5BA= -github.com/Mrs4s/MiraiGo v0.0.0-20220630160133-a39b3fdd962f/go.mod h1:Ow7nlaVS5FztyjrTlTRSG7HLpTNmgINMluHRCgKunDI= +github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe h1:KpRbU5MRL2dplf01Cbgo8m0+ST1V1A+4cFBQkQA/YAk= +github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM= github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0 h1:+UGPBYVjssFsdahLJIiNPwpmmwgl/OaVdv1oc5NonC0= github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= @@ -43,8 +43,8 @@ github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9q github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -69,8 +69,8 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/pierrec/lz4/v4 v4.1.11 h1:LVs17FAZJFOjgmJXl9Tf13WfLUvZq7/RjfEJrnwZ9OE= -github.com/pierrec/lz4/v4 v4.1.11/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -78,22 +78,25 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= -github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= +github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -111,8 +114,6 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -128,8 +129,8 @@ golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -170,8 +171,8 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= From 98712bf9ca2ddd6e05399517995c6eeadbddfdff Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 31 Aug 2022 16:31:47 +0800 Subject: [PATCH 435/612] move mime to internal package --- coolq/bot.go | 6 +++--- coolq/cqcode.go | 5 +++-- internal/base/feature.go | 12 ------------ {modules => internal}/mime/mime.go | 23 +++++++++-------------- main.go | 1 - 5 files changed, 15 insertions(+), 32 deletions(-) rename {modules => internal}/mime/mime.go (65%) diff --git a/coolq/bot.go b/coolq/bot.go index 126de6367..de841313e 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -23,6 +23,7 @@ import ( "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/internal/mime" ) // CQBot CQBot结构体,存储Bot实例相关配置 @@ -152,10 +153,9 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement defer func() { _ = f.Close() }() img.Stream = f } - if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { - return nil, errors.New("image type error: " + mime) + if mt, ok := mime.CheckImage(img.Stream); !ok { + return nil, errors.New("image type error: " + mt) } - // todo: enable multi-thread upload, now got error code 81 i, err := bot.Client.UploadImage(target, img.Stream, 4) if err != nil { return nil, err diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 283213b8c..2c3bc651c 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -28,6 +28,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" + "github.com/Mrs4s/go-cqhttp/internal/mime" "github.com/Mrs4s/go-cqhttp/internal/param" ) @@ -844,8 +845,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return nil, err } if !global.IsAMRorSILK(data) { - lawful, mt := base.IsLawfulAudio(bytes.NewReader(data)) - if !lawful { + mt, ok := mime.CheckAudio(bytes.NewReader(data)) + if !ok { return nil, errors.New("audio type error: " + mt) } data, err = global.EncoderSilk(data) diff --git a/internal/base/feature.go b/internal/base/feature.go index f27c0a091..d57a9aaea 100644 --- a/internal/base/feature.go +++ b/internal/base/feature.go @@ -1,8 +1,6 @@ package base import ( - "io" - "github.com/pkg/errors" ) @@ -19,13 +17,3 @@ func encodeSilk(_ []byte, _ string) ([]byte, error) { func resampleSilk(data []byte) []byte { return data } - -// Mime scan feature -var ( - IsLawfulImage = nocheck // 检查图片MIME - IsLawfulAudio = nocheck // 检查音频MIME -) - -func nocheck(_ io.ReadSeeker) (bool, string) { - return true, "" -} diff --git a/modules/mime/mime.go b/internal/mime/mime.go similarity index 65% rename from modules/mime/mime.go rename to internal/mime/mime.go index f6bfa4baa..c8765bd4b 100644 --- a/modules/mime/mime.go +++ b/internal/mime/mime.go @@ -9,11 +9,6 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" ) -func init() { - base.IsLawfulImage = checkImage - base.IsLawfulAudio = checkAudio -} - const limit = 4 * 1024 func scan(r io.ReadSeeker) string { @@ -24,15 +19,15 @@ func scan(r io.ReadSeeker) string { return http.DetectContentType(in) } -// checkImage 判断给定流是否为合法图片 +// CheckImage 判断给定流是否为合法图片 // 返回 是否合法, 实际Mime // 判断后会自动将 Stream Seek 至 0 -func checkImage(r io.ReadSeeker) (ok bool, t string) { +func CheckImage(r io.ReadSeeker) (t string, ok bool) { if base.SkipMimeScan { - return true, "" + return "", true } if r == nil { - return false, "image/nil-stream" + return "image/nil-stream", false } t = scan(r) switch t { @@ -42,15 +37,15 @@ func checkImage(r io.ReadSeeker) (ok bool, t string) { return } -// checkImage 判断给定流是否为合法音频 -func checkAudio(r io.ReadSeeker) (bool, string) { +// CheckAudio 判断给定流是否为合法音频 +func CheckAudio(r io.ReadSeeker) (string, bool) { if base.SkipMimeScan { - return true, "" + return "", true } t := scan(r) // std mime type detection is not full supported for audio if strings.Contains(t, "text") || strings.Contains(t, "image") { - return false, t + return t, false } - return true, t + return t, true } diff --git a/main.go b/main.go index fa75deda7..1d7970454 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,6 @@ import ( "github.com/Mrs4s/go-cqhttp/cmd/gocq" _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb - _ "github.com/Mrs4s/go-cqhttp/modules/mime" // mime检查模块 _ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块 // 其他模块 // _ "github.com/Mrs4s/go-cqhttp/db/mongodb" // mongodb 数据库支持 From 1ff5e4de122da332045dcbe362ded483022eb97c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 31 Aug 2022 17:24:22 +0800 Subject: [PATCH 436/612] rf: remove Base64DecodeString Now segmentio/asm resolve issue 50, we don't need this wrapper. --- coolq/cqcode.go | 3 ++- global/fs.go | 5 ++--- internal/param/param.go | 12 ------------ 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 2c3bc651c..1f1475b66 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -20,6 +20,7 @@ import ( "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" b14 "github.com/fumiama/go-base16384" + "github.com/segmentio/asm/base64" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" @@ -1122,7 +1123,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return &LocalImageElement{File: fu.Path, URL: f}, nil } if !video && strings.HasPrefix(f, "base64") { - b, err := param.Base64DecodeString(strings.TrimPrefix(f, "base64://")) + b, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(f, "base64://")) if err != nil { return nil, err } diff --git a/global/fs.go b/global/fs.go index ccfac88df..f985dd5a1 100644 --- a/global/fs.go +++ b/global/fs.go @@ -14,9 +14,8 @@ import ( "github.com/Mrs4s/MiraiGo/utils" b14 "github.com/fumiama/go-base16384" + "github.com/segmentio/asm/base64" log "github.com/sirupsen/logrus" - - "github.com/Mrs4s/go-cqhttp/internal/param" ) const ( @@ -89,7 +88,7 @@ func FindFile(file, cache, p string) (data []byte, err error) { return nil, err } case strings.HasPrefix(file, "base64"): - data, err = param.Base64DecodeString(strings.TrimPrefix(file, "base64://")) + data, err = base64.StdEncoding.DecodeString(strings.TrimPrefix(file, "base64://")) if err != nil { return nil, err } diff --git a/internal/param/param.go b/internal/param/param.go index fd86b7462..19da7fc39 100644 --- a/internal/param/param.go +++ b/internal/param/param.go @@ -7,8 +7,6 @@ import ( "strings" "sync" - "github.com/Mrs4s/MiraiGo/utils" - "github.com/segmentio/asm/base64" "github.com/tidwall/gjson" ) @@ -83,13 +81,3 @@ func SplitURL(s string) []string { result = append(result, s[last:]) return result } - -// Base64DecodeString decode base64 with avx2 -// see https://github.com/segmentio/asm/issues/50 -// avoid incorrect unsafe usage in origin library -func Base64DecodeString(s string) ([]byte, error) { - e := base64.StdEncoding - dst := make([]byte, e.DecodedLen(len(s))) - n, err := e.Decode(dst, utils.S2B(s)) - return dst[:n], err -} From 96e63976363cf969a1bec51fce6f43aa275b6d7d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 31 Aug 2022 20:30:16 +0800 Subject: [PATCH 437/612] btree: remove resethash --- internal/btree/btree_test.go | 1 + internal/btree/chunk.go | 26 ++++++++++---------------- internal/btree/helper.go | 5 ----- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/internal/btree/btree_test.go b/internal/btree/btree_test.go index 464c7ac76..649a36cbc 100644 --- a/internal/btree/btree_test.go +++ b/internal/btree/btree_test.go @@ -40,6 +40,7 @@ func TestBtree(t *testing.T) { "123", "We are met on a great battle-field of that war.", "Abraham Lincoln, November 19, 1863, Gettysburg, Pennsylvania", + // "00", // TODO: fix this } sha := make([]*byte, len(tests)) for i, tt := range tests { diff --git a/internal/btree/chunk.go b/internal/btree/chunk.go index 4b38c1440..524fc1d6a 100644 --- a/internal/btree/chunk.go +++ b/internal/btree/chunk.go @@ -1,6 +1,7 @@ package btree import ( + "encoding/binary" "math/rand" "unsafe" ) @@ -31,9 +32,8 @@ func (d *DB) allocChunk(size int) int64 { /* create fake size SHA-1 */ var sha1 [hashSize]byte - p := unsafe.Pointer(&sha1[0]) - *(*int32)(p) = -1 // *(uint32_t *) hash = -1; - *(*uint32)(unsafe.Add(p, i32s)) = uint32(size) // ((__be32 *) hash)[1] = to_be32(size); + binary.LittleEndian.PutUint32(sha1[0*4:1*4], ^uint32(0)) // *(uint32_t *) hash = -1; + binary.LittleEndian.PutUint32(sha1[1*4:2*4], uint32(size)) // ((__be32 *) hash)[1] = to_be32(size); /* find free chunk with the larger or the same size/SHA-1 */ d.inAllocator = true @@ -41,14 +41,13 @@ func (d *DB) allocChunk(size int) int64 { offset = d.delete(d.freeTop, &sha1[0]) d.deleteLarger = false if offset != 0 { - assert(*(*int32)(p) == -1) // assert(*(uint32_t *) hash == (uint32_t) -1) - flen := int(*(*uint32)(unsafe.Add(p, i32s))) // size_t free_len = from_be32(((__be32 *) hash)[1]) + flen := int(binary.LittleEndian.Uint32(sha1[:4])) // size_t free_len = from_be32(((__be32 *) hash)[1]) assert(power2(flen) == flen) assert(flen >= size) /* delete buddy information */ - resethash(&sha1[0]) - *(*int64)(p) = offset + sha1 = [hashSize]byte{} + binary.LittleEndian.PutUint64(sha1[0*8:1*8], uint64(offset)) buddyLen := d.delete(d.freeTop, &sha1[0]) assert(buddyLen == int64(size)) @@ -98,17 +97,12 @@ func (d *DB) freeChunk(offset int64, size int) { /* create fake offset SHA-1 for buddy allocation */ var sha1 [hashSize]byte - p := unsafe.Pointer(&sha1[0]) d.inAllocator = true - - const i32s = unsafe.Sizeof(int32(0)) - /* add buddy information */ - resethash(&sha1[0]) - *(*int32)(p) = -1 // *(uint32_t *) hash = -1; - *(*uint32)(unsafe.Add(p, i32s)) = uint32(size) // ((__be32 *) hash)[1] = to_be32(size); - *(*uint32)(unsafe.Add(p, i32s*2)) = rand.Uint32() /* to make SHA-1 unique */ - *(*uint32)(unsafe.Add(p, i32s*3)) = rand.Uint32() + binary.LittleEndian.PutUint32(sha1[0*4:1*4], ^uint32(0)) // *(uint32_t *) hash = -1; + binary.LittleEndian.PutUint32(sha1[1*4:2*4], uint32(size)) // ((__be32 *) hash)[1] = to_be32(size); + binary.LittleEndian.PutUint32(sha1[2*4:3*4], rand.Uint32()) /* to make SHA-1 unique */ + binary.LittleEndian.PutUint32(sha1[3*4:4*4], rand.Uint32()) // insert_toplevel(btree, &btree->free_top, hash, NULL, offset); _ = d.insertTopLevel(&d.freeTop, &sha1[0], nil, int(offset)) diff --git a/internal/btree/helper.go b/internal/btree/helper.go index eb9c1f78c..762ae7bb5 100644 --- a/internal/btree/helper.go +++ b/internal/btree/helper.go @@ -37,11 +37,6 @@ func copyhash(dst *byte, src *byte) { *(*[hashSize]byte)(pa) = *(*[hashSize]byte)(pb) } -func resethash(sha1 *byte) { - p := unsafe.Pointer(sha1) - *(*[hashSize]byte)(p) = [hashSize]byte{} -} - // reading table func read32(r io.Reader) (int32, error) { From 2f92146092a02346376db29284ae1ed9a190278d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 31 Aug 2022 21:07:18 +0800 Subject: [PATCH 438/612] cache: switch to leveldb --- internal/cache/cache.go | 69 +++++++------------------------ modules/config/default_config.yml | 5 --- 2 files changed, 15 insertions(+), 59 deletions(-) diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 30cb53899..c99e0d423 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -2,14 +2,9 @@ package cache import ( - "fmt" - "sync" - log "github.com/sirupsen/logrus" - - "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/base" - "github.com/Mrs4s/go-cqhttp/internal/btree" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/opt" ) // Media Cache DBs @@ -21,70 +16,36 @@ var ( // Cache wraps the btree.DB for concurrent safe type Cache struct { - lock sync.RWMutex - db *btree.DB + ldb *leveldb.DB } // Insert 添加媒体缓存 func (c *Cache) Insert(md5, data []byte) { - c.lock.Lock() - defer c.lock.Unlock() - - var hash [16]byte - copy(hash[:], md5) - c.db.Insert(&hash[0], data) + _ = c.ldb.Put(md5, data, nil) } // Get 获取缓存信息 func (c *Cache) Get(md5 []byte) []byte { - c.lock.RLock() - defer c.lock.RUnlock() - - var hash [16]byte - copy(hash[:], md5) - return c.db.Get(&hash[0]) + got, _ := c.ldb.Get(md5, nil) + return got } // Delete 删除指定缓存 func (c *Cache) Delete(md5 []byte) { - c.lock.Lock() - defer c.lock.Unlock() - - var hash [16]byte - copy(hash[:], md5) - _ = c.db.Delete(&hash[0]) + _ = c.ldb.Delete(md5, nil) } // Init 初始化 Cache func Init() { - node, ok := base.Database["cache"] - var conf map[string]string - if ok { - err := node.Decode(&conf) + open := func(typ, path string, cache *Cache) { + ldb, err := leveldb.OpenFile(path, &opt.Options{ + WriteBuffer: 4 * opt.KiB, + }) if err != nil { - log.Fatalf("failed to read cache config: %v", err) - } - } - - open := func(typ string, cache *Cache) { - file := conf[typ] - if file == "" { - file = fmt.Sprintf("data/%s.db", typ) - } - if global.PathExists(file) { - db, err := btree.Open(file) - if err != nil { - log.Fatalf("open %s cache failed: %v", typ, err) - } - cache.db = db - } else { - db, err := btree.Create(file) - if err != nil { - log.Fatalf("create %s cache failed: %v", typ, err) - } - cache.db = db + log.Fatalf("open cache %s db failed: %v", typ, err) } + cache.ldb = ldb } - open("image", &Image) - open("video", &Video) + open("image", "data/images", &Image) + open("video", "data/videos", &Video) } diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 69e23cd93..7d2f7e977 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -79,11 +79,6 @@ database: # 数据库相关设置 # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 enable: true - # 媒体文件缓存, 删除此项则使用缓存文件(旧版行为) - cache: - image: data/image.db - video: data/video.db - # 连接服务列表 servers: # 添加方式,同一连接方式可添加多个,具体配置说明请查看文档 From 093605cf016c15f7951df135864d14211cf0798d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 31 Aug 2022 21:11:23 +0800 Subject: [PATCH 439/612] internal/btree: remove Goodbye! --- internal/btree/btree.go | 571 ---------------------------- internal/btree/btree_test.go | 103 ----- internal/btree/chunk.go | 116 ------ internal/btree/file_lock_unix.go | 45 --- internal/btree/file_lock_windows.go | 28 -- internal/btree/helper.go | 101 ----- 6 files changed, 964 deletions(-) delete mode 100644 internal/btree/btree.go delete mode 100644 internal/btree/btree_test.go delete mode 100644 internal/btree/chunk.go delete mode 100644 internal/btree/file_lock_unix.go delete mode 100644 internal/btree/file_lock_windows.go delete mode 100644 internal/btree/helper.go diff --git a/internal/btree/btree.go b/internal/btree/btree.go deleted file mode 100644 index 81adcb902..000000000 --- a/internal/btree/btree.go +++ /dev/null @@ -1,571 +0,0 @@ -// Package btree provide a disk-based btree -package btree - -import ( - "io" - "math/rand" - "os" - "unsafe" - - "github.com/pkg/errors" -) - -const ( - hashSize = 16 // md5 hash - tableSize = (1024 - 1) / int(unsafe.Sizeof(item{})) - cacheSlots = 11 // prime - superSize = int(unsafe.Sizeof(super{})) - tableStructSize = int(unsafe.Sizeof(table{})) -) - -type fileLock interface { - release() error -} - -type item struct { - hash [hashSize]byte - offset int64 - child int64 -} - -type table struct { - items [tableSize]item - size int -} - -type cache struct { - table *table - offset int64 -} - -type super struct { - top int64 - freeTop int64 - alloc int64 -} - -// DB ... -type DB struct { - fd *os.File - top int64 - freeTop int64 - alloc int64 - cache [cacheSlots]cache - - flock fileLock - inAllocator bool - deleteLarger bool - fqueue [freeQueueLen]chunk - fqueueLen int -} - -func (d *DB) get(offset int64) *table { - assert(offset != 0) - - // take from cache - slot := &d.cache[offset%cacheSlots] - if slot.offset == offset { - return slot.table - } - - table := new(table) - - d.fd.Seek(offset, io.SeekStart) - err := readTable(d.fd, table) - if err != nil { - panic(errors.Wrap(err, "btree I/O error")) - } - return table -} - -func (d *DB) put(t *table, offset int64) { - assert(offset != 0) - - // overwrite cache - slot := &d.cache[offset%cacheSlots] - slot.table = t - slot.offset = offset -} - -func (d *DB) flush(t *table, offset int64) { - assert(offset != 0) - - d.fd.Seek(offset, io.SeekStart) - err := writeTable(d.fd, t) - if err != nil { - panic(errors.Wrap(err, "btree I/O error")) - } - d.put(t, offset) -} - -func (d *DB) flushSuper() { - d.fd.Seek(0, io.SeekStart) - super := super{ - top: d.top, - freeTop: d.freeTop, - alloc: d.alloc, - } - err := writeSuper(d.fd, &super) - if err != nil { - panic(errors.Wrap(err, "btree I/O error")) - } -} - -// Open opens an existed btree file -func Open(name string) (*DB, error) { - lock, err := newFileLock(name + ".lock") - if err != nil { - return nil, errors.New("文件被其他进程占用") - } - btree := new(DB) - fd, err := os.OpenFile(name, os.O_RDWR, 0o644) - if err != nil { - return nil, errors.Wrap(err, "btree open file failed") - } - btree.fd = fd - - super := super{} - err = readSuper(fd, &super) - btree.top = super.top - btree.freeTop = super.freeTop - btree.alloc = super.alloc - btree.flock = lock - return btree, errors.Wrap(err, "btree read meta info failed") -} - -// Create creates a database -func Create(name string) (*DB, error) { - lock, err := newFileLock(name + ".lock") - if err != nil { - return nil, errors.New("文件被其他进程占用") - } - btree := new(DB) - fd, err := os.OpenFile(name, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0o644) - if err != nil { - return nil, errors.Wrap(err, "btree open file failed") - } - - btree.flock = lock - btree.fd = fd - btree.alloc = int64(superSize) - btree.flushSuper() - return btree, nil -} - -// Close closes the database -func (d *DB) Close() error { - _ = d.fd.Sync() - if err := d.flock.release(); err != nil { - return err - } - err := d.fd.Close() - for i := 0; i < cacheSlots; i++ { - d.cache[i] = cache{} - } - return errors.Wrap(err, "btree close failed") -} - -func collapse(bt *DB, offset int64) int64 { - table := bt.get(offset) - if table.size != 0 { - /* unable to collapse */ - bt.put(table, offset) - return offset - } - ret := table.items[0].child - bt.put(table, offset) - - /* - * WARNING: this is dangerous as the chunk is added to allocation tree - * before the references to it are removed! - */ - bt.freeChunk(offset, int(unsafe.Sizeof(table))) - return ret -} - -// split a table. The pivot item is stored to 'hash' and 'offset'. -// Returns offset to the new table. -func (d *DB) split(t *table, hash *byte, offset *int64) int64 { - copyhash(hash, &t.items[tableSize/2].hash[0]) - *offset = t.items[tableSize/2].offset - - ntable := new(table) - ntable.size = t.size - tableSize/2 - 1 - - t.size = tableSize / 2 - - copy(ntable.items[:ntable.size+1], t.items[tableSize/2+1:]) - - noff := d.allocChunk(tableStructSize) - d.flush(ntable, noff) - - // make sure data is written before a reference is added to it - _ = d.fd.Sync() - return noff -} - -// takeSmallest find and remove the smallest item from the given table. The key of the item -// is stored to 'hash'. Returns offset to the item -func (d *DB) takeSmallest(toff int64, sha1 *byte) int64 { - table := d.get(toff) - assert(table.size > 0) - - var off int64 - child := table.items[0].child - if child == 0 { - off = d.remove(table, 0, sha1) - } else { - /* recursion */ - off = d.takeSmallest(child, sha1) - table.items[0].child = collapse(d, child) - } - d.flush(table, toff) - - // make sure data is written before a reference is added to it - _ = d.fd.Sync() - return off -} - -// takeLargest find and remove the largest item from the given table. The key of the item -// is stored to 'hash'. Returns offset to the item -func (d *DB) takeLargest(toff int64, hash *byte) int64 { - table := d.get(toff) - assert(table.size > 0) - - var off int64 - child := table.items[table.size].child - if child == 0 { - off = d.remove(table, table.size-1, hash) - } else { - /* recursion */ - off = d.takeLargest(child, hash) - table.items[table.size].child = collapse(d, child) - } - d.flush(table, toff) - - // make sure data is written before a reference is added to it - _ = d.fd.Sync() - return off -} - -// remove an item in position 'i' from the given table. The key of the -// removed item is stored to 'hash'. Returns offset to the item. -func (d *DB) remove(t *table, i int, hash *byte) int64 { - assert(i < t.size) - - if hash != nil { - copyhash(hash, &t.items[i].hash[0]) - } - - offset := t.items[i].offset - lc := t.items[i].child - rc := t.items[i+1].child - - if lc != 0 && rc != 0 { - /* replace the removed item by taking an item from one of the - child tables */ - var noff int64 - if rand.Int()&1 != 0 { - noff = d.takeLargest(lc, &t.items[i].hash[0]) - t.items[i].child = collapse(d, lc) - } else { - noff = d.takeSmallest(rc, &t.items[i].hash[0]) - t.items[i+1].child = collapse(d, rc) - } - t.items[i].child = noff - } else { - // memmove(&table->items[i], &table->items[i + 1], - // (table->size - i) * sizeof(struct btree_item)); - copy(t.items[i:], t.items[i+1:]) - t.size-- - - if lc != 0 { - t.items[i].child = lc - } else { - t.items[i].child = rc - } - } - return offset -} - -func (d *DB) insert(toff int64, hash *byte, data []byte, size int) int64 { - table := d.get(toff) - assert(table.size < tableSize-1) - - left, right := 0, table.size - for left < right { - mid := (right-left)>>1 + left - switch cmp := cmp(hash, &table.items[mid].hash[0]); { - case cmp == 0: - // already in the table - ret := table.items[mid].offset - d.put(table, toff) - return ret - case cmp < 0: - right = mid - default: - left = mid + 1 - } - } - i := left - - var off, rc, ret int64 - lc := table.items[i].child - if lc != 0 { - /* recursion */ - ret = d.insert(lc, hash, data, size) - - /* check if we need to split */ - child := d.get(lc) - if child.size < tableSize-1 { - /* nothing to do */ - d.put(table, toff) - d.put(child, lc) - return ret - } - /* overwrites SHA-1 */ - rc = d.split(child, hash, &off) - /* flush just in case changes happened */ - d.flush(child, lc) - - // make sure data is written before a reference is added to it - _ = d.fd.Sync() - } else { - off = d.insertData(data, size) - ret = off - } - - table.size++ - // memmove(&table->items[i + 1], &table->items[i], - // (table->size - i) * sizeof(struct btree_item)); - copy(table.items[i+1:], table.items[i:]) - copyhash(&table.items[i].hash[0], hash) - table.items[i].offset = off - table.items[i].child = lc - table.items[i+1].child = rc - - d.flush(table, toff) - return ret -} - -func (d *DB) insertData(data []byte, size int) int64 { - if data == nil { - return int64(size) - } - assert(len(data) == size) - - offset := d.allocChunk(4 + len(data)) - - d.fd.Seek(offset, io.SeekStart) - err := write32(d.fd, int32(len(data))) - if err != nil { - panic(errors.Wrap(err, "btree I/O error")) - } - _, err = d.fd.Write(data) - if err != nil { - panic(errors.Wrap(err, "btree I/O error")) - } - - // make sure data is written before a reference is added to it - _ = d.fd.Sync() - return offset -} - -// delete remove an item with key 'hash' from the given table. The offset to the -// removed item is returned. -// Please note that 'hash' is overwritten when called inside the allocator. -func (d *DB) delete(offset int64, hash *byte) int64 { - if offset == 0 { - return 0 - } - table := d.get(offset) - - left, right := 0, table.size - for left < right { - i := (right-left)>>1 + left - switch cmp := cmp(hash, &table.items[i].hash[0]); { - case cmp == 0: - // found - ret := d.remove(table, i, hash) - d.flush(table, offset) - return ret - case cmp < 0: - right = i - default: - left = i + 1 - } - } - - // not found - recursion - i := left - child := table.items[i].child - ret := d.delete(child, hash) - if ret != 0 { - table.items[i].child = collapse(d, child) - } - - if ret == 0 && d.deleteLarger && i < table.size { - ret = d.remove(table, i, hash) - } - if ret != 0 { - /* flush just in case changes happened */ - d.flush(table, offset) - } else { - d.put(table, offset) - } - return ret -} - -func (d *DB) insertTopLevel(toff *int64, hash *byte, data []byte, size int) int64 { // nolint:unparam - var off, ret, rc int64 - if *toff != 0 { - ret = d.insert(*toff, hash, data, size) - - /* check if we need to split */ - table := d.get(*toff) - if table.size < tableSize-1 { - /* nothing to do */ - d.put(table, *toff) - return ret - } - rc = d.split(table, hash, &off) - d.flush(table, *toff) - } else { - off = d.insertData(data, size) - ret = off - } - - /* create new top level table */ - t := new(table) - t.size = 1 - copyhash(&t.items[0].hash[0], hash) - t.items[0].offset = off - t.items[0].child = *toff - t.items[1].child = rc - - ntoff := d.allocChunk(tableStructSize) - d.flush(t, ntoff) - - *toff = ntoff - - // make sure data is written before a reference is added to it - _ = d.fd.Sync() - return ret -} - -func (d *DB) lookup(toff int64, hash *byte) int64 { - if toff == 0 { - return 0 - } - table := d.get(toff) - - left, right := 0, table.size - for left < right { - mid := (right-left)>>1 + left - switch cmp := cmp(hash, &table.items[mid].hash[0]); { - case cmp == 0: - // found - ret := table.items[mid].offset - d.put(table, toff) - return ret - case cmp < 0: - right = mid - default: - left = mid + 1 - } - } - - i := left - child := table.items[i].child - d.put(table, toff) - return d.lookup(child, hash) -} - -// Insert a new item with key 'hash' with the contents in 'data' to the -// database file. -func (d *DB) Insert(chash *byte, data []byte) { - /* SHA-1 must be in writable memory */ - var hash [hashSize]byte - copyhash(&hash[0], chash) - - _ = d.insertTopLevel(&d.top, &hash[0], data, len(data)) - freeQueued(d) - d.flushSuper() -} - -func (d *DB) readValue(off int64) []byte { - d.fd.Seek(off, io.SeekStart) - length, err := read32(d.fd) - if err != nil { - return nil - } - data := make([]byte, length) - n, err := io.ReadFull(d.fd, data) - if err != nil { - return nil - } - return data[:n] -} - -// Get look up item with the given key 'hash' in the database file. Length of the -// item is stored in 'len'. Returns a pointer to the contents of the item. -// The returned pointer should be released with free() after use. -func (d *DB) Get(hash *byte) []byte { - off := d.lookup(d.top, hash) - if off == 0 { - return nil - } - return d.readValue(off) -} - -// Delete remove item with the given key 'hash' from the database file. -func (d *DB) Delete(hash *byte) error { - var h [hashSize]byte - copyhash(&h[0], hash) - - off := d.delete(d.top, &h[0]) - if off == 0 { - return nil // not found key - } - - d.top = collapse(d, d.top) - freeQueued(d) - d.flushSuper() - - d.fd.Seek(off, io.SeekStart) - length, err := read32(d.fd) // len: 0 - if err != nil { - return errors.Wrap(err, "btree I/O error") - } - - d.freeChunk(off, int(length+4)) - freeQueued(d) - d.flushSuper() - return nil -} - -// Foreach iterates over all items in the database file. -func (d *DB) Foreach(iter func(key [16]byte, value []byte)) { - if d.top != 0 { - top := d.get(d.top) - d.iterate(top, iter) - } -} - -func (d *DB) iterate(table *table, iter func(key [16]byte, value []byte)) { - for i := 0; i < table.size; i++ { - item := table.items[i] - offset := item.offset - iter(item.hash, d.readValue(offset)) - - if item.child != 0 { - child := d.get(item.child) - d.iterate(child, iter) - } - } - item := table.items[table.size] - if item.child != 0 { - child := d.get(item.child) - d.iterate(child, iter) - } -} diff --git a/internal/btree/btree_test.go b/internal/btree/btree_test.go deleted file mode 100644 index 649a36cbc..000000000 --- a/internal/btree/btree_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package btree - -import ( - "crypto/sha1" - "os" - "testing" - - "github.com/Mrs4s/MiraiGo/utils" - assert2 "github.com/stretchr/testify/assert" -) - -func tempfile(t *testing.T) string { - temp, err := os.CreateTemp(".", "temp.*.db") - assert2.NoError(t, temp.Close()) - assert2.NoError(t, err) - return temp.Name() -} - -func removedb(name string) { - os.Remove(name) - os.Remove(name + ".lock") -} - -func TestCreate(t *testing.T) { - f := tempfile(t) - _, err := Create(f) - assert2.NoError(t, err) - defer removedb(f) -} - -func TestBtree(t *testing.T) { - f := tempfile(t) - defer removedb(f) - bt, err := Create(f) - assert := assert2.New(t) - assert.NoError(err) - - tests := []string{ - "hello world", - "123", - "We are met on a great battle-field of that war.", - "Abraham Lincoln, November 19, 1863, Gettysburg, Pennsylvania", - // "00", // TODO: fix this - } - sha := make([]*byte, len(tests)) - for i, tt := range tests { - hash := sha1.New() - hash.Write([]byte(tt)) - sha[i] = &hash.Sum(nil)[0] - bt.Insert(sha[i], []byte(tt)) - } - assert.NoError(bt.Close()) - - bt, err = Open(f) - assert.NoError(err) - var ss []string - bt.Foreach(func(key [16]byte, value []byte) { - ss = append(ss, string(value)) - }) - assert.ElementsMatch(tests, ss) - - for i, tt := range tests { - assert.Equal([]byte(tt), bt.Get(sha[i])) - } - - for i := range tests { - assert.NoError(bt.Delete(sha[i])) - } - - for i := range tests { - assert.Equal([]byte(nil), bt.Get(sha[i])) - } - assert.NoError(bt.Close()) -} - -func testForeach(t *testing.T, elemSize int) { - expected := make([]string, elemSize) - for i := 0; i < elemSize; i++ { - expected[i] = utils.RandomString(20) - } - f := tempfile(t) - defer removedb(f) - bt, err := Create(f) - assert2.NoError(t, err) - for _, v := range expected { - hash := sha1.New() - hash.Write([]byte(v)) - bt.Insert(&hash.Sum(nil)[0], []byte(v)) - } - var got []string - bt.Foreach(func(key [16]byte, value []byte) { - got = append(got, string(value)) - }) - assert2.ElementsMatch(t, expected, got) - assert2.NoError(t, bt.Close()) -} - -func TestDB_Foreach(t *testing.T) { - elemSizes := []int{0, 5, 100, 200} - for _, size := range elemSizes { - testForeach(t, size) - } -} diff --git a/internal/btree/chunk.go b/internal/btree/chunk.go deleted file mode 100644 index 524fc1d6a..000000000 --- a/internal/btree/chunk.go +++ /dev/null @@ -1,116 +0,0 @@ -package btree - -import ( - "encoding/binary" - "math/rand" - "unsafe" -) - -type chunk struct { - offset int64 - len int -} - -const freeQueueLen = 64 - -func freeQueued(bt *DB) { - for i := 0; i < bt.fqueueLen; i++ { - chunk := &bt.fqueue[i] - bt.freeChunk(chunk.offset, chunk.len) - } - bt.fqueueLen = 0 -} - -func (d *DB) allocChunk(size int) int64 { - assert(size > 0) - - size = power2(size) - - var offset int64 - if d.inAllocator { - const i32s = unsafe.Sizeof(int32(0)) - - /* create fake size SHA-1 */ - var sha1 [hashSize]byte - binary.LittleEndian.PutUint32(sha1[0*4:1*4], ^uint32(0)) // *(uint32_t *) hash = -1; - binary.LittleEndian.PutUint32(sha1[1*4:2*4], uint32(size)) // ((__be32 *) hash)[1] = to_be32(size); - - /* find free chunk with the larger or the same size/SHA-1 */ - d.inAllocator = true - d.deleteLarger = true - offset = d.delete(d.freeTop, &sha1[0]) - d.deleteLarger = false - if offset != 0 { - flen := int(binary.LittleEndian.Uint32(sha1[:4])) // size_t free_len = from_be32(((__be32 *) hash)[1]) - assert(power2(flen) == flen) - assert(flen >= size) - - /* delete buddy information */ - sha1 = [hashSize]byte{} - binary.LittleEndian.PutUint64(sha1[0*8:1*8], uint64(offset)) - buddyLen := d.delete(d.freeTop, &sha1[0]) - assert(buddyLen == int64(size)) - - d.freeTop = collapse(d, d.freeTop) - - d.inAllocator = false - - /* free extra space at the end of the chunk */ - for flen > size { - flen >>= 1 - d.freeChunk(offset+int64(flen), flen) - } - } else { - d.inAllocator = false - } - } - if offset == 0 { - /* not found, allocate from the end of the file */ - offset = d.alloc - /* TODO: this wastes memory.. */ - if offset&int64(size-1) != 0 { - offset += int64(size) - (offset & (int64(size) - 1)) - } - d.alloc = offset + int64(size) - } - d.flushSuper() - - // make sure the allocation tree is up-to-date before using the chunk - _ = d.fd.Sync() - return offset -} - -/* Mark a chunk as unused in the database file */ -func (d *DB) freeChunk(offset int64, size int) { - assert(size > 0) - assert(offset != 0) - size = power2(size) - assert(offset&int64(size-1) == 0) - - if d.inAllocator { - chunk := &d.fqueue[d.fqueueLen] - d.fqueueLen++ - chunk.offset = offset - chunk.len = size - return - } - - /* create fake offset SHA-1 for buddy allocation */ - var sha1 [hashSize]byte - d.inAllocator = true - /* add buddy information */ - binary.LittleEndian.PutUint32(sha1[0*4:1*4], ^uint32(0)) // *(uint32_t *) hash = -1; - binary.LittleEndian.PutUint32(sha1[1*4:2*4], uint32(size)) // ((__be32 *) hash)[1] = to_be32(size); - binary.LittleEndian.PutUint32(sha1[2*4:3*4], rand.Uint32()) /* to make SHA-1 unique */ - binary.LittleEndian.PutUint32(sha1[3*4:4*4], rand.Uint32()) - - // insert_toplevel(btree, &btree->free_top, hash, NULL, offset); - _ = d.insertTopLevel(&d.freeTop, &sha1[0], nil, int(offset)) - d.inAllocator = false - - d.flushSuper() - - // make sure the allocation tree is up-to-date before removing - // references to the chunk - _ = d.fd.Sync() -} diff --git a/internal/btree/file_lock_unix.go b/internal/btree/file_lock_unix.go deleted file mode 100644 index 897428437..000000000 --- a/internal/btree/file_lock_unix.go +++ /dev/null @@ -1,45 +0,0 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd - -package btree - -import ( - "os" - "syscall" -) - -type unixFileLock struct { - f *os.File -} - -func (fl *unixFileLock) release() error { - if err := setFileLock(fl.f, false); err != nil { - return err - } - return fl.f.Close() -} - -func newFileLock(path string) (fl fileLock, err error) { - flag := os.O_RDWR - f, err := os.OpenFile(path, flag, 0) - if os.IsNotExist(err) { - f, err = os.OpenFile(path, flag|os.O_CREATE, 0644) - } - if err != nil { - return - } - err = setFileLock(f, true) - if err != nil { - f.Close() - return - } - fl = &unixFileLock{f: f} - return -} - -func setFileLock(f *os.File, lock bool) error { - how := syscall.LOCK_UN - if lock { - how = syscall.LOCK_EX - } - return syscall.Flock(int(f.Fd()), how|syscall.LOCK_NB) -} diff --git a/internal/btree/file_lock_windows.go b/internal/btree/file_lock_windows.go deleted file mode 100644 index becdfaded..000000000 --- a/internal/btree/file_lock_windows.go +++ /dev/null @@ -1,28 +0,0 @@ -package btree - -import "syscall" - -type windowsFileLock struct { - fd syscall.Handle -} - -func (fl *windowsFileLock) release() error { - return syscall.Close(fl.fd) -} - -func newFileLock(path string) (fileLock, error) { - pathp, err := syscall.UTF16PtrFromString(path) - if err != nil { - return nil, err - } - - const access uint32 = syscall.GENERIC_READ | syscall.GENERIC_WRITE - fd, err := syscall.CreateFile(pathp, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0) - if err == syscall.ERROR_FILE_NOT_FOUND { - fd, err = syscall.CreateFile(pathp, access, 0, nil, syscall.OPEN_ALWAYS, syscall.FILE_ATTRIBUTE_NORMAL, 0) - } - if err != nil { - return nil, err - } - return &windowsFileLock{fd: fd}, nil -} diff --git a/internal/btree/helper.go b/internal/btree/helper.go deleted file mode 100644 index 762ae7bb5..000000000 --- a/internal/btree/helper.go +++ /dev/null @@ -1,101 +0,0 @@ -package btree - -import ( - "io" - "reflect" - "unsafe" -) - -func assert(cond bool) { - if !cond { - panic("assert failed!") - } -} - -// power2 returns a value that is greater or equal to 'val' and is power-of-two. -func power2(val int) int { - i := 1 - for i < val { - i <<= 1 - } - return i -} - -// helpers for hash - -func cmp(a, b *byte) int64 { - pa, pb := unsafe.Pointer(a), unsafe.Pointer(b) - if *(*uint64)(pa) != *(*uint64)(pb) { - return int64(*(*uint64)(pa) - *(*uint64)(pb)) - } - pa, pb = unsafe.Add(pa, 8), unsafe.Add(pb, 8) - return int64(*(*uint64)(pa) - *(*uint64)(pb)) -} - -func copyhash(dst *byte, src *byte) { - pa, pb := unsafe.Pointer(dst), unsafe.Pointer(src) - *(*[hashSize]byte)(pa) = *(*[hashSize]byte)(pb) -} - -// reading table - -func read32(r io.Reader) (int32, error) { - b := make([]byte, 4) - _, err := r.Read(b) - if err != nil { - return 0, err - } - return *(*int32)(unsafe.Pointer(&b[0])), nil -} - -func readTable(r io.Reader, t *table) error { - buf := make([]byte, tableStructSize) - _, err := r.Read(buf) - if err != nil { - return err - } - *t = *(*table)(unsafe.Pointer(&buf[0])) - return nil -} - -func readSuper(r io.Reader, s *super) error { - buf := make([]byte, superSize) - _, err := r.Read(buf) - if err != nil { - return err - } - *s = *(*super)(unsafe.Pointer(&buf[0])) - return nil -} - -// write table - -func write32(w io.Writer, t int32) error { - var p []byte - ph := (*reflect.SliceHeader)(unsafe.Pointer(&p)) - ph.Data = uintptr(unsafe.Pointer(&t)) - ph.Len = 4 - ph.Cap = 4 - _, err := w.Write(p) - return err -} - -func writeTable(w io.Writer, t *table) error { - var p []byte - ph := (*reflect.SliceHeader)(unsafe.Pointer(&p)) - ph.Data = uintptr(unsafe.Pointer(t)) - ph.Len = tableStructSize - ph.Cap = tableStructSize - _, err := w.Write(p) - return err -} - -func writeSuper(w io.Writer, s *super) error { - var p []byte - ph := (*reflect.SliceHeader)(unsafe.Pointer(&p)) - ph.Data = uintptr(unsafe.Pointer(s)) - ph.Len = superSize - ph.Cap = superSize - _, err := w.Write(p) - return err -} From 140192c76a625a98258388375ef6c97e9467cefb Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 8 Sep 2022 18:43:34 +0800 Subject: [PATCH 440/612] fix: panic of getting group code error in forward message --- coolq/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 81f7dedfe..b3900d512 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -836,7 +836,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType if len(bot.Client.GroupList) == 0 { groupID = 1 } else { - groupID = bot.Client.GroupList[1].Uin + groupID = bot.Client.GroupList[0].Uin } } builder := bot.Client.NewForwardMessageBuilder(groupID) From 0377e7f80328b8d1a51119d06c1823f22e87f32e Mon Sep 17 00:00:00 2001 From: sclock <1342810270@qq.com> Date: Thu, 8 Sep 2022 19:27:13 +0800 Subject: [PATCH 441/612] =?UTF-8?q?=E5=90=88=E5=B9=B6=E8=BD=AC=E5=8F=91?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0log=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/api.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index b3900d512..1784c7daf 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -977,8 +977,10 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa log.Warnf("合并转发(群)消息发送失败: 账号可能被风控.") return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") } + mid := bot.InsertGroupMessage(ret) + log.Infof("发送群 %v(%v) 的合并转发消息: %v (%v)", groupID, groupID, limitedString(m.String()), mid) return OK(global.MSG{ - "message_id": bot.InsertGroupMessage(ret), + "message_id": mid, }) } @@ -1000,6 +1002,7 @@ func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) glob log.Warnf("合并转发(好友)消息发送失败: 账号可能被风控.") return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") } + log.Infof("发送好友 %v(%v) 的合并转发消息: %v (%v)", userID, userID, limitedString(m.String()), mid) return OK(global.MSG{"message_id": mid}) } From bae00b557e91d6997d1de7a56ff4db7500e7bdb9 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 8 Sep 2022 22:20:43 +0800 Subject: [PATCH 442/612] feat: send_forward_msg api will be returned forward id --- coolq/api.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index 1784c7daf..526389cb0 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -981,6 +981,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa log.Infof("发送群 %v(%v) 的合并转发消息: %v (%v)", groupID, groupID, limitedString(m.String()), mid) return OK(global.MSG{ "message_id": mid, + "forward_id": fe.ResId, }) } @@ -1003,7 +1004,10 @@ func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) glob return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") } log.Infof("发送好友 %v(%v) 的合并转发消息: %v (%v)", userID, userID, limitedString(m.String()), mid) - return OK(global.MSG{"message_id": mid}) + return OK(global.MSG{ + "message_id": mid, + "forward_id": fe.ResId, + }) } // CQSendPrivateMessage 发送私聊消息 From 97662e467b3275f0fd195d02c6b83e197ca031e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Fri, 9 Sep 2022 00:39:20 +0800 Subject: [PATCH 443/612] =?UTF-8?q?feat:=20=E6=A0=B9=E6=8D=AE=E5=AE=9E?= =?UTF-8?q?=E9=99=85=E7=BB=8F=E9=AA=8C=EF=BC=8C=E8=B0=83=E6=95=B4=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E9=87=8D=E8=AF=95=E5=88=9D=E5=A7=8B=E5=80=BC=E5=92=8C?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E5=80=BC=E4=B8=BA1=E5=B0=8F=E6=97=B6?= =?UTF-8?q?=E5=92=8C12=E5=B0=8F=E6=97=B6=EF=BC=8C=E4=BD=BF=E5=85=B6?= =?UTF-8?q?=E6=9B=B4=E5=8A=A0=E6=9C=89=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 254f70906..c8dce41ef 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -91,8 +91,8 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete groupsToUpload := groups failIndex := 1 maxFailTimes := 10 - retryWaitTime := time.Minute - maxRetryWaitTime := time.Hour + retryWaitTime := time.Hour + maxRetryWaitTime := 12 * time.Hour for { // 尝试上传新版本 for _, groupID := range groupsToUpload { From bc9c6c49f191a99c0a08b45d524ee7cce33cd906 Mon Sep 17 00:00:00 2001 From: Akiba Date: Fri, 9 Sep 2022 22:44:43 +0800 Subject: [PATCH 444/612] fix: private reply id error, fix #1608 --- coolq/cqcode.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 1f1475b66..571b9b128 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -84,14 +84,14 @@ func (e *PokeElement) Type() message.ElementType { func replyID(r *message.ReplyElement, source message.Source) int32 { id := source.PrimaryID seq := r.ReplySeq - if source.SourceType == message.SourcePrivate { + if r.GroupID != 0 { + id = r.GroupID + } + if source.SourceType == message.SourcePrivate && r.Sender == source.PrimaryID { // 私聊似乎腾讯服务器有bug? seq = int32(uint16(seq)) id = r.Sender } - if r.GroupID != 0 { - id = r.GroupID - } return db.ToGlobalID(id, seq) } From 532f55fba08fccda40d73bf1494e41fc18052d5e Mon Sep 17 00:00:00 2001 From: Madray Haven Date: Sun, 11 Sep 2022 19:58:36 +0800 Subject: [PATCH 445/612] fix: More compatible behavior for getting the current executable dir --- internal/base/flag.go | 3 ++- server/daemon.go | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/base/flag.go b/internal/base/flag.go index 5e9bd7bbf..24ad3610e 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -140,7 +140,8 @@ func ResetWorkingDir() { args = append(args, os.Args[i]) } } - p, _ := filepath.Abs(os.Args[0]) + ex, _ := os.Executable() + p, _ := filepath.Abs(ex) _, err := os.Stat(p) if !(err == nil || errors.Is(err, os.ErrExist)) { log.Fatalf("重置工作目录时出现错误: 无法找到路径 %v", p) diff --git a/server/daemon.go b/server/daemon.go index 0a570b623..cb0671ca3 100644 --- a/server/daemon.go +++ b/server/daemon.go @@ -5,6 +5,7 @@ package server import ( "os" "os/exec" + "path/filepath" "strconv" "strings" @@ -28,7 +29,9 @@ func Daemon() { execArgs = append(execArgs, args[i]) } - proc := exec.Command(os.Args[0], execArgs...) + ex, _ := os.Executable() + p, _ := filepath.Abs(ex) + proc := exec.Command(p, execArgs...) err := proc.Start() if err != nil { panic(err) From 4ff61215c683ed75d7d991b84f9d21034b172ebe Mon Sep 17 00:00:00 2001 From: Madray Haven Date: Sun, 11 Sep 2022 19:59:02 +0800 Subject: [PATCH 446/612] fix: wrong pid output when `-w` use with `-d` --- cmd/gocq/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index dacaead6c..442f73eb6 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -55,10 +55,10 @@ func Main() { switch { case base.LittleH: base.Help() - case base.LittleD: - server.Daemon() case base.LittleWD != "": base.ResetWorkingDir() + case base.LittleD: + server.Daemon() } base.Init() From 069a764a49911fa2a9a036670b804286cd14238e Mon Sep 17 00:00:00 2001 From: Akiba Date: Fri, 16 Sep 2022 09:58:17 +0800 Subject: [PATCH 447/612] fix: #1608 again --- coolq/cqcode.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 571b9b128..4a3a4f3f7 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -87,7 +87,9 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { if r.GroupID != 0 { id = r.GroupID } - if source.SourceType == message.SourcePrivate && r.Sender == source.PrimaryID { + // 私聊时,部分(不确定)的账号会在 ReplyElement 中带有 GroupID 字段。 + // 这里需要判断是由于 “直接回复” 功能,GroupID 为触发直接回复的来源那个群。 + if source.SourceType == message.SourcePrivate && (r.Sender == source.PrimaryID || r.GroupID == source.PrimaryID) { // 私聊似乎腾讯服务器有bug? seq = int32(uint16(seq)) id = r.Sender From ec4b3cc3dba157bef57b7ca4f23f3a642a1b31af Mon Sep 17 00:00:00 2001 From: Ink33 Date: Sun, 18 Sep 2022 15:30:34 +0800 Subject: [PATCH 448/612] feat: msgbox support high dpi Signed-off-by: Ink33 --- global/terminal/double_click_windows.go | 35 ++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/global/terminal/double_click_windows.go b/global/terminal/double_click_windows.go index 1fa746cf0..4c3f4e828 100644 --- a/global/terminal/double_click_windows.go +++ b/global/terminal/double_click_windows.go @@ -6,15 +6,16 @@ package terminal import ( "os" "path/filepath" - "syscall" "unsafe" + "golang.org/x/sys/windows" + "github.com/pkg/errors" ) // RunningByDoubleClick 检查是否通过双击直接运行 func RunningByDoubleClick() bool { - kernel32 := syscall.NewLazyDLL("kernel32.dll") + kernel32 := windows.NewLazySystemDLL("kernel32.dll") lp := kernel32.NewProc("GetConsoleProcessList") if lp != nil { var ids [2]uint32 @@ -29,7 +30,8 @@ func RunningByDoubleClick() bool { // NoMoreDoubleClick 提示用户不要双击运行,并生成安全启动脚本 func NoMoreDoubleClick() error { - r := boxW(0, "请勿通过双击直接运行本程序, 这将导致一些非预料的后果.\n请在shell中运行./go-cqhttp.exe\n点击确认将释出安全启动脚本,点击取消则关闭程序", "警告", 0x00000030|0x00000001) + toHighDPI() + r := boxW(getConsoleWindows(), "请勿通过双击直接运行本程序, 这将导致一些非预料的后果.\n请在shell中运行./go-cqhttp.exe\n点击确认将释出安全启动脚本,点击取消则关闭程序", "警告", 0x00000030|0x00000001) if r == 2 { return nil } @@ -59,9 +61,10 @@ func NoMoreDoubleClick() error { // BoxW of Win32 API. Check https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxw for more detail. func boxW(hwnd uintptr, caption, title string, flags uint) int { - captionPtr, _ := syscall.UTF16PtrFromString(caption) - titlePtr, _ := syscall.UTF16PtrFromString(title) - ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call( + captionPtr, _ := windows.UTF16PtrFromString(caption) + titlePtr, _ := windows.UTF16PtrFromString(title) + u32 := windows.NewLazySystemDLL("user32.dll") + ret, _, _ := u32.NewProc("MessageBoxW").Call( hwnd, uintptr(unsafe.Pointer(captionPtr)), uintptr(unsafe.Pointer(titlePtr)), @@ -69,3 +72,23 @@ func boxW(hwnd uintptr, caption, title string, flags uint) int { return int(ret) } + +// GetConsoleWindows retrieves the window handle used by the console associated with the calling process. +func getConsoleWindows() (hWnd uintptr) { + hWnd, _, _ = windows.NewLazySystemDLL("kernel32.dll").NewProc("GetConsoleWindow").Call() + return +} + +// toHighDPI tries to raise DPI awareness context to DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED +func toHighDPI() { + systemAware := ^uintptr(2) + 1 + unawareGDIScaled := ^uintptr(5) + 1 + u32 := windows.NewLazySystemDLL("user32.dll") + proc := u32.NewProc("SetThreadDpiAwarenessContext") + if proc.Find() != nil { + return + } + for i := unawareGDIScaled; i <= systemAware; i++ { + _, _, _ = u32.NewProc("SetThreadDpiAwarenessContext").Call(i) + } +} From 1dd12df26ac8824fbc18006972273267dc0e0b72 Mon Sep 17 00:00:00 2001 From: CXM <16154023+littlecxm@users.noreply.github.com> Date: Mon, 19 Sep 2022 12:28:06 +0800 Subject: [PATCH 449/612] fix: misc fix (#1699) * chore: gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 43a051cdf..9e66494d5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,7 @@ device.json data/ logs/ internal/btree/*.lock -internal/btree/*.db \ No newline at end of file +internal/btree/*.db + +# binary builds +go-cqhttp From def813d5c9204dc26615e33dfbc138ee865863ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 27 Sep 2022 16:08:27 +0800 Subject: [PATCH 450/612] =?UTF-8?q?feat:=20qq=E9=9F=B3=E4=B9=90=E6=94=B9?= =?UTF-8?q?=E7=94=A8=E6=99=BA=E8=83=BD=E6=90=9C=E7=B4=A2=E6=A1=86=E7=9A=84?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=9D=A5=E8=8E=B7=E5=8F=96=E6=AD=8C=E6=9B=B2?= =?UTF-8?q?id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/music.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/qqrobot/music.go b/qqrobot/music.go index cc9019ff5..89862e226 100644 --- a/qqrobot/music.go +++ b/qqrobot/music.go @@ -1,9 +1,11 @@ package qqrobot import ( + "fmt" "io" "net/http" "net/url" + "time" "github.com/Mrs4s/MiraiGo/message" "github.com/pkg/errors" @@ -99,19 +101,19 @@ func (r *QQRobot) makeQQMusicShareElement(musicName string) (*message.MusicShare }, nil } +// queryQQMusic qq音乐页面上面的智能搜索框的接口 func (r *QQRobot) queryQQMusic(musicName string) string { // 搜索音乐信息 第一首歌 h1 := http.Header{ - "User-Agent": []string{"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"}, + "User-Agent": []string{"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"}, } - search, _ := url.Parse("https://c.y.qq.com/soso/fcgi-bin/client_search_cp") - search.RawQuery = url.Values{ - "w": []string{musicName}, - }.Encode() - res := netGet(search.String(), h1) - info := gjson.ParseBytes(res[9 : len(res)-1]).Get("data.song.list.0") + search := fmt.Sprintf("https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg?"+ + "key=%v&_=%v&cv=4747474&ct=24&format=json&inCharset=utf-8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=1", + url.QueryEscape(musicName), time.Now().UnixMilli()) + res := netGet(search, h1) + info := gjson.ParseBytes(res).Get("data.song.itemlist.0") - return info.Get("songid").String() + return info.Get("id").String() } // netGet 返回请求数据 From e10fb13c117394deeac54e78680994ce904670db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 27 Sep 2022 16:08:36 +0800 Subject: [PATCH 451/612] =?UTF-8?q?feat:=20=E5=88=87=E6=8D=A2=E4=B8=BAqq?= =?UTF-8?q?=E9=9F=B3=E4=B9=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/command.go b/qqrobot/command.go index 33e85e2fd..84d2a006e 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -102,7 +102,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms // full_match|听歌关键词|musicName musicName := match[2] - musicElem, err := r.makeMusicShareElement(musicName, message.CloudMusic) + musicElem, err := r.makeMusicShareElement(musicName, message.QQMusic) if err != nil { return fmt.Sprintf("没有找到歌曲:%v", musicName), nil, nil } From adb5ab9a1833336ee982acc1507f8ff0bc5e9910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 2 Oct 2022 16:17:11 +0800 Subject: [PATCH 452/612] =?UTF-8?q?feat:=20=E6=89=8B=E5=8A=A8=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E4=B9=9F=E5=A2=9E=E5=8A=A0=E9=87=8D=E8=AF=95=EF=BC=8C?= =?UTF-8?q?=E6=96=B9=E4=BE=BF=E6=89=8B=E5=8A=A8=E6=A8=A1=E6=8B=9F=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=9B=B4=E6=96=B0=E6=97=B6=E7=9A=84=E6=83=85?= =?UTF-8?q?=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 6034afc51..470b2db01 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -598,11 +598,14 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { case actiontypeUpdateNewVersion: if isAdmin { r.cqBot.SendGroupMessage(groupID, message.NewSendingMessage().Append(message.NewText("开始执行上传指令,可能需要一点时间~"))) - r.updateNewVersionInGroup("手动触发更新新版本", - config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath, - false, - ) - replies.Append(message.NewText("上传完毕,具体情况可查看服务器日志")) + + go func() { + r.updateNewVersionInGroup("手动触发更新新版本", + config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath, + true, + ) + r.cqBot.SendGroupMessage(groupID, message.NewSendingMessage().Append(message.NewText("上传完毕,具体情况可查看服务器日志"))) + }() } else { replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) } From c3d7eb683db347e5ba146b17bef94ba69322c149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Sun, 2 Oct 2022 16:21:23 +0800 Subject: [PATCH 453/612] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=97=B6=E7=9A=84=E6=97=A5=E5=BF=97=E8=89=B2=E5=BD=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index c8dce41ef..b7d9031f0 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -74,12 +74,17 @@ func (r *QQRobot) checkUpdates() { } } +func boldYellowLog(format string, args ...interface{}) { + logger.Infof(bold(color.Yellow).Render(fmt.Sprintf(format, args...))) +} + func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interpreter string, script string, needRetry bool) { if interpreter != "" && script != "" && global.PathExists(interpreter) && global.PathExists(script) { - logger.Infof("开始更新新版本到各个群中: %v", groups) + boldYellowLog("开始更新新版本到各个群中: %v", groups) + boldYellowLog("是否重试: %v", needRetry) oldVersionKeywords := "DNF蚊子腿小助手_v" - logger.Infof("开始调用配置的更新命令来获取新版本: %v %v", interpreter, script) + boldYellowLog("开始调用配置的更新命令来获取新版本: %v %v", interpreter, script) newVersionFilePath, err := downloadNewVersionUsingPythonScript(interpreter, script) if err != nil { logger.Warnf("下载新版本失败, err=%v", err) @@ -96,7 +101,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete for { // 尝试上传新版本 for _, groupID := range groupsToUpload { - logger.Infof("开始上传 %v 到 群 %v", uploadFileName, groupID) + boldYellowLog("开始上传 %v 到 群 %v", uploadFileName, groupID) r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords, false) // 广播消息间强行间隔一秒 time.Sleep(time.Second) @@ -110,11 +115,11 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete } } if len(groupsNotUploaded) == 0 { - logger.Infof("全部上传成功,完毕") + boldYellowLog("全部上传成功,完毕") break } - logger.Infof("%v 个群未上传成功: %v", len(groupsNotUploaded), groupsNotUploaded) + boldYellowLog("%v 个群未上传成功: %v", len(groupsNotUploaded), groupsNotUploaded) if !needRetry { logger.Warnf("当前配置为不需要重试") break @@ -125,7 +130,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete break } - logger.Infof("第 %v 次上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, retryWaitTime) + boldYellowLog("第 %v 次上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, retryWaitTime) select { case <-time.After(retryWaitTime): break @@ -141,7 +146,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete } } } else { - logger.Infof("%v: 未配置更新python脚本,或者对应脚本不存在,将不会尝试下载并上传新版本到群文件", ctx) + boldYellowLog("%v: 未配置更新python脚本,或者对应脚本不存在,将不会尝试下载并上传新版本到群文件", ctx) } } From f4117bfb70f98c5709d23317e874e03ac1134852 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 13 Oct 2022 21:27:36 +0800 Subject: [PATCH 454/612] internal/download: move download logic to this package also disable http2 when downloading, may fix some issue For #1563 --- cmd/gocq/login.go | 5 +- coolq/api.go | 12 +- coolq/cqcode.go | 12 +- coolq/event.go | 8 +- global/fs.go | 5 +- global/net.go | 287 +-------------------------------- internal/download/download.go | 295 ++++++++++++++++++++++++++++++++++ internal/selfupdate/update.go | 11 +- 8 files changed, 328 insertions(+), 307 deletions(-) create mode 100644 internal/download/download.go diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index d963d2566..3fc3813b2 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -15,9 +15,9 @@ import ( "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" - "github.com/tidwall/gjson" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/download" ) var console = bufio.NewReader(os.Stdin) @@ -243,12 +243,11 @@ func getTicket(u string) (str string) { } func fetchCaptcha(id string) string { - data, err := global.GetBytes("https://captcha.go-cqhttp.org/captcha/ticket?id=" + id) + g, err := download.Request{URL: "https://captcha.go-cqhttp.org/captcha/ticket?id=" + id}.JSON() if err != nil { log.Warnf("获取 Ticket 时出现错误: %v", err) return "" } - g := gjson.ParseBytes(data) if g.Get("ticket").Exists() { return g.Get("ticket").String() } diff --git a/coolq/api.go b/coolq/api.go index 526389cb0..d7275cec0 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -27,6 +27,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" + "github.com/Mrs4s/go-cqhttp/internal/download" "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/filter" ) @@ -1545,12 +1546,8 @@ func (bot *CQBot) CQGetImage(file string) global.MSG { } local := path.Join(global.CachePath, file+path.Ext(msg["filename"].(string))) if !global.PathExists(local) { - if body, err := global.HTTPGetReadCloser(msg["url"].(string)); err == nil { - f, _ := os.OpenFile(local, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o0644) - _, _ = f.ReadFrom(body) - _ = body.Close() - _ = f.Close() - } else { + r := download.Request{URL: msg["url"].(string)} + if err := r.WriteToFile(local); err != nil { log.Warnf("下载图片 %v 时出现错误: %v", msg["url"], err) return Failed(100, "DOWNLOAD_IMAGE_ERROR", err.Error()) } @@ -1593,7 +1590,8 @@ func (bot *CQBot) CQDownloadFile(url string, headers gjson.Result, threadCount i return Failed(100, "DELETE_FILE_ERROR", err.Error()) } } - if err := global.DownloadFileMultiThreading(url, file, 0, threadCount, h); err != nil { + r := download.Request{URL: url, Header: h} + if err := r.WriteToFileMultiThreading(file, threadCount); err != nil { log.Warnf("下载链接 %v 时出现错误: %v", url, err) return Failed(100, "DOWNLOAD_FILE_ERROR", err.Error()) } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 4a3a4f3f7..c241e3413 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -29,6 +29,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" + "github.com/Mrs4s/go-cqhttp/internal/download" "github.com/Mrs4s/go-cqhttp/internal/mime" "github.com/Mrs4s/go-cqhttp/internal/param" ) @@ -895,9 +896,9 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So name := info.Get("track_info.name").Str mid := info.Get("track_info.mid").Str albumMid := info.Get("track_info.album.mid").Str - pinfo, _ := global.GetBytes("http://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576") + pinfo, _ := download.Request{URL: "http://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576"}.JSON() jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + mid + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare" - purl := gjson.ParseBytes(pinfo).Get("url_mid.data.midurlinfo.0.purl").Str + purl := pinfo.Get("url_mid.data.midurlinfo.0.purl").Str preview := "http://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg" content := info.Get("track_info.singer.0.name").Str if d["content"] != "" { @@ -1089,8 +1090,11 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if exist { _ = os.Remove(cacheFile) } - if err := global.DownloadFileMultiThreading(f, cacheFile, maxSize, thread, nil); err != nil { - return nil, err + { + r := download.Request{URL: f, Limit: maxSize} + if err := r.WriteToFileMultiThreading(cacheFile, thread); err != nil { + return nil, err + } } useCacheFile: if video { diff --git a/coolq/event.go b/coolq/event.go index ebdc960b0..34500e2da 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -4,7 +4,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "os" "path" "strconv" "strings" @@ -18,6 +17,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" + "github.com/Mrs4s/go-cqhttp/internal/download" ) // ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式 @@ -666,7 +666,8 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { filename := hex.EncodeToString(i.Md5) + ".image" cache.Image.Insert(i.Md5, data) if i.Url != "" && !global.PathExists(path.Join(global.ImagePath, "guild-images", filename)) { - if err := global.DownloadFile(i.Url, path.Join(global.ImagePath, "guild-images", filename), -1, nil); err != nil { + r := download.Request{URL: i.Url} + if err := r.WriteToFile(path.Join(global.ImagePath, "guild-images", filename)); err != nil { log.Warnf("下载频道图片时出现错误: %v", err) } } @@ -684,12 +685,11 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { i.Name = strings.ReplaceAll(i.Name, "{", "") i.Name = strings.ReplaceAll(i.Name, "}", "") if !global.PathExists(path.Join(global.VoicePath, i.Name)) { - b, err := global.GetBytes(i.Url) + err := download.Request{URL: i.Url}.WriteToFile(path.Join(global.VoicePath, i.Name)) if err != nil { log.Warnf("语音文件 %v 下载失败: %v", i.Name, err) continue } - _ = os.WriteFile(path.Join(global.VoicePath, i.Name), b, 0o644) } case *message.ShortVideoElement: data := binary.NewWriterF(func(w *binary.Writer) { diff --git a/global/fs.go b/global/fs.go index f985dd5a1..046ac2c50 100644 --- a/global/fs.go +++ b/global/fs.go @@ -16,6 +16,8 @@ import ( b14 "github.com/fumiama/go-base16384" "github.com/segmentio/asm/base64" log "github.com/sirupsen/logrus" + + "github.com/Mrs4s/go-cqhttp/internal/download" ) const ( @@ -82,8 +84,7 @@ func FindFile(file, cache, p string) (data []byte, err error) { if (cache == "" || cache == "1") && PathExists(cacheFile) { return os.ReadFile(cacheFile) } - data, err = GetBytes(file) - _ = os.WriteFile(cacheFile, data, 0o644) + err = download.Request{URL: file}.WriteToFile(cacheFile) if err != nil { return nil, err } diff --git a/global/net.go b/global/net.go index 0327a7e0c..030cbea18 100644 --- a/global/net.go +++ b/global/net.go @@ -1,304 +1,27 @@ package global import ( - "bufio" - "compress/gzip" "fmt" - "io" - "net/http" - "net/url" - "os" - "strconv" - "strings" - "sync" - - "github.com/pkg/errors" "github.com/tidwall/gjson" - "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/internal/download" ) -var ( - client = &http.Client{ - Transport: &http.Transport{ - Proxy: func(request *http.Request) (u *url.URL, e error) { - if base.Proxy == "" { - return http.ProxyFromEnvironment(request) - } - return url.Parse(base.Proxy) - }, - ForceAttemptHTTP2: true, - MaxConnsPerHost: 0, - MaxIdleConns: 0, - MaxIdleConnsPerHost: 999, - }, - } - - // ErrOverSize 响应主体过大时返回此错误 - ErrOverSize = errors.New("oversize") - - // UserAgent HTTP请求时使用的UA - UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" -) - -// GetBytes 对给定URL发送Get请求,返回响应主体 -func GetBytes(url string) ([]byte, error) { - reader, err := HTTPGetReadCloser(url) - if err != nil { - return nil, err - } - defer func() { - _ = reader.Close() - }() - return io.ReadAll(reader) -} - -// DownloadFile 将给定URL对应的文件下载至给定Path -func DownloadFile(url, path string, limit int64, headers map[string]string) error { - file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666) - if err != nil { - return err - } - defer file.Close() - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return err - } - - for k, v := range headers { - req.Header.Set(k, v) - } - - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if limit > 0 && resp.ContentLength > limit { - return ErrOverSize - } - _, err = file.ReadFrom(resp.Body) - if err != nil { - return err - } - return nil -} - -// DownloadFileMultiThreading 使用threadCount个线程将给定URL对应的文件下载至给定Path -func DownloadFileMultiThreading(url, path string, limit int64, threadCount int, headers map[string]string) error { - if threadCount < 2 { - return DownloadFile(url, path, limit, headers) - } - type BlockMetaData struct { - BeginOffset int64 - EndOffset int64 - DownloadedSize int64 - } - var blocks []*BlockMetaData - var contentLength int64 - errUnsupportedMultiThreading := errors.New("unsupported multi-threading") - // 初始化分块或直接下载 - initOrDownload := func() error { - copyStream := func(s io.ReadCloser) error { - file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666) - if err != nil { - return err - } - defer file.Close() - if _, err = file.ReadFrom(s); err != nil { - return err - } - return errUnsupportedMultiThreading - } - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return err - } - - for k, v := range headers { - req.Header.Set(k, v) - } - if _, ok := headers["User-Agent"]; !ok { - req.Header["User-Agent"] = []string{UserAgent} - } - req.Header.Set("range", "bytes=0-") - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return errors.New("response status unsuccessful: " + strconv.FormatInt(int64(resp.StatusCode), 10)) - } - if resp.StatusCode == http.StatusOK { - if limit > 0 && resp.ContentLength > limit { - return ErrOverSize - } - return copyStream(resp.Body) - } - if resp.StatusCode == http.StatusPartialContent { - contentLength = resp.ContentLength - if limit > 0 && resp.ContentLength > limit { - return ErrOverSize - } - blockSize := contentLength - if contentLength > 1024*1024 { - blockSize = (contentLength / int64(threadCount)) - 10 - } - if blockSize == contentLength { - return copyStream(resp.Body) - } - var tmp int64 - for tmp+blockSize < contentLength { - blocks = append(blocks, &BlockMetaData{ - BeginOffset: tmp, - EndOffset: tmp + blockSize - 1, - }) - tmp += blockSize - } - blocks = append(blocks, &BlockMetaData{ - BeginOffset: tmp, - EndOffset: contentLength - 1, - }) - return nil - } - return errors.New("unknown status code") - } - // 下载分块 - downloadBlock := func(block *BlockMetaData) error { - req, _ := http.NewRequest(http.MethodGet, url, nil) - file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666) - if err != nil { - return err - } - defer file.Close() - _, _ = file.Seek(block.BeginOffset, io.SeekStart) - writer := bufio.NewWriter(file) - defer writer.Flush() - - for k, v := range headers { - req.Header.Set(k, v) - } - - if _, ok := headers["User-Agent"]; !ok { - req.Header["User-Agent"] = []string{UserAgent} - } - req.Header.Set("range", "bytes="+strconv.FormatInt(block.BeginOffset, 10)+"-"+strconv.FormatInt(block.EndOffset, 10)) - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return errors.New("response status unsuccessful: " + strconv.FormatInt(int64(resp.StatusCode), 10)) - } - buffer := make([]byte, 1024) - i, err := resp.Body.Read(buffer) - for { - if err != nil && err != io.EOF { - return err - } - i64 := int64(len(buffer[:i])) - needSize := block.EndOffset + 1 - block.BeginOffset - if i64 > needSize { - i64 = needSize - err = io.EOF - } - _, e := writer.Write(buffer[:i64]) - if e != nil { - return e - } - block.BeginOffset += i64 - block.DownloadedSize += i64 - if err == io.EOF || block.BeginOffset > block.EndOffset { - break - } - i, err = resp.Body.Read(buffer) - } - return nil - } - - if err := initOrDownload(); err != nil { - if err == errUnsupportedMultiThreading { - return nil - } - return err - } - wg := sync.WaitGroup{} - wg.Add(len(blocks)) - var lastErr error - for i := range blocks { - go func(b *BlockMetaData) { - defer wg.Done() - if err := downloadBlock(b); err != nil { - lastErr = err - } - }(blocks[i]) - } - wg.Wait() - return lastErr -} - // QQMusicSongInfo 通过给定id在QQ音乐上查找曲目信息 func QQMusicSongInfo(id string) (gjson.Result, error) { - d, err := GetBytes(`https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0&data={%22comm%22:{%22ct%22:24,%22cv%22:0},%22songinfo%22:{%22method%22:%22get_song_detail_yqq%22,%22param%22:{%22song_type%22:0,%22song_mid%22:%22%22,%22song_id%22:` + id + `},%22module%22:%22music.pf_song_detail_svr%22}}`) + d, err := download.Request{URL: `https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq.json&needNewCode=0&data={%22comm%22:{%22ct%22:24,%22cv%22:0},%22songinfo%22:{%22method%22:%22get_song_detail_yqq%22,%22param%22:{%22song_type%22:0,%22song_mid%22:%22%22,%22song_id%22:` + id + `},%22module%22:%22music.pf_song_detail_svr%22}}`}.JSON() if err != nil { return gjson.Result{}, err } - return gjson.ParseBytes(d).Get("songinfo.data"), nil + return d.Get("songinfo.data"), nil } // NeteaseMusicSongInfo 通过给定id在wdd音乐上查找曲目信息 func NeteaseMusicSongInfo(id string) (gjson.Result, error) { - d, err := GetBytes(fmt.Sprintf("http://music.163.com/api/song/detail/?id=%s&ids=%%5B%s%%5D", id, id)) + d, err := download.Request{URL: fmt.Sprintf("http://music.163.com/api/song/detail/?id=%s&ids=%%5B%s%%5D", id, id)}.JSON() if err != nil { return gjson.Result{}, err } - return gjson.ParseBytes(d).Get("songs.0"), nil -} - -type gzipCloser struct { - f io.Closer - r *gzip.Reader -} - -// NewGzipReadCloser 从 io.ReadCloser 创建 gunzip io.ReadCloser -func NewGzipReadCloser(reader io.ReadCloser) (io.ReadCloser, error) { - gzipReader, err := gzip.NewReader(reader) - if err != nil { - return nil, err - } - return &gzipCloser{ - f: reader, - r: gzipReader, - }, nil -} - -// Read impls io.Reader -func (g *gzipCloser) Read(p []byte) (n int, err error) { - return g.r.Read(p) -} - -// Close impls io.Closer -func (g *gzipCloser) Close() error { - _ = g.f.Close() - return g.r.Close() -} - -// HTTPGetReadCloser 从 Http url 获取 io.ReadCloser -func HTTPGetReadCloser(url string) (io.ReadCloser, error) { - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return nil, err - } - req.Header["User-Agent"] = []string{UserAgent} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") { - return NewGzipReadCloser(resp.Body) - } - return resp.Body, err + return d.Get("songs.0"), nil } diff --git a/internal/download/download.go b/internal/download/download.go new file mode 100644 index 000000000..b44bb6d3c --- /dev/null +++ b/internal/download/download.go @@ -0,0 +1,295 @@ +package download + +import ( + "bufio" + "compress/gzip" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "sync" + + "github.com/pkg/errors" + "github.com/tidwall/gjson" + + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +var client = &http.Client{ + Transport: &http.Transport{ + Proxy: func(request *http.Request) (u *url.URL, e error) { + if base.Proxy == "" { + return http.ProxyFromEnvironment(request) + } + return url.Parse(base.Proxy) + }, + ForceAttemptHTTP2: false, + MaxConnsPerHost: 0, + MaxIdleConns: 0, + MaxIdleConnsPerHost: 999, + }, +} + +// ErrOverSize 响应主体过大时返回此错误 +var ErrOverSize = errors.New("oversize") + +// UserAgent HTTP请求时使用的UA +const UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" + +type Request struct { + URL string + Header map[string]string + Limit int64 +} + +func (r Request) do() (*http.Response, error) { + req, err := http.NewRequest(http.MethodGet, r.URL, nil) + if err != nil { + return nil, err + } + + req.Header["User-Agent"] = []string{UserAgent} + for k, v := range r.Header { + req.Header.Set(k, v) + } + + return client.Do(req) +} + +func (r Request) body() (io.ReadCloser, error) { + resp, err := r.do() + if err != nil { + return nil, err + } + + limit := r.Limit // check file size limit + if limit > 0 && resp.ContentLength > limit { + _ = resp.Body.Close() + return nil, ErrOverSize + } + + if strings.Contains(resp.Header.Get("Content-Encoding"), "gzip") { + return gzipReadCloser(resp.Body) + } + return resp.Body, err +} + +// Bytes 对给定URL发送Get请求,返回响应主体 +func (r Request) Bytes() ([]byte, error) { + rd, err := r.body() + if err != nil { + return nil, err + } + defer rd.Close() + return io.ReadAll(rd) +} + +func (r Request) JSON() (gjson.Result, error) { + rd, err := r.body() + if err != nil { + return gjson.Result{}, err + } + defer rd.Close() + + var sb strings.Builder + _, err = io.Copy(&sb, rd) + if err != nil { + return gjson.Result{}, err + } + + return gjson.Parse(sb.String()), nil +} + +func writeToFile(reader io.ReadCloser, path string) error { + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o644) + if err != nil { + return err + } + _, err = file.ReadFrom(reader) + return err +} + +func (r Request) WriteToFile(path string) error { + rd, err := r.body() + if err != nil { + return err + } + defer rd.Close() + return writeToFile(rd, path) +} + +func (r Request) WriteToFileMultiThreading(path string, thread int) error { + if thread < 2 { + return r.WriteToFile(path) + } + + limit := r.Limit + type BlockMetaData struct { + BeginOffset int64 + EndOffset int64 + DownloadedSize int64 + } + var blocks []*BlockMetaData + var contentLength int64 + errUnsupportedMultiThreading := errors.New("unsupported multi-threading") + // 初始化分块或直接下载 + initOrDownload := func() error { + header := make(map[string]string, len(r.Header)) + for k, v := range r.Header { // copy headers + header[k] = v + } + header["range"] = "bytes=0-" + req := Request{ + URL: r.URL, + Header: header, + } + resp, err := req.do() + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return errors.New("response status unsuccessful: " + strconv.FormatInt(int64(resp.StatusCode), 10)) + } + if resp.StatusCode == http.StatusOK { + if limit > 0 && resp.ContentLength > limit { + return ErrOverSize + } + if err = writeToFile(resp.Body, path); err != nil { + return err + } + return errUnsupportedMultiThreading + } + if resp.StatusCode == http.StatusPartialContent { + contentLength = resp.ContentLength + if limit > 0 && resp.ContentLength > limit { + return ErrOverSize + } + blockSize := contentLength + if contentLength > 1024*1024 { + blockSize = (contentLength / int64(thread)) - 10 + } + if blockSize == contentLength { + return writeToFile(resp.Body, path) + } + var tmp int64 + for tmp+blockSize < contentLength { + blocks = append(blocks, &BlockMetaData{ + BeginOffset: tmp, + EndOffset: tmp + blockSize - 1, + }) + tmp += blockSize + } + blocks = append(blocks, &BlockMetaData{ + BeginOffset: tmp, + EndOffset: contentLength - 1, + }) + return nil + } + return errors.New("unknown status code") + } + // 下载分块 + downloadBlock := func(block *BlockMetaData) error { + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666) + if err != nil { + return err + } + defer file.Close() + _, _ = file.Seek(block.BeginOffset, io.SeekStart) + writer := bufio.NewWriter(file) + defer writer.Flush() + + header := make(map[string]string, len(r.Header)) + for k, v := range r.Header { // copy headers + header[k] = v + } + header["range"] = fmt.Sprintf("bytes=%d-%d", block.BeginOffset, block.EndOffset) + req := Request{ + URL: r.URL, + Header: header, + } + resp, err := req.do() + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return errors.New("response status unsuccessful: " + strconv.FormatInt(int64(resp.StatusCode), 10)) + } + buffer := make([]byte, 1024) + i, err := resp.Body.Read(buffer) + for { + if err != nil && err != io.EOF { + return err + } + i64 := int64(len(buffer[:i])) + needSize := block.EndOffset + 1 - block.BeginOffset + if i64 > needSize { + i64 = needSize + err = io.EOF + } + _, e := writer.Write(buffer[:i64]) + if e != nil { + return e + } + block.BeginOffset += i64 + block.DownloadedSize += i64 + if err == io.EOF || block.BeginOffset > block.EndOffset { + break + } + i, err = resp.Body.Read(buffer) + } + return nil + } + + if err := initOrDownload(); err != nil { + if err == errUnsupportedMultiThreading { + return nil + } + return err + } + wg := sync.WaitGroup{} + wg.Add(len(blocks)) + var lastErr error + for i := range blocks { + go func(b *BlockMetaData) { + defer wg.Done() + if err := downloadBlock(b); err != nil { + lastErr = err + } + }(blocks[i]) + } + wg.Wait() + return lastErr +} + +type gzipCloser struct { + f io.Closer + r *gzip.Reader +} + +// gzipReadCloser 从 io.ReadCloser 创建 gunzip io.ReadCloser +func gzipReadCloser(reader io.ReadCloser) (io.ReadCloser, error) { + gzipReader, err := gzip.NewReader(reader) + if err != nil { + return nil, err + } + return &gzipCloser{ + f: reader, + r: gzipReader, + }, nil +} + +// Read impls io.Reader +func (g *gzipCloser) Read(p []byte) (n int, err error) { + return g.r.Read(p) +} + +// Close impls io.Closer +func (g *gzipCloser) Close() error { + _ = g.f.Close() + return g.r.Close() +} diff --git a/internal/selfupdate/update.go b/internal/selfupdate/update.go index 8771502bc..108e5fc1c 100644 --- a/internal/selfupdate/update.go +++ b/internal/selfupdate/update.go @@ -3,6 +3,7 @@ package selfupdate import ( "bufio" + "bytes" "encoding/hex" "fmt" "hash" @@ -14,10 +15,10 @@ import ( "strings" "github.com/sirupsen/logrus" - "github.com/tidwall/gjson" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/internal/download" ) func readLine() (str string) { @@ -28,11 +29,11 @@ func readLine() (str string) { } func lastVersion() (string, error) { - r, err := global.GetBytes("https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest") + r, err := download.Request{URL: "https://api.github.com/repos/Mrs4s/go-cqhttp/releases/latest"}.JSON() if err != nil { return "", err } - return gjson.GetBytes(r, "tag_name").Str, nil + return r.Get("tag_name").Str, nil } // CheckUpdate 检查更新 @@ -69,12 +70,12 @@ func binaryName() string { func checksum(github, version string) []byte { sumURL := fmt.Sprintf("%v/Mrs4s/go-cqhttp/releases/download/%v/go-cqhttp_checksums.txt", github, version) - closer, err := global.HTTPGetReadCloser(sumURL) + sum, err := download.Request{URL: sumURL}.Bytes() if err != nil { return nil } - rd := bufio.NewReader(closer) + rd := bufio.NewReader(bytes.NewReader(sum)) for { str, err := rd.ReadString('\n') if err != nil { From 997cdceb7a0d80ee391f446715c85af48924247b Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 13 Oct 2022 21:34:08 +0800 Subject: [PATCH 455/612] internal/download: add comments make lint happy! --- internal/download/download.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/download/download.go b/internal/download/download.go index b44bb6d3c..1387168dd 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -1,3 +1,4 @@ +// Package download provide download utility functions package download import ( @@ -39,6 +40,7 @@ var ErrOverSize = errors.New("oversize") // UserAgent HTTP请求时使用的UA const UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" +// Request is a file download request type Request struct { URL string Header map[string]string @@ -87,6 +89,7 @@ func (r Request) Bytes() ([]byte, error) { return io.ReadAll(rd) } +// JSON 发送GET请求, 并转换响应为JSON func (r Request) JSON() (gjson.Result, error) { rd, err := r.body() if err != nil { @@ -112,6 +115,7 @@ func writeToFile(reader io.ReadCloser, path string) error { return err } +// WriteToFile 下载到制定目录 func (r Request) WriteToFile(path string) error { rd, err := r.body() if err != nil { @@ -121,6 +125,7 @@ func (r Request) WriteToFile(path string) error { return writeToFile(rd, path) } +// WriteToFileMultiThreading 多线程下载到制定目录 func (r Request) WriteToFileMultiThreading(path string, thread int) error { if thread < 2 { return r.WriteToFile(path) From 32d1f213f34d2895f3b1c58cd884ddc674f2fb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 18 Oct 2022 20:09:37 +0800 Subject: [PATCH 456/612] =?UTF-8?q?feat:=20=E5=BC=80=E5=A7=8B=E5=B0=9D?= =?UTF-8?q?=E8=AF=95=E4=B8=8A=E4=BC=A0=E6=97=B6=E5=A2=9E=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E6=97=A5=E5=BF=97=EF=BC=8C=E6=96=B9=E4=BE=BF=E6=8E=92?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index c8dce41ef..a14d227c1 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -95,6 +95,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete maxRetryWaitTime := 12 * time.Hour for { // 尝试上传新版本 + logger.Infof("[%v/%v] 开始尝试上传", failIndex, maxFailTimes, retryWaitTime) for _, groupID := range groupsToUpload { logger.Infof("开始上传 %v 到 群 %v", uploadFileName, groupID) r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords, false) @@ -125,7 +126,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete break } - logger.Infof("第 %v 次上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, retryWaitTime) + logger.Infof("[%v/%v] 上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, maxFailTimes, retryWaitTime) select { case <-time.After(retryWaitTime): break From 60c8d8aa832eba12f386b9a2f11b610cd966273f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 18 Oct 2022 20:12:24 +0800 Subject: [PATCH 457/612] =?UTF-8?q?feat:=20=E5=90=88=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 5dc6ddbfe..ad92d808a 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -100,7 +100,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete maxRetryWaitTime := 12 * time.Hour for { // 尝试上传新版本 - logger.Infof("[%v/%v] 开始尝试上传", failIndex, maxFailTimes, retryWaitTime) + boldYellowLog("[%v/%v] 开始尝试上传", failIndex, maxFailTimes, retryWaitTime) for _, groupID := range groupsToUpload { boldYellowLog("开始上传 %v 到 群 %v", uploadFileName, groupID) r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords, false) @@ -131,7 +131,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete break } - logger.Infof("[%v/%v] 上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, maxFailTimes, retryWaitTime) + boldYellowLog("[%v/%v] 上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, maxFailTimes, retryWaitTime) select { case <-time.After(retryWaitTime): break From fdf1fc13a1eb29e876fb271b0142f84cc4f9dc75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 18 Oct 2022 20:24:16 +0800 Subject: [PATCH 458/612] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E8=BE=85=E5=8A=A9=E5=87=BD=E6=95=B0=EF=BC=8C=E5=8F=91?= =?UTF-8?q?=E9=80=81=E6=96=87=E6=9C=AC=E6=B6=88=E6=81=AF=E7=BB=99=E7=BE=A4?= =?UTF-8?q?=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/qq_robot.go | 4 ++-- qqrobot/util.go | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 470b2db01..5c2f17bc7 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -597,14 +597,14 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { } case actiontypeUpdateNewVersion: if isAdmin { - r.cqBot.SendGroupMessage(groupID, message.NewSendingMessage().Append(message.NewText("开始执行上传指令,可能需要一点时间~"))) + r.sendTextMessageToGroup(groupID, "开始执行上传指令,可能需要一点时间~") go func() { r.updateNewVersionInGroup("手动触发更新新版本", config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath, true, ) - r.cqBot.SendGroupMessage(groupID, message.NewSendingMessage().Append(message.NewText("上传完毕,具体情况可查看服务器日志"))) + r.sendTextMessageToGroup(groupID, "上传完毕,具体情况可查看服务器日志") }() } else { replies.Append(message.NewText("只有管理员可以执行这个指令哦~不要调皮<_<")) diff --git a/qqrobot/util.go b/qqrobot/util.go index e052da66f..1600dd7ed 100644 --- a/qqrobot/util.go +++ b/qqrobot/util.go @@ -79,6 +79,10 @@ func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) (ocrResultSt return ocrResultString } +func (r *QQRobot) sendTextMessageToGroup(groupID int64, msg string) { + r.cqBot.SendGroupMessage(groupID, message.NewSendingMessage().Append(message.NewText(msg))) +} + func (r *QQRobot) currentTime() string { return r.formatTime(time.Now()) } From 85a0e2ad22f0134894f055b38e4dfb0014d85fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 18 Oct 2022 20:38:16 +0800 Subject: [PATCH 459/612] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E7=9A=84?= =?UTF-8?q?=E5=BC=80=E5=A7=8B=E5=92=8C=E7=BB=93=E6=9D=9F=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=91=E6=8C=87=E5=AE=9A=E7=BE=A4=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=EF=BC=8C=E6=96=B9=E4=BE=BF=E5=BE=97=E7=9F=A5=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 39 ++++++++++++++++++++++++++------------- qqrobot/config.go | 3 +++ qqrobot/qq_robot.go | 4 ++-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index ad92d808a..d31229e5f 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -68,7 +68,10 @@ func (r *QQRobot) checkUpdates() { } logger.Infof("check update %v, from %v to %v", rule.Name, lastVersion, latestVersion) - r.updateNewVersionInGroup(rule.Name, rule.NotifyGroups, rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath, true) + r.updateNewVersionInGroup(rule.Name, + rule.NotifyGroups, rule.ProgressNotifyGroups, + rule.DownloadNewVersionPythonInterpreterPath, rule.DownloadNewVersionPythonScriptPath, true, + ) }() } } @@ -78,16 +81,26 @@ func boldYellowLog(format string, args ...interface{}) { logger.Infof(bold(color.Yellow).Render(fmt.Sprintf(format, args...))) } -func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interpreter string, script string, needRetry bool) { +func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, progressNotifyGroups []int64, interpreter string, script string, needRetry bool) { + notifyUpdateProgress := func(progressMessageFormat string, args ...interface{}) { + msg := fmt.Sprintf(progressMessageFormat, args...) + // 打个日志 + boldYellowLog(msg) + // 通知到指定的群 + for _, groupID := range progressNotifyGroups { + r.sendTextMessageToGroup(groupID, msg) + } + } + if interpreter != "" && script != "" && global.PathExists(interpreter) && global.PathExists(script) { - boldYellowLog("开始更新新版本到各个群中: %v", groups) - boldYellowLog("是否重试: %v", needRetry) + notifyUpdateProgress("开始更新新版本到各个群中: %v", groups) + notifyUpdateProgress("是否重试: %v", needRetry) oldVersionKeywords := "DNF蚊子腿小助手_v" - boldYellowLog("开始调用配置的更新命令来获取新版本: %v %v", interpreter, script) + notifyUpdateProgress("开始调用配置的更新命令来获取新版本: %v %v", interpreter, script) newVersionFilePath, err := downloadNewVersionUsingPythonScript(interpreter, script) if err != nil { - logger.Warnf("下载新版本失败, err=%v", err) + notifyUpdateProgress("下载新版本失败, err=%v", err) return } @@ -100,7 +113,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete maxRetryWaitTime := 12 * time.Hour for { // 尝试上传新版本 - boldYellowLog("[%v/%v] 开始尝试上传", failIndex, maxFailTimes, retryWaitTime) + notifyUpdateProgress("[%v/%v] 开始尝试上传", failIndex, maxFailTimes, retryWaitTime) for _, groupID := range groupsToUpload { boldYellowLog("开始上传 %v 到 群 %v", uploadFileName, groupID) r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords, false) @@ -116,22 +129,22 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete } } if len(groupsNotUploaded) == 0 { - boldYellowLog("全部上传成功,完毕") + notifyUpdateProgress("全部上传成功,完毕") break } - boldYellowLog("%v 个群未上传成功: %v", len(groupsNotUploaded), groupsNotUploaded) + notifyUpdateProgress("%v 个群未上传成功: %v", len(groupsNotUploaded), groupsNotUploaded) if !needRetry { - logger.Warnf("当前配置为不需要重试") + notifyUpdateProgress("当前配置为不需要重试,将停止上传流程") break } if failIndex > maxFailTimes { - logger.Warnf("重试次数超过 %v 次,停止重试", maxFailTimes) + notifyUpdateProgress("重试次数超过 %v 次,将停止上传流程", maxFailTimes) break } - boldYellowLog("[%v/%v] 上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, maxFailTimes, retryWaitTime) + notifyUpdateProgress("[%v/%v] 上传失败, 等待 %v 后再尝试上传到这些群中", failIndex, maxFailTimes, retryWaitTime) select { case <-time.After(retryWaitTime): break @@ -147,7 +160,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, interprete } } } else { - boldYellowLog("%v: 未配置更新python脚本,或者对应脚本不存在,将不会尝试下载并上传新版本到群文件", ctx) + notifyUpdateProgress("%v: 未配置更新python脚本,或者对应脚本不存在,将不会尝试下载并上传新版本到群文件", ctx) } } diff --git a/qqrobot/config.go b/qqrobot/config.go index 3bfe6e5b9..b3e275b93 100644 --- a/qqrobot/config.go +++ b/qqrobot/config.go @@ -192,6 +192,8 @@ type NotifyUpdateRule struct { Name string `toml:"name"` // 名称 NotifyGroups []int64 `toml:"notify_groups"` // 通知的群 NotifyGroupTypes []string `toml:"notify_group_types"` // 通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 + ProgressNotifyGroups []int64 `toml:"progress_notify_groups"` // 进度发生变更时通知的群 + ProgressNotifyGroupTypes []string `toml:"progress_notify_group_types"` // 进度发生变更时通知适用的QQ群类别,将于QQ群ID列表合并组成最终生效QQ群列表 Message string `toml:"message"` // 通知的消息,参数:$git_version$=最新版本, $update_message$=更新信息 ImageURL string `toml:"image_url"` // 图片URL,若有,则会额外附加图片 GitChangelogRawURL string `toml:"git_changelog_raw_url"` // git仓库的changelog的raw url,将请求这个网页,从中解析出最新的版本号和更新信息,并替换到message中的$git_version$和$update_message$ @@ -271,6 +273,7 @@ func (c *Config) Init() { rule := &c.NotifyUpdate.Rules[idx] rule.NotifyGroups = c.mergeGroupTypesIntoGroups(rule.NotifyGroups, rule.NotifyGroupTypes) + rule.ProgressNotifyGroups = c.mergeGroupTypesIntoGroups(rule.ProgressNotifyGroups, rule.ProgressNotifyGroupTypes) } if err := c.check(); err != nil { diff --git a/qqrobot/qq_robot.go b/qqrobot/qq_robot.go index 5c2f17bc7..af137806c 100644 --- a/qqrobot/qq_robot.go +++ b/qqrobot/qq_robot.go @@ -601,8 +601,8 @@ func (r *QQRobot) applyGroupRule(m *message.GroupMessage, rule *Rule) error { go func() { r.updateNewVersionInGroup("手动触发更新新版本", - config.UpdateNewVersionToGroups, config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath, - true, + config.UpdateNewVersionToGroups, []int64{groupID}, + config.DownloadNewVersionPythonInterpreterPath, config.DownloadNewVersionPythonScriptPath, true, ) r.sendTextMessageToGroup(groupID, "上传完毕,具体情况可查看服务器日志") }() From 92716fa97af79f3c3559a12180d311f462f3f3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Tue, 18 Oct 2022 20:52:21 +0800 Subject: [PATCH 460/612] =?UTF-8?q?fix:=20=E5=A4=9A=E4=BA=86=E4=B8=AA?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index d31229e5f..47554acfc 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -113,7 +113,7 @@ func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, progressNo maxRetryWaitTime := 12 * time.Hour for { // 尝试上传新版本 - notifyUpdateProgress("[%v/%v] 开始尝试上传", failIndex, maxFailTimes, retryWaitTime) + notifyUpdateProgress("[%v/%v] 开始尝试上传", failIndex, maxFailTimes) for _, groupID := range groupsToUpload { boldYellowLog("开始上传 %v 到 群 %v", uploadFileName, groupID) r.updateFileInGroup(groupID, newVersionFilePath, uploadFileName, oldVersionKeywords, false) From 0682617d5e024d7fdee91635d40f3bffd963ce95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 24 Oct 2022 16:31:57 +0800 Subject: [PATCH 461/612] =?UTF-8?q?feat:=20=E6=A3=80=E6=B5=8B=E5=88=B0?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=97=B6=E4=B9=9F=E5=B0=86=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E5=8F=91=E5=88=B0=E7=BE=A4=E9=87=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/check_update.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/qqrobot/check_update.go b/qqrobot/check_update.go index 47554acfc..65bbc1339 100644 --- a/qqrobot/check_update.go +++ b/qqrobot/check_update.go @@ -44,7 +44,7 @@ func (r *QQRobot) checkUpdates() { // re: 以下是临时措施,应对changelog中的版本更新后平均约 10 分钟后github action的打包release流程才完成的情况 // undone: 等将新版本改成基于release中的meta信息来获取后,再改成实时通知 go func() { - logger.Warnf("%v 版本有更新 %v => %v,但目前因获取版本的来源比release早约十分钟,因此在这里等待20分钟后再实际进行通知", rule.Name, lastVersion, latestVersion) + r.doNotifyUpdateProgress(rule.ProgressNotifyGroups, "%v 版本有更新 %v => %v,但目前因获取版本的来源比release早约十分钟,因此在这里等待20分钟后再实际进行通知", rule.Name, lastVersion, latestVersion) select { case <-time.After(2 * 10 * time.Minute): break @@ -52,7 +52,7 @@ func (r *QQRobot) checkUpdates() { return } - logger.Infof("%v 版本有更新 %v => %v, 开始通知各个群以及上传群文件", rule.Name, lastVersion, latestVersion) + r.doNotifyUpdateProgress(rule.ProgressNotifyGroups, "%v 版本有更新 %v => %v, 开始通知各个群以及上传群文件", rule.Name, lastVersion, latestVersion) replies := r.makeNotifyUpdatesReplies(rule, latestVersion, updateMessage) nowStr := r.currentTime() @@ -81,15 +81,19 @@ func boldYellowLog(format string, args ...interface{}) { logger.Infof(bold(color.Yellow).Render(fmt.Sprintf(format, args...))) } +func (r *QQRobot) doNotifyUpdateProgress(progressNotifyGroups []int64, progressMessageFormat string, args ...interface{}) { + msg := fmt.Sprintf(progressMessageFormat, args...) + // 打个日志 + boldYellowLog(msg) + // 通知到指定的群 + for _, groupID := range progressNotifyGroups { + r.sendTextMessageToGroup(groupID, msg) + } +} + func (r *QQRobot) updateNewVersionInGroup(ctx string, groups []int64, progressNotifyGroups []int64, interpreter string, script string, needRetry bool) { notifyUpdateProgress := func(progressMessageFormat string, args ...interface{}) { - msg := fmt.Sprintf(progressMessageFormat, args...) - // 打个日志 - boldYellowLog(msg) - // 通知到指定的群 - for _, groupID := range progressNotifyGroups { - r.sendTextMessageToGroup(groupID, msg) - } + r.doNotifyUpdateProgress(progressNotifyGroups, progressMessageFormat, args...) } if interpreter != "" && script != "" && global.PathExists(interpreter) && global.PathExists(script) { From db64699f3cec8dfe6ea3f6be44b9257e83e1eed0 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 3 Nov 2022 20:47:43 +0800 Subject: [PATCH 462/612] all: use os.Chdir instead of fork a process when changing working directory --- cmd/gocq/main.go | 5 ++++- internal/base/flag.go | 33 --------------------------------- 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 442f73eb6..2177fcd3f 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -56,7 +56,10 @@ func Main() { case base.LittleH: base.Help() case base.LittleWD != "": - base.ResetWorkingDir() + err := os.Chdir(base.LittleWD) + if err != nil { + log.Fatalf("重置工作目录时出现错误: %v", err) + } case base.LittleD: server.Daemon() } diff --git a/internal/base/flag.go b/internal/base/flag.go index 24ad3610e..94da9b19c 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -5,13 +5,9 @@ import ( "flag" "fmt" "os" - "os/exec" "path" - "path/filepath" - "strings" "time" - "github.com/pkg/errors" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" @@ -128,32 +124,3 @@ Options: flag.PrintDefaults() os.Exit(0) } - -// ResetWorkingDir 重设工作路径 -func ResetWorkingDir() { - wd := LittleWD - args := make([]string, 0, len(os.Args)) - for i := 1; i < len(os.Args); i++ { - if os.Args[i] == "-w" { - i++ // skip value field - } else if !strings.HasPrefix(os.Args[i], "-w") { - args = append(args, os.Args[i]) - } - } - ex, _ := os.Executable() - p, _ := filepath.Abs(ex) - _, err := os.Stat(p) - if !(err == nil || errors.Is(err, os.ErrExist)) { - log.Fatalf("重置工作目录时出现错误: 无法找到路径 %v", p) - } - proc := exec.Command(p, args...) - proc.Stdin = os.Stdin - proc.Stdout = os.Stdout - proc.Stderr = os.Stderr - proc.Dir = wd - err = proc.Run() - if err != nil { - panic(err) - } - os.Exit(0) -} From de44adbfa164ef87feb9f9189ac77603a3c82f6f Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 3 Nov 2022 20:55:01 +0800 Subject: [PATCH 463/612] internal/base: use relative path in flag -c default value --- internal/base/flag.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/base/flag.go b/internal/base/flag.go index 94da9b19c..38790ee8d 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -5,7 +5,6 @@ import ( "flag" "fmt" "os" - "path" "time" log "github.com/sirupsen/logrus" @@ -54,9 +53,7 @@ var ( // Parse parse flags func Parse() { - wd, _ := os.Getwd() - dc := path.Join(wd, "config.yml") - flag.StringVar(&LittleC, "c", dc, "configuration filename") + flag.StringVar(&LittleC, "c", "config.yml", "configuration filename") flag.BoolVar(&LittleD, "d", false, "running as a daemon") flag.BoolVar(&LittleH, "h", false, "this Help") flag.StringVar(&LittleWD, "w", "", "cover the working directory") From 93fa36034a3c5dc7ce2ea86610557c46317dfa7e Mon Sep 17 00:00:00 2001 From: Madray Haven Date: Tue, 8 Nov 2022 09:46:46 +0800 Subject: [PATCH 464/612] fix: daemon mode --- cmd/gocq/main.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 2177fcd3f..50699a3a4 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -55,13 +55,14 @@ func Main() { switch { case base.LittleH: base.Help() - case base.LittleWD != "": + case base.LittleD: + server.Daemon() + } + if base.LittleWD != "" { err := os.Chdir(base.LittleWD) if err != nil { log.Fatalf("重置工作目录时出现错误: %v", err) } - case base.LittleD: - server.Daemon() } base.Init() From c84d5832351a984114199501aee6b429d7a70d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 16:38:33 +0800 Subject: [PATCH 465/612] feat: add sqlite3 database support --- db/sqlite3/model.go | 72 ++++++++ db/sqlite3/sqlite3.go | 391 ++++++++++++++++++++++++++++++++++++++++++ go.mod | 12 +- go.sum | 143 ++++++++++++++- 4 files changed, 610 insertions(+), 8 deletions(-) create mode 100644 db/sqlite3/model.go create mode 100644 db/sqlite3/sqlite3.go diff --git a/db/sqlite3/model.go b/db/sqlite3/model.go new file mode 100644 index 000000000..0afb8e1f1 --- /dev/null +++ b/db/sqlite3/model.go @@ -0,0 +1,72 @@ +package sqlite3 + +const ( + Sqlite3GroupMessageTableName = "grpmsg" + Sqlite3MessageAttributeTableName = "msgattr" + Sqlite3GuildMessageAttributeTableName = "gmsgattr" + Sqlite3QuotedInfoTableName = "quoinf" + Sqlite3PrivateMessageTableName = "privmsg" + Sqlite3GuildChannelMessageTableName = "guildmsg" +) + +// StoredMessageAttribute 持久化消息属性 +type StoredMessageAttribute struct { + ID int64 // ID is the crc64 of 字段s below + MessageSeq int32 + InternalID int32 + SenderUin int64 + SenderName string + Timestamp int64 +} + +// StoredGuildMessageAttribute 持久化频道消息属性 +type StoredGuildMessageAttribute struct { + ID int64 // ID is the crc64 of 字段s below + MessageSeq int64 + InternalID int64 + SenderTinyID int64 + SenderName string + Timestamp int64 +} + +// QuotedInfo 引用回复 +type QuotedInfo struct { + ID int64 // ID is the crc64 of 字段s below + PrevID string + PrevGlobalID int32 + QuotedContent string // QuotedContent is json of original content +} + +// StoredGroupMessage 持久化群消息 +type StoredGroupMessage struct { + GlobalID int32 + ID string + AttributeID int64 + SubType string + QuotedInfoID int64 + GroupCode int64 + AnonymousID string + Content string // Content is json of original content +} + +// StoredPrivateMessage 持久化私聊消息 +type StoredPrivateMessage struct { + GlobalID int32 + ID string + AttributeID int64 + SubType string + QuotedInfoID int64 + SessionUin int64 + TargetUin int64 + Content string // Content is json of original content +} + +// StoredGuildChannelMessage 持久化频道消息 +type StoredGuildChannelMessage struct { + ID string + AttributeID int64 + GuildID int64 + ChannelID int64 + QuotedInfoID int64 + Content string // Content is json of original content +} diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go new file mode 100644 index 000000000..e806679c5 --- /dev/null +++ b/db/sqlite3/sqlite3.go @@ -0,0 +1,391 @@ +package sqlite3 + +import ( + "encoding/json" + "hash/crc64" + "os" + "path" + "strconv" + "sync" + "time" + + sql "github.com/FloatTech/sqlite" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" + + "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/MiraiGo/utils" + "github.com/Mrs4s/go-cqhttp/db" +) + +type database struct { + sync.RWMutex + db *sql.Sqlite + ttl time.Duration +} + +// config mongodb 相关配置 +type config struct { + Enable bool `yaml:"enable"` + CacheTTL time.Duration `yaml:"cachettl"` +} + +func init() { + db.Register("sqlite3", func(node yaml.Node) db.Database { + conf := new(config) + _ = node.Decode(conf) + if !conf.Enable { + return nil + } + return &database{db: new(sql.Sqlite), ttl: conf.CacheTTL} + }) +} + +func (s *database) Open() error { + s.db.DBPath = path.Join("data", "sqlite3") + _ = os.MkdirAll(s.db.DBPath, 0755) + err := s.db.Open(s.ttl) + if err != nil { + return errors.Wrap(err, "open sqlite3 error") + } + return nil +} + +func (s *database) GetMessageByGlobalID(id int32) (db.StoredMessage, error) { + if r, err := s.GetGroupMessageByGlobalID(id); err == nil { + return r, nil + } + return s.GetPrivateMessageByGlobalID(id) +} + +func (s *database) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, error) { + var ret db.StoredGroupMessage + var grpmsg StoredGroupMessage + s.RLock() + err := s.db.Find(Sqlite3GroupMessageTableName, &grpmsg, "WHERE GlobalID="+strconv.Itoa(int(id))) + s.RUnlock() + if err != nil { + return nil, errors.Wrap(err, "query error") + } + ret.ID = grpmsg.ID + ret.GlobalID = grpmsg.GlobalID + ret.SubType = grpmsg.SubType + ret.GroupCode = grpmsg.GroupCode + ret.AnonymousID = grpmsg.AnonymousID + _ = json.Unmarshal(utils.S2B(grpmsg.Content), &ret.Content) + var attr StoredMessageAttribute + s.RLock() + err = s.db.Find(Sqlite3MessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(grpmsg.AttributeID, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredMessageAttribute{ + MessageSeq: attr.MessageSeq, + InternalID: attr.InternalID, + SenderUin: attr.SenderUin, + SenderName: attr.SenderName, + Timestamp: attr.Timestamp, + } + } + var quoinf QuotedInfo + s.RLock() + err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(grpmsg.QuotedInfoID, 10)) + s.RUnlock() + if err == nil { + ret.QuotedInfo = &db.QuotedInfo{ + PrevID: quoinf.PrevID, + PrevGlobalID: quoinf.PrevGlobalID, + } + _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) + } + return &ret, nil +} + +func (s *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessage, error) { + var ret db.StoredPrivateMessage + var privmsg StoredPrivateMessage + s.RLock() + err := s.db.Find(Sqlite3PrivateMessageTableName, &privmsg, "WHERE GlobalID="+strconv.Itoa(int(id))) + s.RUnlock() + if err != nil { + return nil, errors.Wrap(err, "query error") + } + ret.ID = privmsg.ID + ret.GlobalID = privmsg.GlobalID + ret.SubType = privmsg.SubType + ret.SessionUin = privmsg.SessionUin + ret.TargetUin = privmsg.TargetUin + _ = json.Unmarshal(utils.S2B(privmsg.Content), &ret.Content) + var attr StoredMessageAttribute + s.RLock() + err = s.db.Find(Sqlite3MessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(privmsg.AttributeID, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredMessageAttribute{ + MessageSeq: attr.MessageSeq, + InternalID: attr.InternalID, + SenderUin: attr.SenderUin, + SenderName: attr.SenderName, + Timestamp: attr.Timestamp, + } + } + var quoinf QuotedInfo + s.RLock() + err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(privmsg.QuotedInfoID, 10)) + s.RUnlock() + if err == nil { + ret.QuotedInfo = &db.QuotedInfo{ + PrevID: quoinf.PrevID, + PrevGlobalID: quoinf.PrevGlobalID, + } + _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) + } + return &ret, nil +} + +func (s *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { + var ret db.StoredGuildChannelMessage + var guildmsg StoredGuildChannelMessage + s.RLock() + err := s.db.Find(Sqlite3GuildChannelMessageTableName, &guildmsg, "WHERE ID='"+id+"'") + s.RUnlock() + if err != nil { + return nil, errors.Wrap(err, "query error") + } + ret.ID = guildmsg.ID + ret.GuildID = uint64(guildmsg.GuildID) + ret.ChannelID = uint64(guildmsg.ChannelID) + _ = json.Unmarshal(utils.S2B(guildmsg.Content), &ret.Content) + var attr StoredGuildMessageAttribute + s.RLock() + err = s.db.Find(Sqlite3GuildMessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(guildmsg.AttributeID, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredGuildMessageAttribute{ + MessageSeq: uint64(attr.MessageSeq), + InternalID: uint64(attr.InternalID), + SenderTinyID: uint64(attr.SenderTinyID), + SenderName: attr.SenderName, + Timestamp: attr.Timestamp, + } + } + var quoinf QuotedInfo + s.RLock() + err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(guildmsg.QuotedInfoID, 10)) + s.RUnlock() + if err == nil { + ret.QuotedInfo = &db.QuotedInfo{ + PrevID: quoinf.PrevID, + PrevGlobalID: quoinf.PrevGlobalID, + } + _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) + } + return &ret, nil +} + +func (s *database) InsertGroupMessage(msg *db.StoredGroupMessage) error { + grpmsg := &StoredGroupMessage{ + GlobalID: msg.GlobalID, + ID: msg.ID, + SubType: msg.SubType, + GroupCode: msg.GroupCode, + AnonymousID: msg.AnonymousID, + } + h := crc64.New(crc64.MakeTable(crc64.ISO)) + if msg.Attribute != nil { + h.Write(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt32(uint32(msg.Attribute.MessageSeq)) + w.WriteUInt32(uint32(msg.Attribute.InternalID)) + w.WriteUInt64(uint64(msg.Attribute.SenderUin)) + w.WriteUInt64(uint64(msg.Attribute.Timestamp)) + })) + h.Write(utils.S2B(msg.Attribute.SenderName)) + id := int64(h.Sum64()) + s.Lock() + err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{ + ID: id, + MessageSeq: msg.Attribute.MessageSeq, + InternalID: msg.Attribute.InternalID, + SenderUin: msg.Attribute.SenderUin, + SenderName: msg.Attribute.SenderName, + Timestamp: msg.Attribute.Timestamp, + }) + s.Unlock() + if err == nil { + grpmsg.AttributeID = id + } + h.Reset() + } + if msg.QuotedInfo != nil { + h.Write(utils.S2B(msg.QuotedInfo.PrevID)) + h.Write(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt32(uint32(msg.QuotedInfo.PrevGlobalID)) + })) + content, err := json.Marshal(&msg.QuotedInfo.QuotedContent) + if err != nil { + return errors.Wrap(err, "insert marshal QuotedContent error") + } + h.Write(content) + id := int64(h.Sum64()) + s.Lock() + err = s.db.Insert(Sqlite3QuotedInfoTableName, &QuotedInfo{ + ID: id, + PrevID: msg.QuotedInfo.PrevID, + PrevGlobalID: msg.QuotedInfo.PrevGlobalID, + QuotedContent: utils.B2S(content), + }) + s.Unlock() + if err == nil { + grpmsg.QuotedInfoID = id + } + } + content, err := json.Marshal(&msg.Content) + if err != nil { + return errors.Wrap(err, "insert marshal Content error") + } + grpmsg.Content = utils.B2S(content) + s.Lock() + err = s.db.Insert(Sqlite3GroupMessageTableName, grpmsg) + s.Unlock() + if err != nil { + return errors.Wrap(err, "insert error") + } + return nil +} + +func (s *database) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { + privmsg := &StoredPrivateMessage{ + GlobalID: msg.GlobalID, + ID: msg.ID, + SubType: msg.SubType, + SessionUin: msg.SessionUin, + TargetUin: msg.TargetUin, + } + h := crc64.New(crc64.MakeTable(crc64.ISO)) + if msg.Attribute != nil { + h.Write(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt32(uint32(msg.Attribute.MessageSeq)) + w.WriteUInt32(uint32(msg.Attribute.InternalID)) + w.WriteUInt64(uint64(msg.Attribute.SenderUin)) + w.WriteUInt64(uint64(msg.Attribute.Timestamp)) + })) + h.Write(utils.S2B(msg.Attribute.SenderName)) + id := int64(h.Sum64()) + s.Lock() + err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{ + ID: id, + MessageSeq: msg.Attribute.MessageSeq, + InternalID: msg.Attribute.InternalID, + SenderUin: msg.Attribute.SenderUin, + SenderName: msg.Attribute.SenderName, + Timestamp: msg.Attribute.Timestamp, + }) + s.Unlock() + if err == nil { + privmsg.AttributeID = id + } + h.Reset() + } + if msg.QuotedInfo != nil { + h.Write(utils.S2B(msg.QuotedInfo.PrevID)) + h.Write(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt32(uint32(msg.QuotedInfo.PrevGlobalID)) + })) + content, err := json.Marshal(&msg.QuotedInfo.QuotedContent) + if err != nil { + return errors.Wrap(err, "insert marshal QuotedContent error") + } + h.Write(content) + id := int64(h.Sum64()) + s.Lock() + err = s.db.Insert(Sqlite3QuotedInfoTableName, &QuotedInfo{ + ID: id, + PrevID: msg.QuotedInfo.PrevID, + PrevGlobalID: msg.QuotedInfo.PrevGlobalID, + QuotedContent: utils.B2S(content), + }) + s.Unlock() + if err == nil { + privmsg.QuotedInfoID = id + } + } + content, err := json.Marshal(&msg.Content) + if err != nil { + return errors.Wrap(err, "insert marshal Content error") + } + privmsg.Content = utils.B2S(content) + s.Lock() + err = s.db.Insert(Sqlite3PrivateMessageTableName, privmsg) + s.Unlock() + if err != nil { + return errors.Wrap(err, "insert error") + } + return nil +} + +func (s *database) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) error { + guildmsg := &StoredGuildChannelMessage{ + ID: msg.ID, + GuildID: int64(msg.GuildID), + ChannelID: int64(msg.ChannelID), + } + h := crc64.New(crc64.MakeTable(crc64.ISO)) + if msg.Attribute != nil { + h.Write(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt32(uint32(msg.Attribute.MessageSeq)) + w.WriteUInt32(uint32(msg.Attribute.InternalID)) + w.WriteUInt64(uint64(msg.Attribute.SenderTinyID)) + w.WriteUInt64(uint64(msg.Attribute.Timestamp)) + })) + h.Write(utils.S2B(msg.Attribute.SenderName)) + id := int64(h.Sum64()) + s.Lock() + err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredGuildMessageAttribute{ + ID: id, + MessageSeq: int64(msg.Attribute.MessageSeq), + InternalID: int64(msg.Attribute.InternalID), + SenderTinyID: int64(msg.Attribute.SenderTinyID), + SenderName: msg.Attribute.SenderName, + Timestamp: msg.Attribute.Timestamp, + }) + s.Unlock() + if err == nil { + guildmsg.AttributeID = id + } + h.Reset() + } + if msg.QuotedInfo != nil { + h.Write(utils.S2B(msg.QuotedInfo.PrevID)) + h.Write(binary.NewWriterF(func(w *binary.Writer) { + w.WriteUInt32(uint32(msg.QuotedInfo.PrevGlobalID)) + })) + content, err := json.Marshal(&msg.QuotedInfo.QuotedContent) + if err != nil { + return errors.Wrap(err, "insert marshal QuotedContent error") + } + h.Write(content) + id := int64(h.Sum64()) + s.Lock() + err = s.db.Insert(Sqlite3QuotedInfoTableName, &QuotedInfo{ + ID: id, + PrevID: msg.QuotedInfo.PrevID, + PrevGlobalID: msg.QuotedInfo.PrevGlobalID, + QuotedContent: utils.B2S(content), + }) + s.Unlock() + if err == nil { + guildmsg.QuotedInfoID = id + } + } + content, err := json.Marshal(&msg.Content) + if err != nil { + return errors.Wrap(err, "insert marshal Content error") + } + guildmsg.Content = utils.B2S(content) + s.Lock() + err = s.db.Insert(Sqlite3GuildChannelMessageTableName, guildmsg) + s.Unlock() + if err != nil { + return errors.Wrap(err, "insert error") + } + return nil +} diff --git a/go.mod b/go.mod index 0de9c8b62..8bc52adc5 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Mrs4s/go-cqhttp go 1.19 require ( + github.com/FloatTech/sqlite v0.5.0 github.com/Microsoft/go-winio v0.5.1 github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c @@ -20,18 +21,22 @@ require ( github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 go.mongodb.org/mongo-driver v1.8.3 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b // indirect github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect + github.com/fumiama/sqlite3 v1.14.6 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-cmp v0.5.5 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/klauspost/compress v1.13.6 // indirect github.com/lestrrat-go/strftime v1.0.5 // indirect @@ -47,9 +52,8 @@ require ( github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect - golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect golang.org/x/text v0.3.7 // indirect - modernc.org/libc v1.8.1 // indirect - modernc.org/mathutil v1.2.2 // indirect - modernc.org/memory v1.0.4 // indirect + modernc.org/libc v1.14.6 // indirect + modernc.org/mathutil v1.4.1 // indirect + modernc.org/memory v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index 47b2da4d0..c4a4423ae 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/FloatTech/sqlite v0.5.0 h1:U7J5Omc534PqmH6csfu+ypCo3DS8L91l5lTsxUu3b/U= +github.com/FloatTech/sqlite v0.5.0/go.mod h1:i33d92OtR8jcp5fBUvQtospf27+MkfUxnGwnZ95E/dA= +github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw= +github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe h1:KpRbU5MRL2dplf01Cbgo8m0+ST1V1A+4cFBQkQA/YAk= @@ -11,6 +15,7 @@ github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMm github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -20,6 +25,8 @@ github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4= +github.com/fumiama/sqlite3 v1.14.6 h1:+e+iygyiDXQJVi7xeXIviBvR7hAc5y20WA9hRwfKn10= +github.com/fumiama/sqlite3 v1.14.6/go.mod h1:Xx9a2/OtHuy9pBjow0N+bE/RhNeZ7zZz5xh25vqbA5A= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -36,11 +43,15 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -112,23 +123,29 @@ github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyh github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -142,12 +159,17 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -161,6 +183,10 @@ golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13W golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -182,10 +208,119 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= +modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= +modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= +modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4= +modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= +modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8= +modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= +modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= +modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= +modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= +modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= +modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= +modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= +modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= +modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= +modernc.org/libc v1.14.6 h1:SSiZiE5199iYsGM9gtkDj90xqcXVwubWG8CtoYE+Mnk= +modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= From 5c78174d1cae992486b6a0ab26c030b520fd7b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 16:51:12 +0800 Subject: [PATCH 466/612] fix: possible id == 0 --- db/sqlite3/sqlite3.go | 150 +++++++++++++++++++++++++----------------- 1 file changed, 90 insertions(+), 60 deletions(-) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index e806679c5..c550468f3 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -73,29 +73,33 @@ func (s *database) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, ret.GroupCode = grpmsg.GroupCode ret.AnonymousID = grpmsg.AnonymousID _ = json.Unmarshal(utils.S2B(grpmsg.Content), &ret.Content) - var attr StoredMessageAttribute - s.RLock() - err = s.db.Find(Sqlite3MessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(grpmsg.AttributeID, 10)) - s.RUnlock() - if err == nil { - ret.Attribute = &db.StoredMessageAttribute{ - MessageSeq: attr.MessageSeq, - InternalID: attr.InternalID, - SenderUin: attr.SenderUin, - SenderName: attr.SenderName, - Timestamp: attr.Timestamp, + if grpmsg.AttributeID != 0 { + var attr StoredMessageAttribute + s.RLock() + err = s.db.Find(Sqlite3MessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(grpmsg.AttributeID, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredMessageAttribute{ + MessageSeq: attr.MessageSeq, + InternalID: attr.InternalID, + SenderUin: attr.SenderUin, + SenderName: attr.SenderName, + Timestamp: attr.Timestamp, + } } } - var quoinf QuotedInfo - s.RLock() - err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(grpmsg.QuotedInfoID, 10)) - s.RUnlock() - if err == nil { - ret.QuotedInfo = &db.QuotedInfo{ - PrevID: quoinf.PrevID, - PrevGlobalID: quoinf.PrevGlobalID, + if grpmsg.QuotedInfoID != 0 { + var quoinf QuotedInfo + s.RLock() + err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(grpmsg.QuotedInfoID, 10)) + s.RUnlock() + if err == nil { + ret.QuotedInfo = &db.QuotedInfo{ + PrevID: quoinf.PrevID, + PrevGlobalID: quoinf.PrevGlobalID, + } + _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) } - _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) } return &ret, nil } @@ -115,29 +119,33 @@ func (s *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessa ret.SessionUin = privmsg.SessionUin ret.TargetUin = privmsg.TargetUin _ = json.Unmarshal(utils.S2B(privmsg.Content), &ret.Content) - var attr StoredMessageAttribute - s.RLock() - err = s.db.Find(Sqlite3MessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(privmsg.AttributeID, 10)) - s.RUnlock() - if err == nil { - ret.Attribute = &db.StoredMessageAttribute{ - MessageSeq: attr.MessageSeq, - InternalID: attr.InternalID, - SenderUin: attr.SenderUin, - SenderName: attr.SenderName, - Timestamp: attr.Timestamp, + if privmsg.AttributeID != 0 { + var attr StoredMessageAttribute + s.RLock() + err = s.db.Find(Sqlite3MessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(privmsg.AttributeID, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredMessageAttribute{ + MessageSeq: attr.MessageSeq, + InternalID: attr.InternalID, + SenderUin: attr.SenderUin, + SenderName: attr.SenderName, + Timestamp: attr.Timestamp, + } } } - var quoinf QuotedInfo - s.RLock() - err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(privmsg.QuotedInfoID, 10)) - s.RUnlock() - if err == nil { - ret.QuotedInfo = &db.QuotedInfo{ - PrevID: quoinf.PrevID, - PrevGlobalID: quoinf.PrevGlobalID, + if privmsg.QuotedInfoID != 0 { + var quoinf QuotedInfo + s.RLock() + err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(privmsg.QuotedInfoID, 10)) + s.RUnlock() + if err == nil { + ret.QuotedInfo = &db.QuotedInfo{ + PrevID: quoinf.PrevID, + PrevGlobalID: quoinf.PrevGlobalID, + } + _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) } - _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) } return &ret, nil } @@ -155,29 +163,33 @@ func (s *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannel ret.GuildID = uint64(guildmsg.GuildID) ret.ChannelID = uint64(guildmsg.ChannelID) _ = json.Unmarshal(utils.S2B(guildmsg.Content), &ret.Content) - var attr StoredGuildMessageAttribute - s.RLock() - err = s.db.Find(Sqlite3GuildMessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(guildmsg.AttributeID, 10)) - s.RUnlock() - if err == nil { - ret.Attribute = &db.StoredGuildMessageAttribute{ - MessageSeq: uint64(attr.MessageSeq), - InternalID: uint64(attr.InternalID), - SenderTinyID: uint64(attr.SenderTinyID), - SenderName: attr.SenderName, - Timestamp: attr.Timestamp, + if guildmsg.AttributeID != 0 { + var attr StoredGuildMessageAttribute + s.RLock() + err = s.db.Find(Sqlite3GuildMessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(guildmsg.AttributeID, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredGuildMessageAttribute{ + MessageSeq: uint64(attr.MessageSeq), + InternalID: uint64(attr.InternalID), + SenderTinyID: uint64(attr.SenderTinyID), + SenderName: attr.SenderName, + Timestamp: attr.Timestamp, + } } } - var quoinf QuotedInfo - s.RLock() - err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(guildmsg.QuotedInfoID, 10)) - s.RUnlock() - if err == nil { - ret.QuotedInfo = &db.QuotedInfo{ - PrevID: quoinf.PrevID, - PrevGlobalID: quoinf.PrevGlobalID, + if guildmsg.QuotedInfoID != 0 { + var quoinf QuotedInfo + s.RLock() + err = s.db.Find(Sqlite3QuotedInfoTableName, &quoinf, "WHERE ID="+strconv.FormatInt(guildmsg.QuotedInfoID, 10)) + s.RUnlock() + if err == nil { + ret.QuotedInfo = &db.QuotedInfo{ + PrevID: quoinf.PrevID, + PrevGlobalID: quoinf.PrevGlobalID, + } + _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) } - _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) } return &ret, nil } @@ -200,6 +212,9 @@ func (s *database) InsertGroupMessage(msg *db.StoredGroupMessage) error { })) h.Write(utils.S2B(msg.Attribute.SenderName)) id := int64(h.Sum64()) + if id == 0 { + id++ + } s.Lock() err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{ ID: id, @@ -226,6 +241,9 @@ func (s *database) InsertGroupMessage(msg *db.StoredGroupMessage) error { } h.Write(content) id := int64(h.Sum64()) + if id == 0 { + id++ + } s.Lock() err = s.db.Insert(Sqlite3QuotedInfoTableName, &QuotedInfo{ ID: id, @@ -270,6 +288,9 @@ func (s *database) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { })) h.Write(utils.S2B(msg.Attribute.SenderName)) id := int64(h.Sum64()) + if id == 0 { + id++ + } s.Lock() err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{ ID: id, @@ -296,6 +317,9 @@ func (s *database) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { } h.Write(content) id := int64(h.Sum64()) + if id == 0 { + id++ + } s.Lock() err = s.db.Insert(Sqlite3QuotedInfoTableName, &QuotedInfo{ ID: id, @@ -338,6 +362,9 @@ func (s *database) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) })) h.Write(utils.S2B(msg.Attribute.SenderName)) id := int64(h.Sum64()) + if id == 0 { + id++ + } s.Lock() err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredGuildMessageAttribute{ ID: id, @@ -364,6 +391,9 @@ func (s *database) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) } h.Write(content) id := int64(h.Sum64()) + if id == 0 { + id++ + } s.Lock() err = s.db.Insert(Sqlite3QuotedInfoTableName, &QuotedInfo{ ID: id, From fdfae87e9734d14904c87cd6b72943029671a017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 20:06:07 +0800 Subject: [PATCH 467/612] fix: create table --- db/sqlite3/sqlite3.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index c550468f3..3dd80274f 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -48,6 +48,30 @@ func (s *database) Open() error { if err != nil { return errors.Wrap(err, "open sqlite3 error") } + err = s.db.Create(Sqlite3GroupMessageTableName, &StoredGroupMessage{}) + if err != nil { + return errors.Wrap(err, "create sqlite3 table error") + } + err = s.db.Create(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{}) + if err != nil { + return errors.Wrap(err, "create sqlite3 table error") + } + err = s.db.Create(Sqlite3GuildMessageAttributeTableName, &StoredGuildMessageAttribute{}) + if err != nil { + return errors.Wrap(err, "create sqlite3 table error") + } + err = s.db.Create(Sqlite3QuotedInfoTableName, &QuotedInfo{}) + if err != nil { + return errors.Wrap(err, "create sqlite3 table error") + } + err = s.db.Create(Sqlite3PrivateMessageTableName, &StoredPrivateMessage{}) + if err != nil { + return errors.Wrap(err, "create sqlite3 table error") + } + err = s.db.Create(Sqlite3GuildChannelMessageTableName, &StoredGuildChannelMessage{}) + if err != nil { + return errors.Wrap(err, "create sqlite3 table error") + } return nil } From 3d3d19c5931115e14f0d047e676416785c0c6748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 20:32:17 +0800 Subject: [PATCH 468/612] fix: dbpath --- db/sqlite3/sqlite3.go | 1 + 1 file changed, 1 insertion(+) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index 3dd80274f..e14683366 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -44,6 +44,7 @@ func init() { func (s *database) Open() error { s.db.DBPath = path.Join("data", "sqlite3") _ = os.MkdirAll(s.db.DBPath, 0755) + s.db.DBPath += "/msg.db" err := s.db.Open(s.ttl) if err != nil { return errors.Wrap(err, "open sqlite3 error") From 481a7ce8aac424cddcb629e3d7a4de477069fd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 20:47:35 +0800 Subject: [PATCH 469/612] feat: use sqlite3 by default --- main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 1d7970454..008e5d0f0 100644 --- a/main.go +++ b/main.go @@ -4,9 +4,10 @@ package main import ( "github.com/Mrs4s/go-cqhttp/cmd/gocq" - _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb + _ "github.com/Mrs4s/go-cqhttp/db/sqlite3" // sqlite3 数据库支持 _ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块 // 其他模块 + // _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb 数据库支持 // _ "github.com/Mrs4s/go-cqhttp/db/mongodb" // mongodb 数据库支持 // _ "github.com/Mrs4s/go-cqhttp/modules/pprof" // pprof 性能分析 ) From e6fa400e0521bd3600e8b5cd8b6601206bf9fdf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 20:53:59 +0800 Subject: [PATCH 470/612] fix: no impl on winarm --- db/sqlite3/leveldb_winarm.go | 6 ++++++ db/sqlite3/model.go | 3 +++ db/sqlite3/sqlite3.go | 3 +++ 3 files changed, 12 insertions(+) create mode 100644 db/sqlite3/leveldb_winarm.go diff --git a/db/sqlite3/leveldb_winarm.go b/db/sqlite3/leveldb_winarm.go new file mode 100644 index 000000000..2512bc1ea --- /dev/null +++ b/db/sqlite3/leveldb_winarm.go @@ -0,0 +1,6 @@ +//go:build (windows && arm) || (windows && arm64) +// +build windows,arm windows,arm64 + +package sqlite3 + +import _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // 切换到 leveldb diff --git a/db/sqlite3/model.go b/db/sqlite3/model.go index 0afb8e1f1..be72c8a4a 100644 --- a/db/sqlite3/model.go +++ b/db/sqlite3/model.go @@ -1,3 +1,6 @@ +//go:build !((windows || arm) && (windows || arm64)) +// +build !windows,!arm !windows,!arm64 + package sqlite3 const ( diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index e14683366..78c9d6e38 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -1,3 +1,6 @@ +//go:build !((windows || arm) && (windows || arm64)) +// +build !windows,!arm !windows,!arm64 + package sqlite3 import ( From cee4bccf45025c19fa1ea380c388e8f2b36226db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 20:58:53 +0800 Subject: [PATCH 471/612] fix: no impl on winarm --- db/sqlite3/leveldb_winarm.go | 5 +++-- db/sqlite3/model.go | 4 ++-- db/sqlite3/sqlite3.go | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/db/sqlite3/leveldb_winarm.go b/db/sqlite3/leveldb_winarm.go index 2512bc1ea..eaae20149 100644 --- a/db/sqlite3/leveldb_winarm.go +++ b/db/sqlite3/leveldb_winarm.go @@ -1,5 +1,6 @@ -//go:build (windows && arm) || (windows && arm64) -// +build windows,arm windows,arm64 +//go:build windows && (arm || arm64) +// +build windows +// +build arm arm64 package sqlite3 diff --git a/db/sqlite3/model.go b/db/sqlite3/model.go index be72c8a4a..93e8a1770 100644 --- a/db/sqlite3/model.go +++ b/db/sqlite3/model.go @@ -1,5 +1,5 @@ -//go:build !((windows || arm) && (windows || arm64)) -// +build !windows,!arm !windows,!arm64 +//go:build !(windows && (arm || arm64)) +// +build !windows !arm,!arm64 package sqlite3 diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index 78c9d6e38..7d3169b1f 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -1,5 +1,5 @@ -//go:build !((windows || arm) && (windows || arm64)) -// +build !windows,!arm !windows,!arm64 +//go:build !(windows && (arm || arm64)) +// +build !windows !arm,!arm64 package sqlite3 From fc51a69ff191c7d239d9937e8965d6de675dc3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 21:10:20 +0800 Subject: [PATCH 472/612] fix: possible sql inject --- db/sqlite3/sqlite3.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index 7d3169b1f..a6b6a3bfe 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -4,6 +4,7 @@ package sqlite3 import ( + "encoding/base64" "encoding/json" "hash/crc64" "os" @@ -179,10 +180,14 @@ func (s *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessa } func (s *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { + _, err := base64.StdEncoding.DecodeString(id) + if err != nil { + return nil, errors.Wrap(err, "query invalid id error") + } var ret db.StoredGuildChannelMessage var guildmsg StoredGuildChannelMessage s.RLock() - err := s.db.Find(Sqlite3GuildChannelMessageTableName, &guildmsg, "WHERE ID='"+id+"'") + err = s.db.Find(Sqlite3GuildChannelMessageTableName, &guildmsg, "WHERE ID='"+id+"'") s.RUnlock() if err != nil { return nil, errors.Wrap(err, "query error") From bc80944f269715209f2b2acf8749a501de2de962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 21:11:23 +0800 Subject: [PATCH 473/612] fix: possible sql inject --- db/sqlite3/sqlite3.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index a6b6a3bfe..bd6822d5c 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -180,10 +180,13 @@ func (s *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessa } func (s *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannelMessage, error) { - _, err := base64.StdEncoding.DecodeString(id) + b, err := base64.StdEncoding.DecodeString(id) if err != nil { return nil, errors.Wrap(err, "query invalid id error") } + if len(b) < 25 { + return nil, errors.New("query invalid id error: content too short") + } var ret db.StoredGuildChannelMessage var guildmsg StoredGuildChannelMessage s.RLock() From 0ad641aa2d492cb99cdb1638884d9adbfabb9f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 9 Nov 2022 22:58:37 +0800 Subject: [PATCH 474/612] feat: change db to sqlite3 in default config --- modules/config/default_config.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 7d2f7e977..767706eac 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -73,11 +73,12 @@ default-middlewares: &default bucket: 1 # 令牌桶大小 database: # 数据库相关设置 - leveldb: - # 是否启用内置leveldb数据库 - # 启用将会增加10-20MB的内存占用和一定的磁盘空间 + sqlite3: + # 是否启用内置sqlite3数据库 + # 启用将会增加一定的内存占用和一定的磁盘空间 # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 enable: true + cachettl: 3600000000000 # 1h # 连接服务列表 servers: From 2d42a968c9aba43df985a45f22c82ee0ba07b0ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 10 Nov 2022 10:32:57 +0800 Subject: [PATCH 475/612] fix: interface conv error --- global/param.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global/param.go b/global/param.go index f23879a9d..44225bd81 100644 --- a/global/param.go +++ b/global/param.go @@ -8,7 +8,7 @@ import ( ) // MSG 消息Map -type MSG map[string]interface{} +type MSG = map[string]interface{} // VersionNameCompare 检查版本名是否需要更新, 仅适用于 go-cqhttp 的版本命名规则 // From f1957e3814aeb111fdd7b988dc3834b4ce637c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 10 Nov 2022 11:51:01 +0800 Subject: [PATCH 476/612] =?UTF-8?q?feat:=20=E5=B0=86=20uin,=20tiny=20?= =?UTF-8?q?=E5=88=86=E8=A1=A8=E4=BB=A5=E8=8A=82=E7=9C=81=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/sqlite3/model.go | 16 +++++- db/sqlite3/sqlite3.go | 119 ++++++++++++++++++++++++++++-------------- 2 files changed, 94 insertions(+), 41 deletions(-) diff --git a/db/sqlite3/model.go b/db/sqlite3/model.go index 93e8a1770..71f1bfb4f 100644 --- a/db/sqlite3/model.go +++ b/db/sqlite3/model.go @@ -10,6 +10,8 @@ const ( Sqlite3QuotedInfoTableName = "quoinf" Sqlite3PrivateMessageTableName = "privmsg" Sqlite3GuildChannelMessageTableName = "guildmsg" + Sqlite3UinInfoTableName = "uininf" + Sqlite3TinyInfoTableName = "tinyinf" ) // StoredMessageAttribute 持久化消息属性 @@ -18,7 +20,6 @@ type StoredMessageAttribute struct { MessageSeq int32 InternalID int32 SenderUin int64 - SenderName string Timestamp int64 } @@ -28,7 +29,6 @@ type StoredGuildMessageAttribute struct { MessageSeq int64 InternalID int64 SenderTinyID int64 - SenderName string Timestamp int64 } @@ -40,6 +40,18 @@ type QuotedInfo struct { QuotedContent string // QuotedContent is json of original content } +// UinInfo QQ 与 昵称 +type UinInfo struct { + Uin int64 + Name string +} + +// TinyInfo Tiny 与 昵称 +type TinyInfo struct { + ID int64 + Name string +} + // StoredGroupMessage 持久化群消息 type StoredGroupMessage struct { GlobalID int32 diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index bd6822d5c..c80c82a38 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -77,6 +77,14 @@ func (s *database) Open() error { if err != nil { return errors.Wrap(err, "create sqlite3 table error") } + err = s.db.Create(Sqlite3UinInfoTableName, &UinInfo{}) + if err != nil { + return errors.Wrap(err, "create sqlite3 table error") + } + err = s.db.Create(Sqlite3TinyInfoTableName, &TinyInfo{}) + if err != nil { + return errors.Wrap(err, "create sqlite3 table error") + } return nil } @@ -108,12 +116,18 @@ func (s *database) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, err = s.db.Find(Sqlite3MessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(grpmsg.AttributeID, 10)) s.RUnlock() if err == nil { - ret.Attribute = &db.StoredMessageAttribute{ - MessageSeq: attr.MessageSeq, - InternalID: attr.InternalID, - SenderUin: attr.SenderUin, - SenderName: attr.SenderName, - Timestamp: attr.Timestamp, + var uin UinInfo + s.RLock() + err = s.db.Find(Sqlite3UinInfoTableName, &attr, "WHERE Uin="+strconv.FormatInt(attr.SenderUin, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredMessageAttribute{ + MessageSeq: attr.MessageSeq, + InternalID: attr.InternalID, + SenderUin: attr.SenderUin, + SenderName: uin.Name, + Timestamp: attr.Timestamp, + } } } } @@ -154,12 +168,18 @@ func (s *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessa err = s.db.Find(Sqlite3MessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(privmsg.AttributeID, 10)) s.RUnlock() if err == nil { - ret.Attribute = &db.StoredMessageAttribute{ - MessageSeq: attr.MessageSeq, - InternalID: attr.InternalID, - SenderUin: attr.SenderUin, - SenderName: attr.SenderName, - Timestamp: attr.Timestamp, + var uin UinInfo + s.RLock() + err = s.db.Find(Sqlite3UinInfoTableName, &attr, "WHERE Uin="+strconv.FormatInt(attr.SenderUin, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredMessageAttribute{ + MessageSeq: attr.MessageSeq, + InternalID: attr.InternalID, + SenderUin: attr.SenderUin, + SenderName: uin.Name, + Timestamp: attr.Timestamp, + } } } } @@ -205,12 +225,18 @@ func (s *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannel err = s.db.Find(Sqlite3GuildMessageAttributeTableName, &attr, "WHERE ID="+strconv.FormatInt(guildmsg.AttributeID, 10)) s.RUnlock() if err == nil { - ret.Attribute = &db.StoredGuildMessageAttribute{ - MessageSeq: uint64(attr.MessageSeq), - InternalID: uint64(attr.InternalID), - SenderTinyID: uint64(attr.SenderTinyID), - SenderName: attr.SenderName, - Timestamp: attr.Timestamp, + var tiny TinyInfo + s.RLock() + err = s.db.Find(Sqlite3TinyInfoTableName, &attr, "WHERE ID="+strconv.FormatInt(attr.SenderTinyID, 10)) + s.RUnlock() + if err == nil { + ret.Attribute = &db.StoredGuildMessageAttribute{ + MessageSeq: uint64(attr.MessageSeq), + InternalID: uint64(attr.InternalID), + SenderTinyID: uint64(attr.SenderTinyID), + SenderName: tiny.Name, + Timestamp: attr.Timestamp, + } } } } @@ -252,14 +278,19 @@ func (s *database) InsertGroupMessage(msg *db.StoredGroupMessage) error { id++ } s.Lock() - err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{ - ID: id, - MessageSeq: msg.Attribute.MessageSeq, - InternalID: msg.Attribute.InternalID, - SenderUin: msg.Attribute.SenderUin, - SenderName: msg.Attribute.SenderName, - Timestamp: msg.Attribute.Timestamp, + err := s.db.Insert(Sqlite3UinInfoTableName, &UinInfo{ + Uin: msg.Attribute.SenderUin, + Name: msg.Attribute.SenderName, }) + if err == nil { + err = s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{ + ID: id, + MessageSeq: msg.Attribute.MessageSeq, + InternalID: msg.Attribute.InternalID, + SenderUin: msg.Attribute.SenderUin, + Timestamp: msg.Attribute.Timestamp, + }) + } s.Unlock() if err == nil { grpmsg.AttributeID = id @@ -328,14 +359,19 @@ func (s *database) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { id++ } s.Lock() - err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{ - ID: id, - MessageSeq: msg.Attribute.MessageSeq, - InternalID: msg.Attribute.InternalID, - SenderUin: msg.Attribute.SenderUin, - SenderName: msg.Attribute.SenderName, - Timestamp: msg.Attribute.Timestamp, + err := s.db.Insert(Sqlite3UinInfoTableName, &UinInfo{ + Uin: msg.Attribute.SenderUin, + Name: msg.Attribute.SenderName, }) + if err == nil { + err = s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{ + ID: id, + MessageSeq: msg.Attribute.MessageSeq, + InternalID: msg.Attribute.InternalID, + SenderUin: msg.Attribute.SenderUin, + Timestamp: msg.Attribute.Timestamp, + }) + } s.Unlock() if err == nil { privmsg.AttributeID = id @@ -402,14 +438,19 @@ func (s *database) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) id++ } s.Lock() - err := s.db.Insert(Sqlite3MessageAttributeTableName, &StoredGuildMessageAttribute{ - ID: id, - MessageSeq: int64(msg.Attribute.MessageSeq), - InternalID: int64(msg.Attribute.InternalID), - SenderTinyID: int64(msg.Attribute.SenderTinyID), - SenderName: msg.Attribute.SenderName, - Timestamp: msg.Attribute.Timestamp, + err := s.db.Insert(Sqlite3TinyInfoTableName, &TinyInfo{ + ID: int64(msg.Attribute.SenderTinyID), + Name: msg.Attribute.SenderName, }) + if err == nil { + err = s.db.Insert(Sqlite3MessageAttributeTableName, &StoredGuildMessageAttribute{ + ID: id, + MessageSeq: int64(msg.Attribute.MessageSeq), + InternalID: int64(msg.Attribute.InternalID), + SenderTinyID: int64(msg.Attribute.SenderTinyID), + Timestamp: msg.Attribute.Timestamp, + }) + } s.Unlock() if err == nil { guildmsg.AttributeID = id From 8da043f01215ff705086c747a17433af6d7dc9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 10 Nov 2022 14:42:31 +0800 Subject: [PATCH 477/612] feat: add foreign key constraints --- db/sqlite3/sqlite3.go | 33 +++++++++++++++++++++++++-------- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index c80c82a38..bcba4ed94 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -53,35 +53,52 @@ func (s *database) Open() error { if err != nil { return errors.Wrap(err, "open sqlite3 error") } - err = s.db.Create(Sqlite3GroupMessageTableName, &StoredGroupMessage{}) + _, err = s.db.DB.Exec("PRAGMA foreign_keys = ON;") + if err != nil { + return errors.Wrap(err, "enable foreign_keys error") + } + err = s.db.Create(Sqlite3UinInfoTableName, &UinInfo{}) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } - err = s.db.Create(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{}) + err = s.db.Create(Sqlite3TinyInfoTableName, &TinyInfo{}) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } - err = s.db.Create(Sqlite3GuildMessageAttributeTableName, &StoredGuildMessageAttribute{}) + err = s.db.Create(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{}, + "FOREIGN KEY(SenderUin) REFERENCES "+Sqlite3UinInfoTableName+"(Uin)", + ) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } - err = s.db.Create(Sqlite3QuotedInfoTableName, &QuotedInfo{}) + err = s.db.Create(Sqlite3GuildMessageAttributeTableName, &StoredGuildMessageAttribute{}, + "FOREIGN KEY(SenderTinyID) REFERENCES "+Sqlite3TinyInfoTableName+"(ID)", + ) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } - err = s.db.Create(Sqlite3PrivateMessageTableName, &StoredPrivateMessage{}) + err = s.db.Create(Sqlite3QuotedInfoTableName, &QuotedInfo{}) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } - err = s.db.Create(Sqlite3GuildChannelMessageTableName, &StoredGuildChannelMessage{}) + err = s.db.Create(Sqlite3GroupMessageTableName, &StoredGroupMessage{}, + "FOREIGN KEY(AttributeID) REFERENCES "+Sqlite3MessageAttributeTableName+"(ID)", + "FOREIGN KEY(QuotedInfoID) REFERENCES "+Sqlite3QuotedInfoTableName+"(ID)", + ) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } - err = s.db.Create(Sqlite3UinInfoTableName, &UinInfo{}) + err = s.db.Create(Sqlite3PrivateMessageTableName, &StoredPrivateMessage{}, + "FOREIGN KEY(AttributeID) REFERENCES "+Sqlite3MessageAttributeTableName+"(ID)", + "FOREIGN KEY(QuotedInfoID) REFERENCES "+Sqlite3QuotedInfoTableName+"(ID)", + ) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } - err = s.db.Create(Sqlite3TinyInfoTableName, &TinyInfo{}) + err = s.db.Create(Sqlite3GuildChannelMessageTableName, &StoredGuildChannelMessage{}, + "FOREIGN KEY(AttributeID) REFERENCES "+Sqlite3MessageAttributeTableName+"(ID)", + "FOREIGN KEY(QuotedInfoID) REFERENCES "+Sqlite3QuotedInfoTableName+"(ID)", + ) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } diff --git a/go.mod b/go.mod index 8bc52adc5..01f64afb2 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Mrs4s/go-cqhttp go 1.19 require ( - github.com/FloatTech/sqlite v0.5.0 + github.com/FloatTech/sqlite v0.5.1 github.com/Microsoft/go-winio v0.5.1 github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c diff --git a/go.sum b/go.sum index c4a4423ae..7745439e0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/FloatTech/sqlite v0.5.0 h1:U7J5Omc534PqmH6csfu+ypCo3DS8L91l5lTsxUu3b/U= -github.com/FloatTech/sqlite v0.5.0/go.mod h1:i33d92OtR8jcp5fBUvQtospf27+MkfUxnGwnZ95E/dA= +github.com/FloatTech/sqlite v0.5.1 h1:IjTdnqMVIVIoIEFXhvh/KKBfYxFvG0tk7Rghz65/DAU= +github.com/FloatTech/sqlite v0.5.1/go.mod h1:i33d92OtR8jcp5fBUvQtospf27+MkfUxnGwnZ95E/dA= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= From 1de1deb059015bdce76171eb5567210acf47ca56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 10 Nov 2022 15:00:15 +0800 Subject: [PATCH 478/612] fix foreign key constraints --- db/sqlite3/model.go | 4 ++-- db/sqlite3/sqlite3.go | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/db/sqlite3/model.go b/db/sqlite3/model.go index 71f1bfb4f..29e1483f6 100644 --- a/db/sqlite3/model.go +++ b/db/sqlite3/model.go @@ -19,7 +19,7 @@ type StoredMessageAttribute struct { ID int64 // ID is the crc64 of 字段s below MessageSeq int32 InternalID int32 - SenderUin int64 + SenderUin int64 // SenderUin is fk to UinInfo Timestamp int64 } @@ -28,7 +28,7 @@ type StoredGuildMessageAttribute struct { ID int64 // ID is the crc64 of 字段s below MessageSeq int64 InternalID int64 - SenderTinyID int64 + SenderTinyID int64 // SenderTinyID is fk to TinyInfo Timestamp int64 } diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index bcba4ed94..3ff39bb4d 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -61,26 +61,46 @@ func (s *database) Open() error { if err != nil { return errors.Wrap(err, "create sqlite3 table error") } + err = s.db.Insert(Sqlite3UinInfoTableName, &UinInfo{Name: "null"}) + if err != nil { + return errors.Wrap(err, "insert into sqlite3 table "+Sqlite3UinInfoTableName+" error") + } err = s.db.Create(Sqlite3TinyInfoTableName, &TinyInfo{}) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } + err = s.db.Insert(Sqlite3TinyInfoTableName, &TinyInfo{Name: "null"}) + if err != nil { + return errors.Wrap(err, "insert into sqlite3 table "+Sqlite3TinyInfoTableName+" error") + } err = s.db.Create(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{}, "FOREIGN KEY(SenderUin) REFERENCES "+Sqlite3UinInfoTableName+"(Uin)", ) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } + err = s.db.Insert(Sqlite3MessageAttributeTableName, &StoredMessageAttribute{}) + if err != nil { + return errors.Wrap(err, "insert into sqlite3 table "+Sqlite3MessageAttributeTableName+" error") + } err = s.db.Create(Sqlite3GuildMessageAttributeTableName, &StoredGuildMessageAttribute{}, "FOREIGN KEY(SenderTinyID) REFERENCES "+Sqlite3TinyInfoTableName+"(ID)", ) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } + err = s.db.Insert(Sqlite3GuildMessageAttributeTableName, &StoredGuildMessageAttribute{}) + if err != nil { + return errors.Wrap(err, "insert into sqlite3 table "+Sqlite3GuildMessageAttributeTableName+" error") + } err = s.db.Create(Sqlite3QuotedInfoTableName, &QuotedInfo{}) if err != nil { return errors.Wrap(err, "create sqlite3 table error") } + err = s.db.Insert(Sqlite3QuotedInfoTableName, &QuotedInfo{QuotedContent: "null"}) + if err != nil { + return errors.Wrap(err, "insert into sqlite3 table "+Sqlite3QuotedInfoTableName+" error") + } err = s.db.Create(Sqlite3GroupMessageTableName, &StoredGroupMessage{}, "FOREIGN KEY(AttributeID) REFERENCES "+Sqlite3MessageAttributeTableName+"(ID)", "FOREIGN KEY(QuotedInfoID) REFERENCES "+Sqlite3QuotedInfoTableName+"(ID)", From a6613d88bf72b071bc665c0fe42e3f5bc76fb2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 10 Nov 2022 16:31:14 +0800 Subject: [PATCH 479/612] fix: uin extract --- .gitignore | 3 +++ db/sqlite3/sqlite3.go | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 9e66494d5..4066bc3ec 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ internal/btree/*.db # binary builds go-cqhttp + +# macos +.DS_Store diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index 3ff39bb4d..68522ea3a 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -155,7 +155,7 @@ func (s *database) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, if err == nil { var uin UinInfo s.RLock() - err = s.db.Find(Sqlite3UinInfoTableName, &attr, "WHERE Uin="+strconv.FormatInt(attr.SenderUin, 10)) + err = s.db.Find(Sqlite3UinInfoTableName, &uin, "WHERE Uin="+strconv.FormatInt(attr.SenderUin, 10)) s.RUnlock() if err == nil { ret.Attribute = &db.StoredMessageAttribute{ @@ -207,7 +207,7 @@ func (s *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessa if err == nil { var uin UinInfo s.RLock() - err = s.db.Find(Sqlite3UinInfoTableName, &attr, "WHERE Uin="+strconv.FormatInt(attr.SenderUin, 10)) + err = s.db.Find(Sqlite3UinInfoTableName, &uin, "WHERE Uin="+strconv.FormatInt(attr.SenderUin, 10)) s.RUnlock() if err == nil { ret.Attribute = &db.StoredMessageAttribute{ @@ -264,7 +264,7 @@ func (s *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannel if err == nil { var tiny TinyInfo s.RLock() - err = s.db.Find(Sqlite3TinyInfoTableName, &attr, "WHERE ID="+strconv.FormatInt(attr.SenderTinyID, 10)) + err = s.db.Find(Sqlite3TinyInfoTableName, &tiny, "WHERE ID="+strconv.FormatInt(attr.SenderTinyID, 10)) s.RUnlock() if err == nil { ret.Attribute = &db.StoredGuildMessageAttribute{ From 92d78839f18720b70a58df66ff3557efe24ce2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 10 Nov 2022 17:30:54 +0800 Subject: [PATCH 480/612] fix: interface convert panic --- coolq/cqcode.go | 28 +++++++++++++++-- db/database.go | 70 +++++++++++++++++++++---------------------- db/sqlite3/sqlite3.go | 25 ++++++++-------- 3 files changed, 72 insertions(+), 51 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index c241e3413..e93e50c4c 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -11,6 +11,7 @@ import ( "net/url" "os" "path" + "reflect" "runtime" "strconv" "strings" @@ -725,7 +726,17 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message flash = true } if t.(string) == "show" { - id = data["id"].(int32) + id := 0 + switch idn := data["id"].(type) { + case int32: + id = int(idn) + case int: + id = idn + case int64: + id = int(idn) + default: + id = int(reflect.ValueOf(data["id"]).Convert(reflect.TypeOf(0)).Int()) + } if id < 40000 || id >= 40006 { id = 40000 } @@ -753,7 +764,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message case "all": r = append(r, message.NewAt(0)) case "user": - r = append(r, message.NewAt(data["target"].(int64), data["display"].(string))) + r = append(r, message.NewAt(reflect.ValueOf(data["target"]).Int(), data["display"].(string))) default: continue } @@ -767,7 +778,18 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message ResId: data["id"].(string), }) case "face": - r = append(r, message.NewFace(data["id"].(int32))) + id := int32(0) + switch idn := data["id"].(type) { + case int32: + id = idn + case int: + id = int32(idn) + case int64: + id = int32(idn) + default: + id = int32(reflect.ValueOf(data["id"]).Convert(reflect.TypeOf(0)).Int()) + } + r = append(r, message.NewFace(id)) case "video": e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, true, sourceType) if err != nil { diff --git a/db/database.go b/db/database.go index c19da97f7..c9048226d 100644 --- a/db/database.go +++ b/db/database.go @@ -40,61 +40,61 @@ type ( // StoredGroupMessage 持久化群消息 StoredGroupMessage struct { - ID string `bson:"_id"` - GlobalID int32 `bson:"globalId"` - Attribute *StoredMessageAttribute `bson:"attribute"` - SubType string `bson:"subType"` - QuotedInfo *QuotedInfo `bson:"quotedInfo"` - GroupCode int64 `bson:"groupCode"` - AnonymousID string `bson:"anonymousId"` - Content []global.MSG `bson:"content"` + ID string `bson:"_id" yaml:"-"` + GlobalID int32 `bson:"globalId" yaml:"-"` + Attribute *StoredMessageAttribute `bson:"attribute" yaml:"-"` + SubType string `bson:"subType" yaml:"-"` + QuotedInfo *QuotedInfo `bson:"quotedInfo" yaml:"-"` + GroupCode int64 `bson:"groupCode" yaml:"-"` + AnonymousID string `bson:"anonymousId" yaml:"-"` + Content []global.MSG `bson:"content" yaml:"content"` } // StoredPrivateMessage 持久化私聊消息 StoredPrivateMessage struct { - ID string `bson:"_id"` - GlobalID int32 `bson:"globalId"` - Attribute *StoredMessageAttribute `bson:"attribute"` - SubType string `bson:"subType"` - QuotedInfo *QuotedInfo `bson:"quotedInfo"` - SessionUin int64 `bson:"sessionUin"` - TargetUin int64 `bson:"targetUin"` - Content []global.MSG `bson:"content"` + ID string `bson:"_id" yaml:"-"` + GlobalID int32 `bson:"globalId" yaml:"-"` + Attribute *StoredMessageAttribute `bson:"attribute" yaml:"-"` + SubType string `bson:"subType" yaml:"-"` + QuotedInfo *QuotedInfo `bson:"quotedInfo" yaml:"-"` + SessionUin int64 `bson:"sessionUin" yaml:"-"` + TargetUin int64 `bson:"targetUin" yaml:"-"` + Content []global.MSG `bson:"content" yaml:"content"` } // StoredGuildChannelMessage 持久化频道消息 StoredGuildChannelMessage struct { - ID string `bson:"_id"` - Attribute *StoredGuildMessageAttribute `bson:"attribute"` - GuildID uint64 `bson:"guildId"` - ChannelID uint64 `bson:"channelId"` - QuotedInfo *QuotedInfo `bson:"quotedInfo"` - Content []global.MSG `bson:"content"` + ID string `bson:"_id" yaml:"-"` + Attribute *StoredGuildMessageAttribute `bson:"attribute" yaml:"-"` + GuildID uint64 `bson:"guildId" yaml:"-"` + ChannelID uint64 `bson:"channelId" yaml:"-"` + QuotedInfo *QuotedInfo `bson:"quotedInfo" yaml:"-"` + Content []global.MSG `bson:"content" yaml:"content"` } // StoredMessageAttribute 持久化消息属性 StoredMessageAttribute struct { - MessageSeq int32 `bson:"messageSeq"` - InternalID int32 `bson:"internalId"` - SenderUin int64 `bson:"senderUin"` - SenderName string `bson:"senderName"` - Timestamp int64 `bson:"timestamp"` + MessageSeq int32 `bson:"messageSeq" yaml:"-"` + InternalID int32 `bson:"internalId" yaml:"-"` + SenderUin int64 `bson:"senderUin" yaml:"-"` + SenderName string `bson:"senderName" yaml:"-"` + Timestamp int64 `bson:"timestamp" yaml:"-"` } // StoredGuildMessageAttribute 持久化频道消息属性 StoredGuildMessageAttribute struct { - MessageSeq uint64 `bson:"messageSeq"` - InternalID uint64 `bson:"internalId"` - SenderTinyID uint64 `bson:"senderTinyId"` - SenderName string `bson:"senderName"` - Timestamp int64 `bson:"timestamp"` + MessageSeq uint64 `bson:"messageSeq" yaml:"-"` + InternalID uint64 `bson:"internalId" yaml:"-"` + SenderTinyID uint64 `bson:"senderTinyId" yaml:"-"` + SenderName string `bson:"senderName" yaml:"-"` + Timestamp int64 `bson:"timestamp" yaml:"-"` } // QuotedInfo 引用回复 QuotedInfo struct { - PrevID string `bson:"prevId"` - PrevGlobalID int32 `bson:"prevGlobalId"` - QuotedContent []global.MSG `bson:"quotedContent"` + PrevID string `bson:"prevId" yaml:"-"` + PrevGlobalID int32 `bson:"prevGlobalId" yaml:"-"` + QuotedContent []global.MSG `bson:"quotedContent" yaml:"quoted_content"` } ) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index 68522ea3a..4495a0650 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -5,7 +5,6 @@ package sqlite3 import ( "encoding/base64" - "encoding/json" "hash/crc64" "os" "path" @@ -146,7 +145,7 @@ func (s *database) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, ret.SubType = grpmsg.SubType ret.GroupCode = grpmsg.GroupCode ret.AnonymousID = grpmsg.AnonymousID - _ = json.Unmarshal(utils.S2B(grpmsg.Content), &ret.Content) + _ = yaml.Unmarshal(utils.S2B(grpmsg.Content), &ret) if grpmsg.AttributeID != 0 { var attr StoredMessageAttribute s.RLock() @@ -178,7 +177,7 @@ func (s *database) GetGroupMessageByGlobalID(id int32) (*db.StoredGroupMessage, PrevID: quoinf.PrevID, PrevGlobalID: quoinf.PrevGlobalID, } - _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) + _ = yaml.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo) } } return &ret, nil @@ -198,7 +197,7 @@ func (s *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessa ret.SubType = privmsg.SubType ret.SessionUin = privmsg.SessionUin ret.TargetUin = privmsg.TargetUin - _ = json.Unmarshal(utils.S2B(privmsg.Content), &ret.Content) + _ = yaml.Unmarshal(utils.S2B(privmsg.Content), &ret) if privmsg.AttributeID != 0 { var attr StoredMessageAttribute s.RLock() @@ -230,7 +229,7 @@ func (s *database) GetPrivateMessageByGlobalID(id int32) (*db.StoredPrivateMessa PrevID: quoinf.PrevID, PrevGlobalID: quoinf.PrevGlobalID, } - _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) + _ = yaml.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo) } } return &ret, nil @@ -255,7 +254,7 @@ func (s *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannel ret.ID = guildmsg.ID ret.GuildID = uint64(guildmsg.GuildID) ret.ChannelID = uint64(guildmsg.ChannelID) - _ = json.Unmarshal(utils.S2B(guildmsg.Content), &ret.Content) + _ = yaml.Unmarshal(utils.S2B(guildmsg.Content), &ret) if guildmsg.AttributeID != 0 { var attr StoredGuildMessageAttribute s.RLock() @@ -287,7 +286,7 @@ func (s *database) GetGuildChannelMessageByID(id string) (*db.StoredGuildChannel PrevID: quoinf.PrevID, PrevGlobalID: quoinf.PrevGlobalID, } - _ = json.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo.QuotedContent) + _ = yaml.Unmarshal(utils.S2B(quoinf.QuotedContent), &ret.QuotedInfo) } } return &ret, nil @@ -339,7 +338,7 @@ func (s *database) InsertGroupMessage(msg *db.StoredGroupMessage) error { h.Write(binary.NewWriterF(func(w *binary.Writer) { w.WriteUInt32(uint32(msg.QuotedInfo.PrevGlobalID)) })) - content, err := json.Marshal(&msg.QuotedInfo.QuotedContent) + content, err := yaml.Marshal(&msg.QuotedInfo) if err != nil { return errors.Wrap(err, "insert marshal QuotedContent error") } @@ -360,7 +359,7 @@ func (s *database) InsertGroupMessage(msg *db.StoredGroupMessage) error { grpmsg.QuotedInfoID = id } } - content, err := json.Marshal(&msg.Content) + content, err := yaml.Marshal(&msg) if err != nil { return errors.Wrap(err, "insert marshal Content error") } @@ -420,7 +419,7 @@ func (s *database) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { h.Write(binary.NewWriterF(func(w *binary.Writer) { w.WriteUInt32(uint32(msg.QuotedInfo.PrevGlobalID)) })) - content, err := json.Marshal(&msg.QuotedInfo.QuotedContent) + content, err := yaml.Marshal(&msg.QuotedInfo) if err != nil { return errors.Wrap(err, "insert marshal QuotedContent error") } @@ -441,7 +440,7 @@ func (s *database) InsertPrivateMessage(msg *db.StoredPrivateMessage) error { privmsg.QuotedInfoID = id } } - content, err := json.Marshal(&msg.Content) + content, err := yaml.Marshal(&msg) if err != nil { return errors.Wrap(err, "insert marshal Content error") } @@ -499,7 +498,7 @@ func (s *database) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) h.Write(binary.NewWriterF(func(w *binary.Writer) { w.WriteUInt32(uint32(msg.QuotedInfo.PrevGlobalID)) })) - content, err := json.Marshal(&msg.QuotedInfo.QuotedContent) + content, err := yaml.Marshal(&msg.QuotedInfo) if err != nil { return errors.Wrap(err, "insert marshal QuotedContent error") } @@ -520,7 +519,7 @@ func (s *database) InsertGuildChannelMessage(msg *db.StoredGuildChannelMessage) guildmsg.QuotedInfoID = id } } - content, err := json.Marshal(&msg.Content) + content, err := yaml.Marshal(&msg) if err != nil { return errors.Wrap(err, "insert marshal Content error") } From 51747981ddd3efb79014988d522f7ae034ce43c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 15 Nov 2022 10:50:47 +0800 Subject: [PATCH 481/612] =?UTF-8?q?feat:=20=E9=BB=98=E8=AE=A4=E4=BB=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20leveldb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 4 ++-- modules/config/default_config.yml | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 008e5d0f0..30838f77d 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,10 @@ package main import ( "github.com/Mrs4s/go-cqhttp/cmd/gocq" - _ "github.com/Mrs4s/go-cqhttp/db/sqlite3" // sqlite3 数据库支持 + _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb 数据库支持 _ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块 // 其他模块 - // _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb 数据库支持 + // _ "github.com/Mrs4s/go-cqhttp/db/sqlite3" // sqlite3 数据库支持 // _ "github.com/Mrs4s/go-cqhttp/db/mongodb" // mongodb 数据库支持 // _ "github.com/Mrs4s/go-cqhttp/modules/pprof" // pprof 性能分析 ) diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 767706eac..60d5db007 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -73,11 +73,16 @@ default-middlewares: &default bucket: 1 # 令牌桶大小 database: # 数据库相关设置 + leveldb: + # 是否启用内置leveldb数据库 + # 启用将会增加10-20MB的内存占用和一定的磁盘空间 + # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 + enable: true sqlite3: # 是否启用内置sqlite3数据库 # 启用将会增加一定的内存占用和一定的磁盘空间 # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 - enable: true + enable: false cachettl: 3600000000000 # 1h # 连接服务列表 From b981b45245b46923d7e12d489429b849d4082480 Mon Sep 17 00:00:00 2001 From: Shigma Date: Mon, 21 Nov 2022 19:55:26 +0800 Subject: [PATCH 482/612] feat: check if stdin isatty Co-Authored-By: Il Harper --- cmd/gocq/login.go | 17 +++++++++++++---- cmd/gocq/main.go | 4 ++-- go.mod | 3 ++- go.sum | 5 ++++- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index d963d2566..aaa31e351 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -16,6 +16,7 @@ import ( "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" + "gopkg.ilharper.com/x/isatty" "github.com/Mrs4s/go-cqhttp/global" ) @@ -44,6 +45,14 @@ func readLineTimeout(t time.Duration, de string) (str string) { return } +func readIfTTY(de string) (str string) { + if isatty.Isatty(os.Stdin.Fd()) { + return readLine() + } + log.Warnf("未检测到输入终端,自动选择%s.", de) + return de +} + var cli *client.QQClient // ErrSMSRequestError SMS请求出错 @@ -148,8 +157,8 @@ func loginResponseProcessor(res *client.LoginResponse) error { log.Warnf("登录需要滑条验证码, 请选择验证方式: ") log.Warnf("1. 使用浏览器抓取滑条并登录") log.Warnf("2. 使用手机QQ扫码验证 (需要手Q和gocq在同一网络下).") - log.Warn("请输入(1 - 2) (将在10秒后自动选择1):") - text = readLineTimeout(time.Second*10, "1") + log.Warn("请输入(1 - 2):") + text = readIfTTY("1") if strings.Contains(text, "1") { ticket := getTicket(res.VerifyUrl) if ticket == "" { @@ -185,8 +194,8 @@ func loginResponseProcessor(res *client.LoginResponse) error { log.Warnf("账号已开启设备锁,请选择验证方式:") log.Warnf("1. 向手机 %v 发送短信验证码", res.SMSPhone) log.Warnf("2. 使用手机QQ扫码验证.") - log.Warn("请输入(1 - 2) (将在10秒后自动选择2):") - text = readLineTimeout(time.Second*10, "2") + log.Warn("请输入(1 - 2):") + text = readIfTTY("2") if strings.Contains(text, "1") { if !cli.RequestSMS() { log.Warnf("发送验证码失败,可能是请求过于频繁.") diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index dacaead6c..5c25745dd 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -220,8 +220,8 @@ func Main() { log.Warnf("警告: 配置文件内的QQ号 (%v) 与缓存内的QQ号 (%v) 不相同", base.Account.Uin, cu) log.Warnf("1. 使用会话缓存继续.") log.Warnf("2. 删除会话缓存并重启.") - log.Warnf("请选择: (5秒后自动选1)") - text := readLineTimeout(time.Second*5, "1") + log.Warnf("请选择:") + text := readIfTTY("1") if text == "2" { _ = os.Remove("session.token") log.Infof("缓存已删除.") diff --git a/go.mod b/go.mod index db48f45ad..f5182d3d3 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 + gopkg.ilharper.com/x/isatty v1.1.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -48,7 +49,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect + golang.org/x/sys v0.2.0 // indirect golang.org/x/text v0.3.7 // indirect modernc.org/libc v1.8.1 // indirect modernc.org/mathutil v1.2.2 // indirect diff --git a/go.sum b/go.sum index d70e91683..92c889a61 100644 --- a/go.sum +++ b/go.sum @@ -145,8 +145,9 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -169,6 +170,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.ilharper.com/x/isatty v1.1.0 h1:slOK6hP9/y9mJWyCInMwnT432NExfWyYV2SsebdYOCY= +gopkg.ilharper.com/x/isatty v1.1.0/go.mod h1:ofpv77Td5qQO6R1dmDd3oNt8TZdRo+l5gYAMxopRyS0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 6f2bb1402f6bfd8ec9caca3220c9b15d8f6f33db Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 1 Dec 2022 17:17:24 +0800 Subject: [PATCH 483/612] update dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0de9c8b62..3b6bd5b34 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe + github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.5.2 diff --git a/go.sum b/go.sum index 47b2da4d0..9550d9cbc 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe h1:KpRbU5MRL2dplf01Cbgo8m0+ST1V1A+4cFBQkQA/YAk= -github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM= +github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e h1:PvTvu1c9R8XCDGysBYGF+TSO+UfX347NJkNblifC4C8= +github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM= github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0 h1:+UGPBYVjssFsdahLJIiNPwpmmwgl/OaVdv1oc5NonC0= github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= From 0248c86078edc09fa396f5e1bfc8bacc8fca3fd1 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 1 Dec 2022 18:09:18 +0800 Subject: [PATCH 484/612] fix #1733 --- coolq/cqcode/element.go | 4 +- coolq/event.go | 6 +- global/quote.go | 146 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 global/quote.go diff --git a/coolq/cqcode/element.go b/coolq/cqcode/element.go index 2605d5aa8..2788ba27e 100644 --- a/coolq/cqcode/element.go +++ b/coolq/cqcode/element.go @@ -2,10 +2,10 @@ package cqcode import ( "bytes" - "strconv" "strings" "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/go-cqhttp/global" ) // Element single message @@ -60,7 +60,7 @@ func (e *Element) MarshalJSON() ([]byte, error) { buf.WriteByte('"') buf.WriteString(data.K) buf.WriteString(`":`) - buf.WriteString(strconv.Quote(data.V)) + buf.WriteString(global.Quote(data.V)) } buf.WriteString(`}}`) }), nil diff --git a/coolq/event.go b/coolq/event.go index 34500e2da..02c4da624 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -61,7 +61,11 @@ func (ev *event) MarshalJSON() ([]byte, error) { fmt.Fprintf(buf, `,"sub_type":"%s"`, ev.SubType) } for k, v := range ev.Others { - v, _ := json.Marshal(v) + v, err := json.Marshal(v) + if err != nil { + log.Warnf("marshal message payload error: %v", err) + return nil, err + } fmt.Fprintf(buf, `,"%s":%s`, k, v) } buf.WriteByte('}') diff --git a/global/quote.go b/global/quote.go new file mode 100644 index 000000000..bad54b94d --- /dev/null +++ b/global/quote.go @@ -0,0 +1,146 @@ +package global + +import ( + "strconv" + "unicode/utf8" +) + +const ( + lowerhex = "0123456789abcdef" + upperhex = "0123456789ABCDEF" +) + +// Quote returns a double-quoted Go string literal representing s. The +// returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for +// control characters and non-printable characters as defined by +// IsPrint. +func Quote(s string) string { + return quoteWith(s, '"', false, false) +} + +func quoteWith(s string, quote byte, ASCIIonly, graphicOnly bool) string { + return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, ASCIIonly, graphicOnly)) +} + +func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly bool) []byte { + // Often called with big strings, so preallocate. If there's quoting, + // this is conservative but still helps a lot. + if cap(buf)-len(buf) < len(s) { + nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1) + copy(nBuf, buf) + buf = nBuf + } + buf = append(buf, quote) + for width := 0; len(s) > 0; s = s[width:] { + r := rune(s[0]) + width = 1 + if r >= utf8.RuneSelf { + r, width = utf8.DecodeRuneInString(s) + } + if width == 1 && r == utf8.RuneError { + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[0]>>4]) + buf = append(buf, lowerhex[s[0]&0xF]) + continue + } + buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly) + } + buf = append(buf, quote) + return buf +} +func appendEscapedRune(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte { + var runeTmp [utf8.UTFMax]byte + if r == rune(quote) || r == '\\' { // always backslashed + buf = append(buf, '\\') + buf = append(buf, byte(r)) + return buf + } + if ASCIIonly { + if r < utf8.RuneSelf && strconv.IsPrint(r) { + buf = append(buf, byte(r)) + return buf + } + } else if strconv.IsPrint(r) || graphicOnly && isInGraphicList(r) { + n := utf8.EncodeRune(runeTmp[:], r) + buf = append(buf, runeTmp[:n]...) + return buf + } + switch r { + case '\a': + buf = append(buf, `\a`...) + case '\b': + buf = append(buf, `\b`...) + case '\f': + buf = append(buf, `\f`...) + case '\n': + buf = append(buf, `\n`...) + case '\r': + buf = append(buf, `\r`...) + case '\t': + buf = append(buf, `\t`...) + case '\v': + buf = append(buf, `\v`...) + default: + switch { + case !utf8.ValidRune(r): + r = 0xFFFD + fallthrough + case r < 0x10000: + buf = append(buf, `\u`...) + for s := 12; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + default: + buf = append(buf, `\U`...) + for s := 28; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + } + } + return buf +} + +func isInGraphicList(r rune) bool { + // We know r must fit in 16 bits - see makeisprint.go. + if r > 0xFFFF { + return false + } + rr := uint16(r) + i := bsearch16(isGraphic, rr) + return i < len(isGraphic) && rr == isGraphic[i] +} + +// bsearch16 returns the smallest i such that a[i] >= x. +// If there is no such i, bsearch16 returns len(a). +func bsearch16(a []uint16, x uint16) int { + i, j := 0, len(a) + for i < j { + h := i + (j-i)>>1 + if a[h] < x { + i = h + 1 + } else { + j = h + } + } + return i +} + +// isGraphic lists the graphic runes not matched by IsPrint. +var isGraphic = []uint16{ + 0x00a0, + 0x1680, + 0x2000, + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200a, + 0x202f, + 0x205f, + 0x3000, +} From 4bebd4fec8be7a841f0fb24dd425eefda89151c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Fri, 2 Dec 2022 17:59:04 +0800 Subject: [PATCH 485/612] fix: update deps to modernc.org/sqlite --- go.mod | 69 ++++++++------ go.sum | 290 +++++++++++++++++++-------------------------------------- 2 files changed, 133 insertions(+), 226 deletions(-) diff --git a/go.mod b/go.mod index 01f64afb2..2d4d05ea4 100644 --- a/go.mod +++ b/go.mod @@ -3,57 +3,66 @@ module github.com/Mrs4s/go-cqhttp go 1.19 require ( - github.com/FloatTech/sqlite v0.5.1 - github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe - github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c + github.com/FloatTech/sqlite v1.5.4 + github.com/Microsoft/go-winio v0.6.0 + github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e + github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc - github.com/fumiama/go-base16384 v1.5.2 + github.com/fumiama/go-base16384 v1.6.1 github.com/fumiama/go-hide-param v0.1.4 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible - github.com/mattn/go-colorable v0.1.12 + github.com/mattn/go-colorable v0.1.13 github.com/pkg/errors v0.9.1 - github.com/segmentio/asm v1.1.3 - github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.8.0 + github.com/segmentio/asm v1.2.0 + github.com/sirupsen/logrus v1.9.0 + github.com/stretchr/testify v1.8.1 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tidwall/gjson v1.14.3 - github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 - go.mongodb.org/mongo-driver v1.8.3 - golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 - golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 + github.com/tidwall/gjson v1.14.4 + github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea + go.mongodb.org/mongo-driver v1.11.0 + golang.org/x/crypto v0.3.0 + golang.org/x/sys v0.2.0 + golang.org/x/term v0.2.0 + golang.org/x/time v0.2.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b // indirect - github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0 // indirect + github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect - github.com/fumiama/sqlite3 v1.14.6 // indirect - github.com/go-stack/stack v1.8.0 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.5 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/jonboulle/clockwork v0.2.2 // indirect + github.com/jonboulle/clockwork v0.3.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.13.6 // indirect - github.com/lestrrat-go/strftime v1.0.5 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/lestrrat-go/strftime v1.0.6 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.0.2 // indirect - github.com/xdg-go/stringprep v1.0.2 // indirect + github.com/xdg-go/scram v1.1.1 // indirect + github.com/xdg-go/stringprep v1.0.3 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect - golang.org/x/text v0.3.7 // indirect - modernc.org/libc v1.14.6 // indirect - modernc.org/mathutil v1.4.1 // indirect - modernc.org/memory v1.0.5 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/tools v0.1.12 // indirect + lukechampine.com/uint128 v1.2.0 // indirect + modernc.org/cc/v3 v3.40.0 // indirect + modernc.org/ccgo/v3 v3.16.13 // indirect + modernc.org/libc v1.21.5 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.4.0 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/sqlite v1.20.0 // indirect + modernc.org/strutil v1.1.3 // indirect + modernc.org/token v1.0.1 // indirect ) + +replace github.com/remyoudompheng/bigfft => github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b diff --git a/go.sum b/go.sum index 7745439e0..904e2d531 100644 --- a/go.sum +++ b/go.sum @@ -1,34 +1,32 @@ -github.com/FloatTech/sqlite v0.5.1 h1:IjTdnqMVIVIoIEFXhvh/KKBfYxFvG0tk7Rghz65/DAU= -github.com/FloatTech/sqlite v0.5.1/go.mod h1:i33d92OtR8jcp5fBUvQtospf27+MkfUxnGwnZ95E/dA= +github.com/FloatTech/sqlite v1.5.4 h1:f4HFyF+ziS3VdhhptdwFyVBGxgn2izRD6nQOBTKyl3I= +github.com/FloatTech/sqlite v1.5.4/go.mod h1:wiETjtOKD5l5CKDqDktlNL2DoZogngln7AE/jbNOu5Y= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= -github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe h1:KpRbU5MRL2dplf01Cbgo8m0+ST1V1A+4cFBQkQA/YAk= -github.com/Mrs4s/MiraiGo v0.0.0-20220828090150-a3c348100dfe/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM= -github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0 h1:+UGPBYVjssFsdahLJIiNPwpmmwgl/OaVdv1oc5NonC0= -github.com/RomiChan/protobuf v0.1.1-0.20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= -github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q= -github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e h1:PvTvu1c9R8XCDGysBYGF+TSO+UfX347NJkNblifC4C8= +github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM= +github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0 h1:GEwcB4dL9vc4veW1fLNt0Fby3wspVflAn5v9/HbUwDM= +github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= +github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= +github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fumiama/go-base16384 v1.5.2 h1:cbxXTcDH92PNgG7bEBwiCEoWb5O+nwZKxKOG94ilFo8= -github.com/fumiama/go-base16384 v1.5.2/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM= +github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b h1:Zt3pFQditAdWTHCOVkiloc9ZauBoWrb37guFV4iIRvE= +github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/fumiama/go-base16384 v1.6.1 h1:4yb4JgmBJDnQtq3XGXXdLrVwEnRpjhMUt4eAcsNeA30= +github.com/fumiama/go-base16384 v1.6.1/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4= -github.com/fumiama/sqlite3 v1.14.6 h1:+e+iygyiDXQJVi7xeXIviBvR7hAc5y20WA9hRwfKn10= -github.com/fumiama/sqlite3 v1.14.6/go.mod h1:Xx9a2/OtHuy9pBjow0N+bE/RhNeZ7zZz5xh25vqbA5A= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -43,14 +41,14 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= +github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -63,13 +61,15 @@ github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2t github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= -github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= -github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ= +github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -82,70 +82,61 @@ github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= -github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= -github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4= -github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= +github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea h1:sl1pYm1kHtIndckTY8YDt+QFt77vI0JnKHP0U8rZtKc= +github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4= -go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE= +go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= -golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -153,40 +144,35 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= +golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -208,119 +194,31 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= -modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= -modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= -modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= -modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= -modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= -modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= -modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= -modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= -modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= -modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= -modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= -modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= -modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= -modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= -modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= -modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= -modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= -modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= -modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= -modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= -modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= -modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= -modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= -modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= -modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= -modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= -modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= -modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= -modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= -modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= -modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= -modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= -modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= -modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= -modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= -modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= -modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4= -modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= -modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8= -modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I= -modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= +modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= +modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= +modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= -modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= -modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= -modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= -modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= -modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= -modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= -modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= -modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= -modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= -modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= -modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= -modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= -modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= -modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= -modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= -modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= -modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= -modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= -modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= -modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= -modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= -modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= -modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= -modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= -modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= -modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= -modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= -modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= -modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= -modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= -modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= -modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= -modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= -modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= -modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= -modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= -modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= -modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= -modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= -modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= -modernc.org/libc v1.14.6 h1:SSiZiE5199iYsGM9gtkDj90xqcXVwubWG8CtoYE+Mnk= -modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak= +modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI= +modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= -modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= +modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.20.0 h1:80zmD3BGkm8BZ5fUi/4lwJQHiO3GXgIUvZRXpoIfROY= +modernc.org/sqlite v1.20.0/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw= +modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34= +modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= +modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= From efb5e63c75d3aab4bd559026ba36eba4e75fad31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:04:38 +0800 Subject: [PATCH 486/612] update ft/sqlite --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2d4d05ea4..15bb3b1b8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Mrs4s/go-cqhttp go 1.19 require ( - github.com/FloatTech/sqlite v1.5.4 + github.com/FloatTech/sqlite v1.5.5 github.com/Microsoft/go-winio v0.6.0 github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e diff --git a/go.sum b/go.sum index 904e2d531..67be421b5 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/FloatTech/sqlite v1.5.4 h1:f4HFyF+ziS3VdhhptdwFyVBGxgn2izRD6nQOBTKyl3I= -github.com/FloatTech/sqlite v1.5.4/go.mod h1:wiETjtOKD5l5CKDqDktlNL2DoZogngln7AE/jbNOu5Y= +github.com/FloatTech/sqlite v1.5.5 h1:SmGE2MC2W7RQOYfWTau9ZdrfwUflOiLdobi9QgTiL0Y= +github.com/FloatTech/sqlite v1.5.5/go.mod h1:ItKoTpmEE2cYQ/hGrC02W+BBYE/tlUjlVGSTiM6ewFM= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= From 2e16533c819f5741f9f5760cf2dbaa9108a2059e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:13:34 +0800 Subject: [PATCH 487/612] fix: update ft/sqlite --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 15bb3b1b8..a0b7fc32e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Mrs4s/go-cqhttp go 1.19 require ( - github.com/FloatTech/sqlite v1.5.5 + github.com/FloatTech/sqlite v1.5.6 github.com/Microsoft/go-winio v0.6.0 github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e diff --git a/go.sum b/go.sum index 67be421b5..5a2479022 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/FloatTech/sqlite v1.5.5 h1:SmGE2MC2W7RQOYfWTau9ZdrfwUflOiLdobi9QgTiL0Y= -github.com/FloatTech/sqlite v1.5.5/go.mod h1:ItKoTpmEE2cYQ/hGrC02W+BBYE/tlUjlVGSTiM6ewFM= +github.com/FloatTech/sqlite v1.5.6 h1:Ws4uiN5AjjGV+ikM/cL8eOHHeYvX390srv8SaqPQ5Ok= +github.com/FloatTech/sqlite v1.5.6/go.mod h1:ItKoTpmEE2cYQ/hGrC02W+BBYE/tlUjlVGSTiM6ewFM= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= From 8f63750d7fba81053edff7a000f1f7437d39fa9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:53:50 +0800 Subject: [PATCH 488/612] fix: sqlite --- db/sqlite3/leveldb_winarm.go | 7 ------- db/sqlite3/model.go | 3 --- db/sqlite3/sqlite3.go | 4 +--- go.mod | 2 +- go.sum | 4 ++-- 5 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 db/sqlite3/leveldb_winarm.go diff --git a/db/sqlite3/leveldb_winarm.go b/db/sqlite3/leveldb_winarm.go deleted file mode 100644 index eaae20149..000000000 --- a/db/sqlite3/leveldb_winarm.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build windows && (arm || arm64) -// +build windows -// +build arm arm64 - -package sqlite3 - -import _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // 切换到 leveldb diff --git a/db/sqlite3/model.go b/db/sqlite3/model.go index 29e1483f6..286986567 100644 --- a/db/sqlite3/model.go +++ b/db/sqlite3/model.go @@ -1,6 +1,3 @@ -//go:build !(windows && (arm || arm64)) -// +build !windows !arm,!arm64 - package sqlite3 const ( diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index 4495a0650..50970579c 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -1,6 +1,3 @@ -//go:build !(windows && (arm || arm64)) -// +build !windows !arm,!arm64 - package sqlite3 import ( @@ -34,6 +31,7 @@ type config struct { } func init() { + sql.DriverName = "sqlite" db.Register("sqlite3", func(node yaml.Node) db.Database { conf := new(config) _ = node.Decode(conf) diff --git a/go.mod b/go.mod index a0b7fc32e..4f012a68f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/Mrs4s/go-cqhttp go 1.19 require ( - github.com/FloatTech/sqlite v1.5.6 + github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e diff --git a/go.sum b/go.sum index 5a2479022..564292cb8 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/FloatTech/sqlite v1.5.6 h1:Ws4uiN5AjjGV+ikM/cL8eOHHeYvX390srv8SaqPQ5Ok= -github.com/FloatTech/sqlite v1.5.6/go.mod h1:ItKoTpmEE2cYQ/hGrC02W+BBYE/tlUjlVGSTiM6ewFM= +github.com/FloatTech/sqlite v1.5.7 h1:Bvo4LSojcZ6dVtbHrkqvt6z4v8e+sj0G5PSUIvdawsk= +github.com/FloatTech/sqlite v1.5.7/go.mod h1:zFbHzRfB+CJ+VidfjuVbrcin3DAz283F7hF1hIeHzpY= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= From 894c047330127a5a47620da7a65683a900a9ab8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BE=BD=E5=85=88=E7=94=9F?= <8655163+VergilGao@users.noreply.github.com> Date: Mon, 5 Dec 2022 21:28:52 +0800 Subject: [PATCH 489/612] docker: use su-exec to run cqhttp as a non-root user (#1753) --- Dockerfile | 32 ++++++++++++++++++++++++++------ docker-entrypoint.sh | 20 ++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 docker-entrypoint.sh diff --git a/Dockerfile b/Dockerfile index 27e5cfba2..ba2c97525 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM golang:1.19-alpine AS builder RUN go env -w GO111MODULE=auto \ && go env -w CGO_ENABLED=0 \ - && go env -w GOPROXY=https://goproxy.cn,direct + && go env -w GOPROXY=https://goproxy.cn,direct WORKDIR /build @@ -14,11 +14,31 @@ RUN set -ex \ FROM alpine:latest -RUN apk add --no-cache ffmpeg - -COPY --from=builder /build/cqhttp /usr/bin/cqhttp -RUN chmod +x /usr/bin/cqhttp +COPY docker-entrypoint.sh /docker-entrypoint.sh + +RUN chmod +x /docker-entrypoint.sh && \ + apk add --no-cache --update \ + ffmpeg \ + coreutils \ + shadow \ + su-exec && \ + rm -rf /var/cache/apk/* && \ + mkdir -p /app && \ + mkdir -p /data && \ + mkdir -p /config && \ + useradd -d /config -s /bin/sh abc && \ + chown -R abc /config && \ + chown -R abc /data + +ENV TZ="Asia/Shanghai" +ENV UID=99 +ENV GID=100 +ENV UMASK=002 + +COPY --from=builder /build/cqhttp /app/ WORKDIR /data -ENTRYPOINT [ "/usr/bin/cqhttp" ] +VOLUME [ "/data" ] + +ENTRYPOINT [ "/docker-entrypoint.sh" ] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 000000000..3419c707f --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +USER=abc + +echo "---Setup Timezone to ${TZ}---" +echo "${TZ}" > /etc/timezone +echo "---Checking if UID: ${UID} matches user---" +usermod -o -u ${UID} ${USER} +echo "---Checking if GID: ${GID} matches user---" +groupmod -o -g ${GID} ${USER} > /dev/null 2>&1 ||: +usermod -g ${GID} ${USER} +echo "---Setting umask to ${UMASK}---" +umask ${UMASK} + +echo "---Taking ownership of data...---" +chown -R ${UID}:${GID} /app /data +chmod +x /app/cqhttp + +echo "Starting..." +su-exec ${USER} /app/cqhttp From d5936a40641f925c0ef39294c4b9d70fa9f09e09 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 7 Dec 2022 03:07:20 +0800 Subject: [PATCH 490/612] update dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4f012a68f..7ffb98bca 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e + github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 564292cb8..d0fe8fca3 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e h1:PvTvu1c9R8XCDGysBYGF+TSO+UfX347NJkNblifC4C8= -github.com/Mrs4s/MiraiGo v0.0.0-20221201090748-23f637d2954e/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM= +github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd h1:rzAbPc++5CJ1VZDjq/eORXOWMMGsDN3DMAPMXfI7Fvs= +github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM= github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0 h1:GEwcB4dL9vc4veW1fLNt0Fby3wspVflAn5v9/HbUwDM= github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 231544d51e82783bea39cefede940ceaf1a798db Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 7 Dec 2022 03:23:15 +0800 Subject: [PATCH 491/612] remove: slider captcha anto-select --- cmd/gocq/login.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 3fc3813b2..84205edbe 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -148,8 +148,8 @@ func loginResponseProcessor(res *client.LoginResponse) error { log.Warnf("登录需要滑条验证码, 请选择验证方式: ") log.Warnf("1. 使用浏览器抓取滑条并登录") log.Warnf("2. 使用手机QQ扫码验证 (需要手Q和gocq在同一网络下).") - log.Warn("请输入(1 - 2) (将在10秒后自动选择1):") - text = readLineTimeout(time.Second*10, "1") + log.Warn("请输入(1 - 2):") + text = readLine() if strings.Contains(text, "1") { ticket := getTicket(res.VerifyUrl) if ticket == "" { From 0e08ceccdda970d10dfd87cb78a8807a359f459c Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 7 Dec 2022 03:27:19 +0800 Subject: [PATCH 492/612] api: get_version_info remove `protocol` field & add `protocol_name` field --- coolq/api.go | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index d7275cec0..9941fe17a 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1990,22 +1990,7 @@ func (bot *CQBot) CQGetVersionInfo() global.MSG { "runtime_version": runtime.Version(), "runtime_os": runtime.GOOS, "version": base.Version, - "protocol": func() int { - switch client.SystemDeviceInfo.Protocol { - case client.Unset, client.IPad: - return 0 - case client.AndroidPhone: - return 1 - case client.AndroidWatch: - return 2 - case client.MacOS: - return 3 - case client.QiDian: - return 4 - default: - return -1 - } - }(), + "protocol_name": client.SystemDeviceInfo.Protocol, }) } From 8343db5ae3b311aea1f0c26afff203d9edc20284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=91=E8=B4=A8?= Date: Tue, 13 Dec 2022 22:17:32 +0800 Subject: [PATCH 493/612] =?UTF-8?q?=E5=B0=86=E5=8F=8D=E5=90=91HTTP=20POST?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E4=B8=AD=E8=AF=B7=E6=B1=82=E7=9A=84Method?= =?UTF-8?q?=E7=94=B1GET=E4=BF=AE=E6=94=B9=E4=B8=BAPOST=20(#1794)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/http.go b/server/http.go index a78102e07..6bebc18f9 100644 --- a/server/http.go +++ b/server/http.go @@ -356,7 +356,7 @@ func (c *HTTPClient) onBotPushEvent(e *coolq.Event) { for i := uint64(0); i <= c.MaxRetries; i++ { // see https://stackoverflow.com/questions/31337891/net-http-http-contentlength-222-with-body-length-0 // we should create a new request for every single post trial - req, err = http.NewRequest(http.MethodGet, c.addr, bytes.NewReader(e.JSONBytes())) + req, err = http.NewRequest(http.MethodPost, c.addr, bytes.NewReader(e.JSONBytes())) if err != nil { log.Warnf("上报 Event 数据到 %v 时创建请求失败: %v", c.addr, err) return From b05a64fc1b873756dc7da40e35f728aed5e0df45 Mon Sep 17 00:00:00 2001 From: xiangxiangxiong9 <1296976552@qq.com> Date: Tue, 20 Dec 2022 20:37:08 +0800 Subject: [PATCH 494/612] =?UTF-8?q?=E9=82=80=E8=AF=B7=E5=85=A5=E7=BE=A4?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=82=80=E8=AF=B7=E4=BA=BAid=20[invitor=5Fid?= =?UTF-8?q?]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/event.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/coolq/event.go b/coolq/event.go index 02c4da624..51492ef53 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -543,10 +543,11 @@ func (bot *CQBot) groupInvitedEvent(c *client.QQClient, e *client.GroupInvitedRe log.Infof("收到来自群 %v(%v) 内用户 %v(%v) 的加群邀请.", e.GroupName, e.GroupCode, e.InvitorNick, e.InvitorUin) flag := strconv.FormatInt(e.RequestId, 10) bot.dispatchEvent("request/group/invite", global.MSG{ - "group_id": e.GroupCode, - "user_id": e.InvitorUin, - "comment": "", - "flag": flag, + "group_id": e.GroupCode, + "user_id": e.InvitorUin, + "invitor_id": 0, + "comment": "", + "flag": flag, }) } @@ -554,10 +555,11 @@ func (bot *CQBot) groupJoinReqEvent(c *client.QQClient, e *client.UserJoinGroupR log.Infof("群 %v(%v) 收到来自用户 %v(%v) 的加群请求.", e.GroupName, e.GroupCode, e.RequesterNick, e.RequesterUin) flag := strconv.FormatInt(e.RequestId, 10) bot.dispatchEvent("request/group/add", global.MSG{ - "group_id": e.GroupCode, - "user_id": e.RequesterUin, - "comment": e.Message, - "flag": flag, + "group_id": e.GroupCode, + "user_id": e.RequesterUin, + "invitor_id": e.ActionUin, + "comment": e.Message, + "flag": flag, }) } From 02aadaf63ce130e81e0b89f3caa738245bba3266 Mon Sep 17 00:00:00 2001 From: xiangxiangxiong9 <1296976552@qq.com> Date: Tue, 20 Dec 2022 21:03:31 +0800 Subject: [PATCH 495/612] make lint happy --- global/quote.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/global/quote.go b/global/quote.go index bad54b94d..6e6121894 100644 --- a/global/quote.go +++ b/global/quote.go @@ -18,11 +18,11 @@ func Quote(s string) string { return quoteWith(s, '"', false, false) } -func quoteWith(s string, quote byte, ASCIIonly, graphicOnly bool) string { - return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, ASCIIonly, graphicOnly)) +func quoteWith(s string, quote byte, asciiOnly, graphicOnly bool) string { + return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, asciiOnly, graphicOnly)) } -func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly bool) []byte { +func appendQuotedWith(buf []byte, s string, quote byte, asciiOnly, graphicOnly bool) []byte { // Often called with big strings, so preallocate. If there's quoting, // this is conservative but still helps a lot. if cap(buf)-len(buf) < len(s) { @@ -43,19 +43,19 @@ func appendQuotedWith(buf []byte, s string, quote byte, ASCIIonly, graphicOnly b buf = append(buf, lowerhex[s[0]&0xF]) continue } - buf = appendEscapedRune(buf, r, quote, ASCIIonly, graphicOnly) + buf = appendEscapedRune(buf, r, quote, asciiOnly, graphicOnly) } buf = append(buf, quote) return buf } -func appendEscapedRune(buf []byte, r rune, quote byte, ASCIIonly, graphicOnly bool) []byte { +func appendEscapedRune(buf []byte, r rune, quote byte, asciiOnly, graphicOnly bool) []byte { var runeTmp [utf8.UTFMax]byte if r == rune(quote) || r == '\\' { // always backslashed buf = append(buf, '\\') buf = append(buf, byte(r)) return buf } - if ASCIIonly { + if asciiOnly { if r < utf8.RuneSelf && strconv.IsPrint(r) { buf = append(buf, byte(r)) return buf From c744025cc844a818c76303f38e0ebd868e6fa1f4 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 24 Dec 2022 15:01:19 +0800 Subject: [PATCH 496/612] dep: downgrade package go-silk Fixes #1818 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7ffb98bca..b078f2177 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/stretchr/testify v1.8.1 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tidwall/gjson v1.14.4 - github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea + github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 go.mongodb.org/mongo-driver v1.11.0 golang.org/x/crypto v0.3.0 golang.org/x/sys v0.2.0 diff --git a/go.sum b/go.sum index d0fe8fca3..2fe15b905 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,8 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea h1:sl1pYm1kHtIndckTY8YDt+QFt77vI0JnKHP0U8rZtKc= -github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= +github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 h1:lRKf10iIOW0VsH5WDF621ihzR+R2wEBZVtNRHuLLCb4= +github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= From 49a8b9bd644957b7e348d6cb41294d834116dea7 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 1 Jan 2023 21:25:38 +0800 Subject: [PATCH 497/612] clean code --- cmd/gocq/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 954573db7..adf5a9916 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -135,7 +135,6 @@ func Main() { if base.Debug { log.SetLevel(log.DebugLevel) log.Warnf("已开启Debug模式.") - // log.Debugf("开发交流群: 192548878") } if !global.PathExists("device.json") { log.Warn("虚拟设备信息不存在, 将自动生成随机设备.") From 37a89010613697a3f92d29c44c5115a7c8f7db50 Mon Sep 17 00:00:00 2001 From: linepro6 <53991395+linepro6@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:00:42 +0800 Subject: [PATCH 498/612] fix: FindFile http return nil without cache (#1832) --- global/fs.go | 1 + 1 file changed, 1 insertion(+) diff --git a/global/fs.go b/global/fs.go index 046ac2c50..aebcf5116 100644 --- a/global/fs.go +++ b/global/fs.go @@ -88,6 +88,7 @@ func FindFile(file, cache, p string) (data []byte, err error) { if err != nil { return nil, err } + return os.ReadFile(cacheFile) case strings.HasPrefix(file, "base64"): data, err = base64.StdEncoding.DecodeString(strings.TrimPrefix(file, "base64://")) if err != nil { From 40619049459e82a859d156b287bb0946b9a597ce Mon Sep 17 00:00:00 2001 From: Shigma Date: Tue, 3 Jan 2023 21:01:31 +0800 Subject: [PATCH 499/612] chore: bump isatty (#1830) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d91966ebe..8b211b3e2 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( golang.org/x/sys v0.2.0 golang.org/x/term v0.2.0 golang.org/x/time v0.2.0 - gopkg.ilharper.com/x/isatty v1.1.0 + gopkg.ilharper.com/x/isatty v1.1.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 024f0846e..ae1bf8cec 100644 --- a/go.sum +++ b/go.sum @@ -182,8 +182,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -gopkg.ilharper.com/x/isatty v1.1.0 h1:slOK6hP9/y9mJWyCInMwnT432NExfWyYV2SsebdYOCY= -gopkg.ilharper.com/x/isatty v1.1.0/go.mod h1:ofpv77Td5qQO6R1dmDd3oNt8TZdRo+l5gYAMxopRyS0= +gopkg.ilharper.com/x/isatty v1.1.1 h1:RAg32Pxq/nIK4AVtdm9RBqxsxZZX1uRKRSS21E5SHMk= +gopkg.ilharper.com/x/isatty v1.1.1/go.mod h1:ofpv77Td5qQO6R1dmDd3oNt8TZdRo+l5gYAMxopRyS0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 2a4ea28f4d25c4cbaa3e7f61276bfcc4dc6ae956 Mon Sep 17 00:00:00 2001 From: Maiko Tan Date: Thu, 5 Jan 2023 21:58:37 +0800 Subject: [PATCH 500/612] feat: add webp image convert function --- coolq/bot.go | 16 +++++++++++++++- global/codec.go | 5 +++++ internal/base/flag.go | 2 ++ modules/config/config.go | 1 + modules/config/default_config.yml | 2 ++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index de841313e..3b01e3972 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -153,9 +153,23 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement defer func() { _ = f.Close() }() img.Stream = f } - if mt, ok := mime.CheckImage(img.Stream); !ok { + mt, ok := mime.CheckImage(img.Stream) + if !ok { return nil, errors.New("image type error: " + mt) } + if mt == "image/webp" && base.ConvertWebpImage != false { + newname := img.File + ".png" + err := global.ConvertImagePng(img.File, newname) + if err != nil { + return nil, errors.Wrap(err, "convert webp image error") + } + f, err := os.Open(newname) + if err != nil { + return nil, errors.Wrap(err, "open image error") + } + defer func() { _ = f.Close() }() + img.Stream = f + } i, err := bot.Client.UploadImage(target, img.Stream, 4) if err != nil { return nil, err diff --git a/global/codec.go b/global/codec.go index 6a5f088cf..4ef6bbcfd 100644 --- a/global/codec.go +++ b/global/codec.go @@ -46,3 +46,8 @@ func ExtractCover(src string, target string) error { cmd := exec.Command("ffmpeg", "-i", src, "-y", "-ss", "0", "-frames:v", "1", target) return errors.Wrap(cmd.Run(), "extract video cover failed") } + +func ConvertImagePng(src string, target string) error { + cmd := exec.Command("ffmpeg", "-i", src, "-y", "-f", "png", target) + return errors.Wrap(cmd.Run(), "convert image to png failed") +} diff --git a/internal/base/flag.go b/internal/base/flag.go index 38790ee8d..b9331f41c 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -30,6 +30,7 @@ var ( SplitURL bool // 是否分割URL ForceFragmented bool // 是否启用强制分片 SkipMimeScan bool // 是否跳过Mime扫描 + ConvertWebpImage bool // 是否转换Webp图片 ReportSelfMessage bool // 是否上报自身消息 UseSSOAddress bool // 是否使用服务器下发的新地址进行重连 LogForceNew bool // 是否在每次启动时强制创建全新的文件储存日志 @@ -79,6 +80,7 @@ func Init() { ExtraReplyData = conf.Message.ExtraReplyData ForceFragmented = conf.Message.ForceFragment SkipMimeScan = conf.Message.SkipMimeScan + ConvertWebpImage = conf.Message.ConvertWebpImage ReportSelfMessage = conf.Message.ReportSelfMessage UseSSOAddress = conf.Account.UseSSOAddress AllowTempSession = conf.Account.AllowTempSession diff --git a/modules/config/config.go b/modules/config/config.go index 23329d7f4..24562e986 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -55,6 +55,7 @@ type Config struct { RemoveReplyAt bool `yaml:"remove-reply-at"` ExtraReplyData bool `yaml:"extra-reply-data"` SkipMimeScan bool `yaml:"skip-mime-scan"` + ConvertWebpImage bool `yaml:"convert-webp-image"` } `yaml:"message"` Output struct { diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 60d5db007..9e4acd8ea 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -43,6 +43,8 @@ message: extra-reply-data: false # 跳过 Mime 扫描, 忽略错误数据 skip-mime-scan: false + # 是否自动转换 WebP 图片 + convert-webp-image: false output: # 日志等级 trace,debug,info,warn,error From 524debbfdae7e5abf86e2f3619511b0d0718d12c Mon Sep 17 00:00:00 2001 From: Maiko Tan Date: Fri, 6 Jan 2023 12:57:19 +0800 Subject: [PATCH 501/612] fix: simplify bool checking --- coolq/bot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/bot.go b/coolq/bot.go index 3b01e3972..0a2fffab3 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -157,7 +157,7 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement if !ok { return nil, errors.New("image type error: " + mt) } - if mt == "image/webp" && base.ConvertWebpImage != false { + if mt == "image/webp" && base.ConvertWebpImage { newname := img.File + ".png" err := global.ConvertImagePng(img.File, newname) if err != nil { From 008e139c279f4d84196da4374b94e045c07d1a95 Mon Sep 17 00:00:00 2001 From: Maiko Tan Date: Fri, 6 Jan 2023 13:06:44 +0800 Subject: [PATCH 502/612] fix: ffmpeg runtime error --- coolq/bot.go | 2 +- global/codec.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 0a2fffab3..6818e1c8a 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -159,7 +159,7 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement } if mt == "image/webp" && base.ConvertWebpImage { newname := img.File + ".png" - err := global.ConvertImagePng(img.File, newname) + err := global.ConvertImage(img.File, newname) if err != nil { return nil, errors.Wrap(err, "convert webp image error") } diff --git a/global/codec.go b/global/codec.go index 4ef6bbcfd..ebdb24185 100644 --- a/global/codec.go +++ b/global/codec.go @@ -47,7 +47,7 @@ func ExtractCover(src string, target string) error { return errors.Wrap(cmd.Run(), "extract video cover failed") } -func ConvertImagePng(src string, target string) error { - cmd := exec.Command("ffmpeg", "-i", src, "-y", "-f", "png", target) +func ConvertImage(src string, target string) error { + cmd := exec.Command("ffmpeg", "-i", src, "-y", target) return errors.Wrap(cmd.Run(), "convert image to png failed") } From 311a254b9c6d6a9f6b69b23e0f793f33b7a1c939 Mon Sep 17 00:00:00 2001 From: Maiko Tan Date: Fri, 6 Jan 2023 14:44:00 +0800 Subject: [PATCH 503/612] refactor: use in-memory convertion --- coolq/bot.go | 18 +++++++----------- global/codec.go | 5 ----- go.mod | 3 ++- go.sum | 16 ++++++++++++++++ 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 6818e1c8a..f6b17f6a3 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "image/png" "os" "runtime/debug" "strings" @@ -24,6 +25,8 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/mime" + + "golang.org/x/image/webp" ) // CQBot CQBot结构体,存储Bot实例相关配置 @@ -158,17 +161,10 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement return nil, errors.New("image type error: " + mt) } if mt == "image/webp" && base.ConvertWebpImage { - newname := img.File + ".png" - err := global.ConvertImage(img.File, newname) - if err != nil { - return nil, errors.Wrap(err, "convert webp image error") - } - f, err := os.Open(newname) - if err != nil { - return nil, errors.Wrap(err, "open image error") - } - defer func() { _ = f.Close() }() - img.Stream = f + img0, _ := webp.Decode(img.Stream) + stream := bytes.NewBuffer(nil) + png.Encode(stream, img0) + img.Stream = bytes.NewReader(stream.Bytes()) } i, err := bot.Client.UploadImage(target, img.Stream, 4) if err != nil { diff --git a/global/codec.go b/global/codec.go index ebdb24185..6a5f088cf 100644 --- a/global/codec.go +++ b/global/codec.go @@ -46,8 +46,3 @@ func ExtractCover(src string, target string) error { cmd := exec.Command("ffmpeg", "-i", src, "-y", "-ss", "0", "-frames:v", "1", target) return errors.Wrap(cmd.Run(), "extract video cover failed") } - -func ConvertImage(src string, target string) error { - cmd := exec.Command("ffmpeg", "-i", src, "-y", target) - return errors.Wrap(cmd.Run(), "convert image to png failed") -} diff --git a/go.mod b/go.mod index 8b211b3e2..833f73ef0 100644 --- a/go.mod +++ b/go.mod @@ -50,9 +50,10 @@ require ( github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + golang.org/x/image v0.3.0 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect golang.org/x/tools v0.1.12 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect diff --git a/go.sum b/go.sum index ae1bf8cec..016fc5bc3 100644 --- a/go.sum +++ b/go.sum @@ -120,23 +120,32 @@ github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCO github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE= go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/image v0.3.0 h1:HTDXbdK9bjfSWkPzDJIw89W8CAtfFGduujWs33NLLsg= +golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -154,11 +163,14 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -168,11 +180,15 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From a5b51051e6cce0bab640a2bc113e3a405218df13 Mon Sep 17 00:00:00 2001 From: Maiko Tan Date: Fri, 6 Jan 2023 19:25:54 +0800 Subject: [PATCH 504/612] fix: handle decode / encode error --- coolq/bot.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index f6b17f6a3..388c7811d 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -161,9 +161,15 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement return nil, errors.New("image type error: " + mt) } if mt == "image/webp" && base.ConvertWebpImage { - img0, _ := webp.Decode(img.Stream) + img0, err := webp.Decode(img.Stream) + if err != nil { + return nil, errors.Wrap(err, "decode webp error") + } stream := bytes.NewBuffer(nil) - png.Encode(stream, img0) + err = png.Encode(stream, img0) + if err != nil { + return nil, errors.Wrap(err, "encode png error") + } img.Stream = bytes.NewReader(stream.Bytes()) } i, err := bot.Client.UploadImage(target, img.Stream, 4) From 960f7ab79b14ed1618633c9a82e7d666f2721068 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 11 Jan 2023 06:05:38 +0800 Subject: [PATCH 505/612] fix: when the reconnect-interval of ws-reverse is set to 0, push event will panic if has connection error --- server/websocket.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/websocket.go b/server/websocket.go index f6000172f..668d5ec3e 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -191,9 +191,13 @@ func runWSClient(b *coolq.CQBot, node yaml.Node) { filter: conf.Filter, } filter.Add(c.filter) + if conf.ReconnectInterval != 0 { c.reconnectInterval = time.Duration(conf.ReconnectInterval) * time.Millisecond + } else { + c.reconnectInterval = time.Second * 5 } + if conf.RateLimit.Enabled { c.limiter = rateLimit(conf.RateLimit.Frequency, conf.RateLimit.Bucket) } From 7d5f1d68434f14421e6f1619bdfff408a0da77aa Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 11 Jan 2023 06:08:33 +0800 Subject: [PATCH 506/612] fix #1815 --- coolq/event.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/coolq/event.go b/coolq/event.go index 51492ef53..4bd837278 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -144,7 +144,10 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven PrimaryID: e.Session.Sender, } cqm := toStringMessage(m.Elements, source) - bot.tempSessionCache.Store(m.Sender.Uin, e.Session) + if base.AllowTempSession { + bot.tempSessionCache.Store(m.Sender.Uin, e.Session) + } + id := m.Id // todo(Mrs4s) // if bot.db != nil { // nolint From 43004e249636723bcec71f56fdaa0aac6f19f70a Mon Sep 17 00:00:00 2001 From: SlimeNull Date: Fri, 13 Jan 2023 18:32:55 +0800 Subject: [PATCH 507/612] =?UTF-8?q?=E5=85=A8=E9=83=A8=E6=B7=BB=E5=8A=A0=20?= =?UTF-8?q?"=E6=96=87=E6=A1=A3=E5=B7=B2=E7=A7=BB=E5=8A=A8"=20=E6=8F=90?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/EventFilter.md | 2 ++ docs/QA.md | 2 ++ docs/config.md | 2 ++ docs/cqhttp.md | 4 +++- docs/guild.md | 2 ++ docs/quick_start.md | 2 ++ 6 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/EventFilter.md b/docs/EventFilter.md index 6911f59e7..9376e85a2 100644 --- a/docs/EventFilter.md +++ b/docs/EventFilter.md @@ -5,6 +5,8 @@ 注意: 与客户端建立连接的握手事件**不会**经过事件过滤器 +> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足. + ## 示例 这节首先给出一些示例,演示过滤器的基本用法,下一节将给出具体语法说明。 diff --git a/docs/QA.md b/docs/QA.md index 28f455c9f..dc7e4c8ac 100644 --- a/docs/QA.md +++ b/docs/QA.md @@ -1,5 +1,7 @@ # 常见问题 +> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足. + ### Q: 为什么挂一段时间后就会出现 `消息发送失败,账号可能被风控`? ### A: 如果你刚开始使用 go-cqhttp 建议挂机3-7天,即可解除风控 diff --git a/docs/config.md b/docs/config.md index 38f128786..957b191be 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,5 +1,7 @@ # 配置 +> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足. + go-cqhttp 包含 `config.yml` 和 `device.json` 两个配置文件, 其中 `config.yml` 为运行配置 `device.json` 为虚拟设备信息. ## 配置信息 diff --git a/docs/cqhttp.md b/docs/cqhttp.md index 2f6dbb345..901c289e9 100644 --- a/docs/cqhttp.md +++ b/docs/cqhttp.md @@ -1,6 +1,8 @@ # 拓展API -由于部分 api 原版 CQHTTP 并未实现,go-cqhttp 修改并增加了一些拓展 api . +由于部分 api 原版 CQHTTP 并未实现,go-cqhttp 修改并增加了一些拓展 api + +> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足..
    目录 diff --git a/docs/guild.md b/docs/guild.md index 9a20285aa..ec570cf8c 100644 --- a/docs/guild.md +++ b/docs/guild.md @@ -5,6 +5,8 @@ QQ频道相关功能的事件以及API +> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足. + ## 命名说明 API以及字段相关命名均为参考QQ官方命名或相似产品命名规则, 由于QQ频道的账号系统独立于QQ本体, 所以各个 `ID` 并不能和QQ通用.也无法通过 `tiny_id` 获取到 `QQ号` diff --git a/docs/quick_start.md b/docs/quick_start.md index af91dcb2d..28c8c5cde 100644 --- a/docs/quick_start.md +++ b/docs/quick_start.md @@ -2,6 +2,8 @@ 欢迎来到 go-cqhttp 文档 目前还在咕 +> 注意, 最新文档已经移动到 [go-cqhttp-docs](https://github.com/ishkong/go-cqhttp-docs), 当前文档只做兼容性保留, 所以内容可能有不足. + # 基础教程 ## 下载 从[release](https://github.com/Mrs4s/go-cqhttp/releases)界面下载最新版本的go-cqhttp From e050fd68852994c4fb00d1da51f965b3852bb846 Mon Sep 17 00:00:00 2001 From: Shigma Date: Tue, 17 Jan 2023 01:23:32 +0800 Subject: [PATCH 508/612] feat: change polling log level to debug --- cmd/gocq/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index d0d9a338c..27fe61ba5 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -254,7 +254,7 @@ func getTicket(u string) (str string) { func fetchCaptcha(id string) string { g, err := download.Request{URL: "https://captcha.go-cqhttp.org/captcha/ticket?id=" + id}.JSON() if err != nil { - log.Warnf("获取 Ticket 时出现错误: %v", err) + log.Debugf("获取 Ticket 时出现错误: %v", err) return "" } if g.Get("ticket").Exists() { From 4d064e145fdfa3b2df4a8aad394e782776e9a564 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Thu, 19 Jan 2023 23:26:54 +0800 Subject: [PATCH 509/612] fix #1864 --- internal/download/download.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/download/download.go b/internal/download/download.go index 1387168dd..84fe64c1f 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -111,6 +111,7 @@ func writeToFile(reader io.ReadCloser, path string) error { if err != nil { return err } + defer func() { _ = file.Close() }() _, err = file.ReadFrom(reader) return err } From 84e061f321f5bc59a81f01ff6c1fa332be16117d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 26 Jan 2023 22:59:04 +0800 Subject: [PATCH 510/612] make golangci-lint happy --- cmd/gocq/login.go | 10 ++++------ coolq/api.go | 3 +-- coolq/bot.go | 3 +-- coolq/converter.go | 3 +-- coolq/cqcode/element.go | 1 + modules/config/config.go | 2 +- 6 files changed, 9 insertions(+), 13 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index d0d9a338c..244965a1e 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -29,7 +29,7 @@ func readLine() (str string) { return } -func readLineTimeout(t time.Duration, de string) (str string) { +func readLineTimeout(t time.Duration) { r := make(chan string) go func() { select { @@ -37,12 +37,10 @@ func readLineTimeout(t time.Duration, de string) (str string) { case <-time.After(t): } }() - str = de select { - case str = <-r: + case <-r: case <-time.After(t): } - return } func readIfTTY(de string) (str string) { @@ -210,7 +208,7 @@ func loginResponseProcessor(res *client.LoginResponse) error { case client.UnsafeDeviceError: log.Warnf("账号已开启设备锁,请前往 -> %v <- 验证后重启Bot.", res.VerifyUrl) log.Infof("按 Enter 或等待 5s 后继续....") - readLineTimeout(time.Second*5, "") + readLineTimeout(time.Second * 5) os.Exit(0) case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError: msg := res.ErrorMessage @@ -221,7 +219,7 @@ func loginResponseProcessor(res *client.LoginResponse) error { } log.Warnf("登录失败: %v", msg) log.Infof("按 Enter 或等待 5s 后继续....") - readLineTimeout(time.Second*5, "") + readLineTimeout(time.Second * 5) os.Exit(0) } } diff --git a/coolq/api.go b/coolq/api.go index 9941fe17a..1794cc651 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -14,12 +14,11 @@ import ( "strings" "time" - "github.com/segmentio/asm/base64" - "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" + "github.com/segmentio/asm/base64" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" diff --git a/coolq/bot.go b/coolq/bot.go index 388c7811d..8c8ade218 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -20,13 +20,12 @@ import ( "github.com/pkg/errors" "github.com/segmentio/asm/base64" log "github.com/sirupsen/logrus" + "golang.org/x/image/webp" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/mime" - - "golang.org/x/image/webp" ) // CQBot CQBot结构体,存储Bot实例相关配置 diff --git a/coolq/converter.go b/coolq/converter.go index e1053572a..30720e2a8 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -4,10 +4,9 @@ import ( "strconv" "strings" - "github.com/Mrs4s/MiraiGo/topic" - "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" + "github.com/Mrs4s/MiraiGo/topic" log "github.com/sirupsen/logrus" "github.com/Mrs4s/go-cqhttp/global" diff --git a/coolq/cqcode/element.go b/coolq/cqcode/element.go index 2788ba27e..87c057bf5 100644 --- a/coolq/cqcode/element.go +++ b/coolq/cqcode/element.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/Mrs4s/MiraiGo/binary" + "github.com/Mrs4s/go-cqhttp/global" ) diff --git a/modules/config/config.go b/modules/config/config.go index 24562e986..22d9879dc 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -55,7 +55,7 @@ type Config struct { RemoveReplyAt bool `yaml:"remove-reply-at"` ExtraReplyData bool `yaml:"extra-reply-data"` SkipMimeScan bool `yaml:"skip-mime-scan"` - ConvertWebpImage bool `yaml:"convert-webp-image"` + ConvertWebpImage bool `yaml:"convert-webp-image"` } `yaml:"message"` Output struct { From 20c62111f57dec56a1b896fc4c26992db6406655 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 26 Jan 2023 23:03:08 +0800 Subject: [PATCH 511/612] all: run gofmt -w -r 'interface{} -> any' --- coolq/api.go | 2 +- coolq/cqcode.go | 2 +- coolq/event.go | 2 +- db/leveldb/reader.go | 2 +- db/leveldb/writer.go | 2 +- global/log_hook.go | 2 +- global/param.go | 2 +- internal/param/param.go | 2 +- server/middlewares.go | 6 +++--- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 1794cc651..03225e190 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -2083,7 +2083,7 @@ func (bot *CQBot) CQReloadEventFilter(file string) global.MSG { } // OK 生成成功返回值 -func OK(data interface{}) global.MSG { +func OK(data any) global.MSG { return global.MSG{"data": data, "retcode": 0, "status": "ok"} } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index e93e50c4c..ab73c5f13 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -807,7 +807,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message // 返回 interface{} 存在三种类型 // // message.IMessageElement []message.IMessageElement nil -func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.SourceType) (m interface{}, err error) { +func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.SourceType) (m any, err error) { switch t { case "text": if base.SplitURL { diff --git a/coolq/event.go b/coolq/event.go index 4bd837278..e54a40e45 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -21,7 +21,7 @@ import ( ) // ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式 -func ToFormattedMessage(e []message.IMessageElement, source message.Source) (r interface{}) { +func ToFormattedMessage(e []message.IMessageElement, source message.Source) (r any) { if base.PostFormat == "string" { r = toStringMessage(e, source) } else if base.PostFormat == "array" { diff --git a/db/leveldb/reader.go b/db/leveldb/reader.go index e89103640..fef6ebd71 100644 --- a/db/leveldb/reader.go +++ b/db/leveldb/reader.go @@ -84,7 +84,7 @@ func (r *reader) arrayMsg() []global.MSG { return msgs } -func (r *reader) obj() interface{} { +func (r *reader) obj() any { switch coder := r.coder(); coder { case coderNil: return nil diff --git a/db/leveldb/writer.go b/db/leveldb/writer.go index 6067ca102..a8acba7e3 100644 --- a/db/leveldb/writer.go +++ b/db/leveldb/writer.go @@ -96,7 +96,7 @@ func (w *writer) arrayMsg(a []global.MSG) { } } -func (w *writer) obj(o interface{}) { +func (w *writer) obj(o any) { switch x := o.(type) { case nil: w.nil() diff --git a/global/log_hook.go b/global/log_hook.go index 44f4f1d83..df3084b27 100644 --- a/global/log_hook.go +++ b/global/log_hook.go @@ -108,7 +108,7 @@ func (hook *LocalHook) SetPath(path string) { } // NewLocalHook 初始化本地日志钩子实现 -func NewLocalHook(args interface{}, consoleFormatter, fileFormatter logrus.Formatter, levels ...logrus.Level) *LocalHook { +func NewLocalHook(args any, consoleFormatter, fileFormatter logrus.Formatter, levels ...logrus.Level) *LocalHook { hook := &LocalHook{ lock: new(sync.Mutex), } diff --git a/global/param.go b/global/param.go index 44225bd81..0b245809b 100644 --- a/global/param.go +++ b/global/param.go @@ -8,7 +8,7 @@ import ( ) // MSG 消息Map -type MSG = map[string]interface{} +type MSG = map[string]any // VersionNameCompare 检查版本名是否需要更新, 仅适用于 go-cqhttp 的版本命名规则 // diff --git a/internal/param/param.go b/internal/param/param.go index 19da7fc39..8e763e1b6 100644 --- a/internal/param/param.go +++ b/internal/param/param.go @@ -19,7 +19,7 @@ import ( // type gjson.True or gjson.False // // type string "true","yes","1" or "false","no","0" (case insensitive) -func EnsureBool(p interface{}, defaultVal bool) bool { +func EnsureBool(p any, defaultVal bool) bool { var str string if b, ok := p.(bool); ok { return b diff --git a/server/middlewares.go b/server/middlewares.go index 7230248d1..f783e0f4b 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -50,7 +50,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler { return nil } var ( - ch = make(chan []interface{}) + ch = make(chan []any) timeout = time.Duration(p.Get("timeout").Int()) * time.Second ) go func() { @@ -63,7 +63,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler { if limit <= 0 || queue.Len() < limit { limit = queue.Len() } - ret := make([]interface{}, limit) + ret := make([]any, limit) elem := queue.Front() for i := 0; i < limit; i++ { ret[i] = elem.Value @@ -81,7 +81,7 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler { if timeout != 0 { select { case <-time.After(timeout): - return coolq.OK([]interface{}{}) + return coolq.OK([]any{}) case ret := <-ch: return coolq.OK(ret) } From 0be18fb221ba43d831f7a8b93422e994dd898311 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 26 Jan 2023 23:58:43 +0800 Subject: [PATCH 512/612] coolq/cqcode: simplify quote string --- coolq/cqcode/all_test.go | 19 +++++ coolq/cqcode/element.go | 92 +++++++++++++++++++++++- global/quote.go | 146 --------------------------------------- 3 files changed, 108 insertions(+), 149 deletions(-) create mode 100644 coolq/cqcode/all_test.go delete mode 100644 global/quote.go diff --git a/coolq/cqcode/all_test.go b/coolq/cqcode/all_test.go new file mode 100644 index 000000000..a42e9bbef --- /dev/null +++ b/coolq/cqcode/all_test.go @@ -0,0 +1,19 @@ +package cqcode + +import ( + "bytes" + "testing" +) + +func TestIssue1733(t *testing.T) { + const ( + input = "\u0005" + expected = `"\u0005"` + ) + var b bytes.Buffer + writeQuote(&b, input) + got := b.String() + if got != expected { + t.Errorf("want %v but got %v", expected, got) + } +} diff --git a/coolq/cqcode/element.go b/coolq/cqcode/element.go index 87c057bf5..12dc08ea8 100644 --- a/coolq/cqcode/element.go +++ b/coolq/cqcode/element.go @@ -3,10 +3,9 @@ package cqcode import ( "bytes" "strings" + "unicode/utf8" "github.com/Mrs4s/MiraiGo/binary" - - "github.com/Mrs4s/go-cqhttp/global" ) // Element single message @@ -61,8 +60,95 @@ func (e *Element) MarshalJSON() ([]byte, error) { buf.WriteByte('"') buf.WriteString(data.K) buf.WriteString(`":`) - buf.WriteString(global.Quote(data.V)) + writeQuote(buf, data.V) } buf.WriteString(`}}`) }), nil } + +const hex = "0123456789abcdef" + +func writeQuote(b *bytes.Buffer, s string) { + i, j := 0, 0 + + b.WriteByte('"') + for j < len(s) { + c := s[j] + + if c >= 0x20 && c <= 0x7f && c != '\\' && c != '"' { + // fast path: most of the time, printable ascii characters are used + j++ + continue + } + + switch c { + case '\\', '"', '\n', '\r', '\t': + b.WriteString(s[i:j]) + b.WriteByte('\\') + switch c { + case '\n': + c = 'n' + case '\r': + c = 'r' + case '\t': + c = 't' + } + b.WriteByte(c) + i = j + 1 + j = j + 1 + continue + + case '<', '>', '&': + b.WriteString(s[i:j]) + b.WriteString(`\u00`) + b.WriteByte(hex[c>>4]) + b.WriteByte(hex[c&0xF]) + i = j + 1 + j = j + 1 + continue + } + + // This encodes bytes < 0x20 except for \t, \n and \r. + if c < 0x20 { + b.WriteString(s[i:j]) + b.WriteString(`\u00`) + b.WriteByte(hex[c>>4]) + b.WriteByte(hex[c&0xF]) + i = j + 1 + j = j + 1 + continue + } + + r, size := utf8.DecodeRuneInString(s[j:]) + + if r == utf8.RuneError && size == 1 { + b.WriteString(s[i:j]) + b.WriteString(`\ufffd`) + i = j + size + j = j + size + continue + } + + switch r { + case '\u2028', '\u2029': + // U+2028 is LINE SEPARATOR. + // U+2029 is PARAGRAPH SEPARATOR. + // They are both technically valid characters in JSON strings, + // but don't work in JSONP, which has to be evaluated as JavaScript, + // and can lead to security holes there. It is valid JSON to + // escape them, so we do so unconditionally. + // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. + b.WriteString(s[i:j]) + b.WriteString(`\u202`) + b.WriteByte(hex[r&0xF]) + i = j + size + j = j + size + continue + } + + j += size + } + + b.WriteString(s[i:]) + b.WriteByte('"') +} diff --git a/global/quote.go b/global/quote.go deleted file mode 100644 index 6e6121894..000000000 --- a/global/quote.go +++ /dev/null @@ -1,146 +0,0 @@ -package global - -import ( - "strconv" - "unicode/utf8" -) - -const ( - lowerhex = "0123456789abcdef" - upperhex = "0123456789ABCDEF" -) - -// Quote returns a double-quoted Go string literal representing s. The -// returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for -// control characters and non-printable characters as defined by -// IsPrint. -func Quote(s string) string { - return quoteWith(s, '"', false, false) -} - -func quoteWith(s string, quote byte, asciiOnly, graphicOnly bool) string { - return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote, asciiOnly, graphicOnly)) -} - -func appendQuotedWith(buf []byte, s string, quote byte, asciiOnly, graphicOnly bool) []byte { - // Often called with big strings, so preallocate. If there's quoting, - // this is conservative but still helps a lot. - if cap(buf)-len(buf) < len(s) { - nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1) - copy(nBuf, buf) - buf = nBuf - } - buf = append(buf, quote) - for width := 0; len(s) > 0; s = s[width:] { - r := rune(s[0]) - width = 1 - if r >= utf8.RuneSelf { - r, width = utf8.DecodeRuneInString(s) - } - if width == 1 && r == utf8.RuneError { - buf = append(buf, `\x`...) - buf = append(buf, lowerhex[s[0]>>4]) - buf = append(buf, lowerhex[s[0]&0xF]) - continue - } - buf = appendEscapedRune(buf, r, quote, asciiOnly, graphicOnly) - } - buf = append(buf, quote) - return buf -} -func appendEscapedRune(buf []byte, r rune, quote byte, asciiOnly, graphicOnly bool) []byte { - var runeTmp [utf8.UTFMax]byte - if r == rune(quote) || r == '\\' { // always backslashed - buf = append(buf, '\\') - buf = append(buf, byte(r)) - return buf - } - if asciiOnly { - if r < utf8.RuneSelf && strconv.IsPrint(r) { - buf = append(buf, byte(r)) - return buf - } - } else if strconv.IsPrint(r) || graphicOnly && isInGraphicList(r) { - n := utf8.EncodeRune(runeTmp[:], r) - buf = append(buf, runeTmp[:n]...) - return buf - } - switch r { - case '\a': - buf = append(buf, `\a`...) - case '\b': - buf = append(buf, `\b`...) - case '\f': - buf = append(buf, `\f`...) - case '\n': - buf = append(buf, `\n`...) - case '\r': - buf = append(buf, `\r`...) - case '\t': - buf = append(buf, `\t`...) - case '\v': - buf = append(buf, `\v`...) - default: - switch { - case !utf8.ValidRune(r): - r = 0xFFFD - fallthrough - case r < 0x10000: - buf = append(buf, `\u`...) - for s := 12; s >= 0; s -= 4 { - buf = append(buf, lowerhex[r>>uint(s)&0xF]) - } - default: - buf = append(buf, `\U`...) - for s := 28; s >= 0; s -= 4 { - buf = append(buf, lowerhex[r>>uint(s)&0xF]) - } - } - } - return buf -} - -func isInGraphicList(r rune) bool { - // We know r must fit in 16 bits - see makeisprint.go. - if r > 0xFFFF { - return false - } - rr := uint16(r) - i := bsearch16(isGraphic, rr) - return i < len(isGraphic) && rr == isGraphic[i] -} - -// bsearch16 returns the smallest i such that a[i] >= x. -// If there is no such i, bsearch16 returns len(a). -func bsearch16(a []uint16, x uint16) int { - i, j := 0, len(a) - for i < j { - h := i + (j-i)>>1 - if a[h] < x { - i = h + 1 - } else { - j = h - } - } - return i -} - -// isGraphic lists the graphic runes not matched by IsPrint. -var isGraphic = []uint16{ - 0x00a0, - 0x1680, - 0x2000, - 0x2001, - 0x2002, - 0x2003, - 0x2004, - 0x2005, - 0x2006, - 0x2007, - 0x2008, - 0x2009, - 0x200a, - 0x202f, - 0x205f, - 0x3000, -} From 4ed04443c536a728a43cbb15535d5ae3cef45dad Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 31 Jan 2023 21:18:15 +0800 Subject: [PATCH 513/612] server: quick path for http join query --- server/http.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/server/http.go b/server/http.go index 6bebc18f9..7b79a58e7 100644 --- a/server/http.go +++ b/server/http.go @@ -104,31 +104,35 @@ func init() { var joinQuery = regexp.MustCompile(`\[(.+?),(.+?)]\.0`) -func (h *httpCtx) get(s string, join bool) gjson.Result { +func mayJSONParam(p string) bool { + if strings.HasPrefix(p, "{") || strings.HasPrefix(p, "[") { + return gjson.Valid(p) + } + return false +} + +func (h *httpCtx) get(pattern string, join bool) gjson.Result { // support gjson advanced syntax: - // h.Get("[a,b].0") see usage in http_test.go - if join && joinQuery.MatchString(s) { - matched := joinQuery.FindStringSubmatch(s) + // h.Get("[a,b].0") see usage in http_test.go. See issue #1241, #1325. + if join && strings.HasPrefix(pattern, "[") && joinQuery.MatchString(pattern) { + matched := joinQuery.FindStringSubmatch(pattern) if r := h.get(matched[1], false); r.Exists() { return r } return h.get(matched[2], false) } - validJSONParam := func(p string) bool { - return (strings.HasPrefix(p, "{") || strings.HasPrefix(p, "[")) && gjson.Valid(p) - } if h.postForm != nil { - if form := h.postForm.Get(s); form != "" { - if validJSONParam(form) { + if form := h.postForm.Get(pattern); form != "" { + if mayJSONParam(form) { return gjson.Result{Type: gjson.JSON, Raw: form} } return gjson.Result{Type: gjson.String, Str: form} } } if h.query != nil { - if query := h.query.Get(s); query != "" { - if validJSONParam(query) { + if query := h.query.Get(pattern); query != "" { + if mayJSONParam(query) { return gjson.Result{Type: gjson.JSON, Raw: query} } return gjson.Result{Type: gjson.String, Str: query} From 06450c66a221064c73647507fe436ec788f812b9 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 31 Jan 2023 21:20:48 +0800 Subject: [PATCH 514/612] ci: make golangci-lint happy --- coolq/cqcode/element.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/coolq/cqcode/element.go b/coolq/cqcode/element.go index 12dc08ea8..7c1cc74d8 100644 --- a/coolq/cqcode/element.go +++ b/coolq/cqcode/element.go @@ -94,8 +94,8 @@ func writeQuote(b *bytes.Buffer, s string) { c = 't' } b.WriteByte(c) - i = j + 1 - j = j + 1 + j++ + i = j continue case '<', '>', '&': @@ -103,8 +103,8 @@ func writeQuote(b *bytes.Buffer, s string) { b.WriteString(`\u00`) b.WriteByte(hex[c>>4]) b.WriteByte(hex[c&0xF]) - i = j + 1 - j = j + 1 + j++ + i = j continue } @@ -114,8 +114,8 @@ func writeQuote(b *bytes.Buffer, s string) { b.WriteString(`\u00`) b.WriteByte(hex[c>>4]) b.WriteByte(hex[c&0xF]) - i = j + 1 - j = j + 1 + j++ + i = j continue } @@ -124,8 +124,8 @@ func writeQuote(b *bytes.Buffer, s string) { if r == utf8.RuneError && size == 1 { b.WriteString(s[i:j]) b.WriteString(`\ufffd`) - i = j + size - j = j + size + j += size + i = j continue } @@ -141,8 +141,8 @@ func writeQuote(b *bytes.Buffer, s string) { b.WriteString(s[i:j]) b.WriteString(`\u202`) b.WriteByte(hex[r&0xF]) - i = j + size - j = j + size + j += size + i = j continue } From f3da083be9a0152e5b953c926d962e852a65feb8 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 31 Jan 2023 23:10:30 +0800 Subject: [PATCH 515/612] coolq/cqcode: add a testcase for quote May report an empty json message. --- coolq/cqcode/all_test.go | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/coolq/cqcode/all_test.go b/coolq/cqcode/all_test.go index a42e9bbef..ff285c57e 100644 --- a/coolq/cqcode/all_test.go +++ b/coolq/cqcode/all_test.go @@ -2,18 +2,31 @@ package cqcode import ( "bytes" + "encoding/json" "testing" ) -func TestIssue1733(t *testing.T) { - const ( - input = "\u0005" - expected = `"\u0005"` - ) - var b bytes.Buffer - writeQuote(&b, input) - got := b.String() - if got != expected { - t.Errorf("want %v but got %v", expected, got) +func jsonMarshal(s string) string { + b, err := json.Marshal(s) + if err != nil { + panic(err) + } + return string(b) +} + +func Test_quote(t *testing.T) { + testcase := []string{ + "\u0005", // issue 1773 + "\v", + } + + for _, input := range testcase { + var b bytes.Buffer + writeQuote(&b, input) + got := b.String() + expected := jsonMarshal(input) + if got != expected { + t.Errorf("want %v but got %v", expected, got) + } } } From fc0845b16db462bbc6f4539d5fadf91dd42f83fa Mon Sep 17 00:00:00 2001 From: BeautyyuYanli Date: Wed, 1 Feb 2023 13:22:56 +0800 Subject: [PATCH 516/612] Support API set_group_anonymous (#1875) * support api set_group_anonymous * update MiraiGo version * fix bug due to MiraiGo update --- coolq/api.go | 17 +++++++++++++++-- go.mod | 2 +- go.sum | 3 +++ modules/api/api.go | 7 +++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 03225e190..f27305fef 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1106,12 +1106,12 @@ func (bot *CQBot) CQSetGroupMemo(groupID int64, msg, img string) global.MSG { if err != nil { return Failed(100, "IMAGE_NOT_FOUND", "图片未找到") } - err = bot.Client.AddGroupNoticeWithPic(groupID, msg, data) + _, err = bot.Client.AddGroupNoticeWithPic(groupID, msg, data) if err != nil { return Failed(100, "SEND_NOTICE_ERROR", err.Error()) } } else { - err := bot.Client.AddGroupNoticeSimple(groupID, msg) + _, err := bot.Client.AddGroupNoticeSimple(groupID, msg) if err != nil { return Failed(100, "SEND_NOTICE_ERROR", err.Error()) } @@ -1337,6 +1337,19 @@ func (bot *CQBot) CQSetGroupAdmin(groupID, userID int64, enable bool) global.MSG return OK(nil) } +// CQSetGroupAnonymous 群组匿名 +// +// https://beautyyu.one +// @route(set_group_anonymous) +// @default(enable=true) +func (bot *CQBot) CQSetGroupAnonymous(groupID int64, enable bool) global.MSG { + if g := bot.Client.FindGroup(groupID); g != nil { + g.SetAnonymous(enable) + return OK(nil) + } + return Failed(100, "GROUP_NOT_FOUND", "群聊不存在") +} + // CQGetGroupHonorInfo 获取群荣誉信息 // // https://git.io/Jtz1H diff --git a/go.mod b/go.mod index 833f73ef0..b4c346f44 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd + github.com/Mrs4s/MiraiGo v0.0.0-20230131090543-15069b74b35d github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 016fc5bc3..30b330842 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2y github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd h1:rzAbPc++5CJ1VZDjq/eORXOWMMGsDN3DMAPMXfI7Fvs= github.com/Mrs4s/MiraiGo v0.0.0-20221202060717-4658474c60dd/go.mod h1:lecSP26qedhinCceWn1x02dLDxGotH5nTFlpIMilmVM= +github.com/Mrs4s/MiraiGo v0.0.0-20230131090543-15069b74b35d h1:R1Sd1psdee4pXYZk47u2GqSe6Wit87aGJZen/f2CFq0= +github.com/Mrs4s/MiraiGo v0.0.0-20230131090543-15069b74b35d/go.mod h1:EAJPnMB7OYB4pLCv/QZlfCrtW34lmRuNNnQevDX5HDY= github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0 h1:GEwcB4dL9vc4veW1fLNt0Fby3wspVflAn5v9/HbUwDM= github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= @@ -52,6 +54,7 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= diff --git a/modules/api/api.go b/modules/api/api.go index e13f8c80e..a381005ee 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -267,6 +267,13 @@ func (c *Caller) call(action string, p Getter) global.MSG { p2 = pt.Bool() } return c.bot.CQSetGroupAdmin(p0, p1, p2) + case "set_group_anonymous": + p0 := p.Get("group_id").Int() + p1 := true + if pt := p.Get("enable"); pt.Exists() { + p1 = pt.Bool() + } + return c.bot.CQSetGroupAnonymous(p0, p1) case "set_group_anonymous_ban": p0 := p.Get("group_id").Int() p1 := p.Get("[anonymous_flag,anonymous.flag].0").String() From d96f840d7fd4be39bf19c36d1c915da452c4df07 Mon Sep 17 00:00:00 2001 From: ctaoist <14012127+ctaoist@users.noreply.github.com> Date: Fri, 3 Feb 2023 23:59:05 +0800 Subject: [PATCH 517/612] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=96=E5=87=BA?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=97=B6LocalImageElement=E7=BC=BA=E5=A4=B1?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20(#1884)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/cqcode.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index ab73c5f13..4258557be 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -282,6 +282,18 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. {K: "type", V: "sticker"}, }, } + case *LocalImageElement: + data := pairs{ + {K: "file", V: o.File}, + {K: "url", V: o.URL}, + } + if o.Flash { + data = append(data, pair{K: "type", V: "flash"}) + } + m = cqcode.Element{ + Type: "image", + Data: data, + } default: continue } From bbef3300696ca9ccf0be9454a9204d68a0732e70 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 4 Feb 2023 13:01:16 +0800 Subject: [PATCH 518/612] server: add a error log --- server/websocket.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server/websocket.go b/server/websocket.go index 668d5ec3e..dc26e6577 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -467,14 +467,16 @@ func (s *webSocketServer) listenAPI(c *wsConn) { func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { defer func() { if err := recover(); err != nil { - log.Printf("处置WS命令时发生无法恢复的异常:%v\n%s", err, debug.Stack()) + log.Errorf("处置WS命令时发生无法恢复的异常:%v\n%s", err, debug.Stack()) _ = c.Close() } }() + j := gjson.Parse(utils.B2S(payload)) t := strings.TrimSuffix(j.Get("action").Str, "_async") - log.Debugf("WS接收到API调用: %v 参数: %v", t, j.Get("params").Raw) - ret := c.apiCaller.Call(t, j.Get("params")) + params := j.Get("params") + log.Debugf("WS接收到API调用: %v 参数: %v", t, params.Raw) + ret := c.apiCaller.Call(t, params) if j.Get("echo").Exists() { ret["echo"] = j.Get("echo").Value() } @@ -482,7 +484,11 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { c.mu.Lock() defer c.mu.Unlock() _ = c.conn.SetWriteDeadline(time.Now().Add(time.Second * 15)) - writer, _ := c.conn.NextWriter(websocket.TextMessage) + writer, err := c.conn.NextWriter(websocket.TextMessage) + if err != nil { + log.Errorf("无法响应API调用(连接已断开?): %v", err) + return + } _ = json.NewEncoder(writer).Encode(ret) _ = writer.Close() } From 2483eb09c44e50ec349b9024a438038f88b37b04 Mon Sep 17 00:00:00 2001 From: Pandaft <80463781+Pandaft@users.noreply.github.com> Date: Sat, 4 Feb 2023 13:07:34 +0800 Subject: [PATCH 519/612] fix: set_group_ban limit error (#1846) * fix: set_group_ban limit error --- coolq/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/api.go b/coolq/api.go index f27305fef..28a19296f 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1166,7 +1166,7 @@ func (bot *CQBot) CQSetGroupBan(groupID, userID int64, duration uint32) global.M if m := g.FindMember(userID); m != nil { err := m.Mute(duration) if err != nil { - if duration > 2592000 { + if duration >= 2592000 { return Failed(100, "DURATION_IS_NOT_IN_RANGE", "非法的禁言时长") } return Failed(100, "NOT_MANAGEABLE", "机器人权限不足") From 0f0ccf459fc80c612fdbdf8c3ce07389551de81f Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 6 Feb 2023 20:39:06 +0800 Subject: [PATCH 520/612] all: update go 1.20 --- .github/workflows/ci.yml | 2 +- .github/workflows/golint.yml | 2 +- .github/workflows/release.yml | 2 +- Dockerfile | 2 +- go.mod | 6 +++--- go.sum | 8 ++++---- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e1cee170..4747ab197 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v2.1.3 with: - go-version: 1.19 + go-version: '1.20' - name: Cache downloaded module uses: actions/cache@v2 with: diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index 89fffe563..75d451a6e 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -12,7 +12,7 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v2.1.3 with: - go-version: 1.19 + go-version: '1.20' - name: golangci-lint uses: golangci/golangci-lint-action@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e53dc2c11..b3496afaf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: '1.19' + go-version: '1.20' - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 diff --git a/Dockerfile b/Dockerfile index ba2c97525..869edff65 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.19-alpine AS builder +FROM golang:1.20-alpine AS builder RUN go env -w GO111MODULE=auto \ && go env -w CGO_ENABLED=0 \ diff --git a/go.mod b/go.mod index f2ea3e656..cccba24eb 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/Mrs4s/go-cqhttp -go 1.19 +go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230131090543-15069b74b35d + github.com/Mrs4s/MiraiGo v0.0.0-20230206122951-91f9576e4815 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 @@ -31,7 +31,7 @@ require ( require ( github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b // indirect - github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0 // indirect + github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fumiama/imgsz v0.0.2 // indirect github.com/golang/snappy v0.0.4 // indirect diff --git a/go.sum b/go.sum index 6be3ac641..c3f914c9f 100644 --- a/go.sum +++ b/go.sum @@ -4,10 +4,10 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230131090543-15069b74b35d h1:R1Sd1psdee4pXYZk47u2GqSe6Wit87aGJZen/f2CFq0= -github.com/Mrs4s/MiraiGo v0.0.0-20230131090543-15069b74b35d/go.mod h1:EAJPnMB7OYB4pLCv/QZlfCrtW34lmRuNNnQevDX5HDY= -github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0 h1:GEwcB4dL9vc4veW1fLNt0Fby3wspVflAn5v9/HbUwDM= -github.com/RomiChan/protobuf v0.0.0-20220624030127-3310cba9dbc0/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= +github.com/Mrs4s/MiraiGo v0.0.0-20230206122951-91f9576e4815 h1:Cmy4RFT5RurAyj3h2CdUQ76+T2P2OUic27Ancsin9V0= +github.com/Mrs4s/MiraiGo v0.0.0-20230206122951-91f9576e4815/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= +github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w= github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0= From 17420feeac372ea7c7ef6f989bde0a4decb44dcb Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 9 Feb 2023 22:33:15 +0800 Subject: [PATCH 521/612] coolq: add sign in api get_stranger_info Fixes #1853 --- coolq/api.go | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index 28a19296f..7b67f88a4 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1431,6 +1431,7 @@ func (bot *CQBot) CQGetStrangerInfo(userID int64) global.MSG { // unknown = 0x2 return "unknown" }(), + "sign": info.Sign, "age": info.Age, "level": info.Level, "login_days": info.LoginDays, diff --git a/go.mod b/go.mod index cccba24eb..773ec7161 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230206122951-91f9576e4815 + github.com/Mrs4s/MiraiGo v0.0.0-20230209142832-f56bcbe3269b github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index c3f914c9f..84b582695 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230206122951-91f9576e4815 h1:Cmy4RFT5RurAyj3h2CdUQ76+T2P2OUic27Ancsin9V0= -github.com/Mrs4s/MiraiGo v0.0.0-20230206122951-91f9576e4815/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230209142832-f56bcbe3269b h1:jCFQnNUqEk03YwgxwwSMzbiyhonbnIycelZV9MbM5ic= +github.com/Mrs4s/MiraiGo v0.0.0-20230209142832-f56bcbe3269b/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 0a4f849154c2bc3728ba7fcdbf9c4901fe3af27f Mon Sep 17 00:00:00 2001 From: AkagiYui Date: Thu, 9 Feb 2023 22:35:34 +0800 Subject: [PATCH 522/612] fix: BINARY_NAME wrong in ci action (#1898) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4747ab197..eebf2ac18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: run: | if [ $GOOS = "windows" ]; then export BINARY_SUFFIX="$BINARY_SUFFIX.exe"; fi if $IS_PR ; then echo $PR_PROMPT; fi - export BINARY_NAME="$BINARY_PREFIX$GOOS_$GOARCH$BINARY_SUFFIX" + export BINARY_NAME="$BINARY_PREFIX"$GOOS"_$GOARCH$BINARY_SUFFIX" export CGO_ENABLED=0 export LD_FLAGS="-w -s -X github.com/Mrs4s/go-cqhttp/internal/base.Version=${COMMIT_ID::7}" go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . From a7c003d404e8e567b52273a3f763d31810073d33 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 13 Feb 2023 21:29:17 +0800 Subject: [PATCH 523/612] update MiraiGo --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 773ec7161..be7fe1567 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230209142832-f56bcbe3269b + github.com/Mrs4s/MiraiGo v0.0.0-20230213132655-3ff1fee1b645 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 84b582695..6e43c31bb 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230209142832-f56bcbe3269b h1:jCFQnNUqEk03YwgxwwSMzbiyhonbnIycelZV9MbM5ic= -github.com/Mrs4s/MiraiGo v0.0.0-20230209142832-f56bcbe3269b/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230213132655-3ff1fee1b645 h1:KHWuWmhF2nacb2mKqA3OJorerCEo9n6BNizMuBACa38= +github.com/Mrs4s/MiraiGo v0.0.0-20230213132655-3ff1fee1b645/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 6b3aabd9afa00e5cf5139d5754f176039c7b954c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 14 Feb 2023 23:27:15 +0800 Subject: [PATCH 524/612] update MiraiGo --- cmd/gocq/main.go | 11 +++++++---- coolq/api.go | 2 +- coolq/api_v12.go | 3 ++- coolq/converter.go | 2 ++ go.mod | 2 +- go.sum | 4 ++-- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index adf5a9916..9d194f2d7 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -136,14 +136,16 @@ func Main() { log.SetLevel(log.DebugLevel) log.Warnf("已开启Debug模式.") } + var device *client.DeviceInfo if !global.PathExists("device.json") { log.Warn("虚拟设备信息不存在, 将自动生成随机设备.") - client.GenRandomDevice() - _ = os.WriteFile("device.json", client.SystemDeviceInfo.ToJson(), 0o644) + device = client.GenRandomDevice() + _ = os.WriteFile("device.json", device.ToJson(), 0o644) log.Info("已生成设备信息并保存到 device.json 文件.") } else { log.Info("将使用 device.json 内的设备信息运行Bot.") - if err := client.SystemDeviceInfo.ReadJson([]byte(global.ReadAllText("device.json"))); err != nil { + device = new(client.DeviceInfo) + if err := device.ReadJson([]byte(global.ReadAllText("device.json"))); err != nil { log.Fatalf("加载设备信息失败: %v", err) } } @@ -205,8 +207,9 @@ func Main() { time.Sleep(time.Second * 5) } log.Info("开始尝试登录并同步消息...") - log.Infof("使用协议: %s", client.SystemDeviceInfo.Protocol) + log.Infof("使用协议: %s", device.Protocol) cli = newClient() + cli.UseDevice(device) isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt isTokenLogin := false saveToken := func() { diff --git a/coolq/api.go b/coolq/api.go index abf1a275b..27a5b4193 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -2012,7 +2012,7 @@ func (bot *CQBot) CQGetVersionInfo() global.MSG { "runtime_version": runtime.Version(), "runtime_os": runtime.GOOS, "version": base.Version, - "protocol_name": client.SystemDeviceInfo.Protocol, + "protocol_name": bot.Client.Device().Protocol, }) } diff --git a/coolq/api_v12.go b/coolq/api_v12.go index 997606b3f..95a432da5 100644 --- a/coolq/api_v12.go +++ b/coolq/api_v12.go @@ -3,9 +3,10 @@ package coolq import ( "runtime" + "github.com/tidwall/gjson" + "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" - "github.com/tidwall/gjson" ) // CQGetVersion 获取版本信息 OneBotV12 diff --git a/coolq/converter.go b/coolq/converter.go index 5fb72b49f..5ec30c4ed 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -13,6 +13,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) +// IDConverter id 转换 type IDConverter func(id any) any func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG { @@ -224,6 +225,7 @@ func fU64(v uint64) string { return strconv.FormatUint(v, 10) } +// ConvertIDWithVersion id 转换 func ConvertIDWithVersion(v any, version uint16) any { if version == 12 { return fmt.Sprint(v) diff --git a/go.mod b/go.mod index be7fe1567..9b2c7525d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230213132655-3ff1fee1b645 + github.com/Mrs4s/MiraiGo v0.0.0-20230214152309-cfd7d92d1f67 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 6e43c31bb..30dce95d1 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230213132655-3ff1fee1b645 h1:KHWuWmhF2nacb2mKqA3OJorerCEo9n6BNizMuBACa38= -github.com/Mrs4s/MiraiGo v0.0.0-20230213132655-3ff1fee1b645/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230214152309-cfd7d92d1f67 h1:KUNf7psf+/2ms6ci9W2ouH+eeAzVpjZv/IW5KIDR8Mg= +github.com/Mrs4s/MiraiGo v0.0.0-20230214152309-cfd7d92d1f67/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From cf717ad7620ea83520dc6b2ed65c6b4bcd798f1c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 15 Feb 2023 14:24:57 +0800 Subject: [PATCH 525/612] internal/onebot: new package --- cmd/api-generator/main.go | 36 ++++--- cmd/api-generator/supported.go | 44 +++++++++ coolq/api.go | 9 ++ internal/onebot/onebot.go | 41 ++++++++ internal/onebot/spec.go | 23 +++++ internal/onebot/supported.go | 168 +++++++++++++++++++++++++++++++++ modules/api/api.go | 5 +- modules/api/caller.go | 2 +- 8 files changed, 313 insertions(+), 15 deletions(-) create mode 100644 cmd/api-generator/supported.go create mode 100644 internal/onebot/onebot.go create mode 100644 internal/onebot/spec.go create mode 100644 internal/onebot/supported.go diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index 18f96dc13..da4823676 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -15,6 +15,11 @@ import ( "strings" ) +var supported = flag.Bool("supported", false, "genRouter supported.go") +var output = flag.String("o", "", "output file") +var pkg = flag.String("pkg", "", "package name") +var src = flag.String("path", "", "source file") + type Param struct { Name string Type string @@ -44,7 +49,16 @@ func (g *generator) WriteString(s string) { io.WriteString(g.out, s) } -func (g *generator) generate(routers []Router) { +func (g *generator) writef(format string, a ...any) { + fmt.Fprintf(g.out, format, a...) +} + +func (g *generator) header() { + g.WriteString("// Code generated by cmd/api-generator. DO NOT EDIT.\n\n") + g.writef("package %s\n\n", *pkg) +} + +func (g *generator) genRouter(routers []Router) { var actions []string // for onebot v12 get_supported_actions for _, router := range routers { if len(router.PathV12) > 0 { @@ -59,21 +73,17 @@ func (g *generator) generate(routers []Router) { } // TODO: v12 和 all 的 switch-case 由常量改为数组寻址, 以利用 get_supported_actions - g.WriteString("// Code generated by cmd/api-generator. DO NOT EDIT.\n\n") - g.WriteString("package api\n\nimport (\n\n") + g.WriteString("import (\n\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/coolq\"\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/global\"\n") g.WriteString(")\n\n") - g.WriteString(fmt.Sprintf(`func (c *Caller) call(action string, version uint16, p Getter) global.MSG { + g.WriteString(`func (c *Caller) call(action string, version uint16, p Getter) global.MSG { var converter coolq.IDConverter = func(id any) any { return coolq.ConvertIDWithVersion(id,version) } if version == 12 { - if action == "get_supported_actions" { - return coolq.OK([]string{%v}) - } switch action { -`, strings.Join(actions, ","))) +`) for _, router := range routers { g.router(router, PathV12) } @@ -194,7 +204,6 @@ func conv(v, t string) string { func main() { var routers []Router - src := flag.String("path", "", "source file") flag.Parse() fset := token.NewFileSet() for _, s := range strings.Split(*src, ",") { @@ -304,12 +313,17 @@ func main() { out := new(bytes.Buffer) g := &generator{out: out} - g.generate(routers) + g.header() + if *supported { + g.genSupported(routers) + } else { + g.genRouter(routers) + } source, err := format.Source(out.Bytes()) if err != nil { panic(err) } - err = os.WriteFile("api.go", source, 0o644) + err = os.WriteFile(*output, source, 0o644) if err != nil { panic(err) } diff --git a/cmd/api-generator/supported.go b/cmd/api-generator/supported.go new file mode 100644 index 000000000..b72d314dc --- /dev/null +++ b/cmd/api-generator/supported.go @@ -0,0 +1,44 @@ +package main + +import "html/template" + +func (g *generator) genSupported(routers []Router) { + var v11, v12 []string // for onebot v12 get_supported_actions + for _, router := range routers { + if len(router.PathV11) > 0 { + v11 = append(v11, router.PathV11...) + } + if len(router.PathV11) > 0 { + v12 = append(v12, router.PathV12...) + } + if len(router.Path) > 0 { + v11 = append(v11, router.Path...) + v12 = append(v12, router.Path...) + } + } + + type S struct { + V11 []string + V12 []string + } + + tmpl, err := template.New("").Parse(supportedTemplete) + if err != nil { + panic(err) + } + err = tmpl.Execute(g.out, &S{V11: v11, V12: v12}) + if err != nil { + panic(err) + } +} + +const supportedTemplete = ` +var supportedV11 = []string{ + {{range .V11}} "{{.}}", +{{end}} +} + +var supportedV12 = []string{ + {{range .V12}} "{{.}}", +{{end}} +}` diff --git a/coolq/api.go b/coolq/api.go index 27a5b4193..a11947bc5 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -27,6 +27,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/download" + "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/filter" ) @@ -2105,6 +2106,14 @@ func (bot *CQBot) CQReloadEventFilter(file string) global.MSG { return OK(nil) } +// CQGetSupportedActions 获取支持的动作列表 +// +// @route12(get_supported_actions) +func (bot *CQBot) CQGetSupportedActions() global.MSG { + // TODO: fix v11 + return OK(onebot.V12.SupportedActions) +} + // OK 生成成功返回值 func OK(data any) global.MSG { return global.MSG{"data": data, "retcode": 0, "status": "ok", "message": ""} diff --git a/internal/onebot/onebot.go b/internal/onebot/onebot.go new file mode 100644 index 000000000..617d67425 --- /dev/null +++ b/internal/onebot/onebot.go @@ -0,0 +1,41 @@ +package onebot + +// Self 机器人自身标识 +// +// https://12.onebot.dev/connect/data-protocol/basic-types/#_10 +type Self struct { + Platform string `json:"platform"` + UserID string `json:"user_id"` +} + +// Request 动作请求是应用端为了主动向 OneBot 实现请求服务而发送的数据 +// +// https://12.onebot.dev/connect/data-protocol/action-request/ +type Request struct { + Action string // 动作名称 + Params any // 动作参数 + Echo any // 每次请求的唯一标识 +} + +// Response 动作响应是 OneBot 实现收到应用端的动作请求并处理完毕后,发回应用端的数据 +// +// https://12.onebot.dev/connect/data-protocol/action-response/ +type Response struct { + Status string `json:"status"` // 执行状态,必须是 ok、failed 中的一个 + Code int64 `json:"retcode"` // 返回码 + Data any `json:"data"` // 响应数据 + Message string `json:"message"` // 错误信息 + Echo any `json:"echo"` // 动作请求中的 echo 字段值 +} + +// Event 事件 +// +// https://12.onebot.dev/connect/data-protocol/event/ +type Event struct { + ID string + Time int64 + Type string + DetailType string + SubType string + Self *Self +} diff --git a/internal/onebot/spec.go b/internal/onebot/spec.go new file mode 100644 index 000000000..c1eb377d1 --- /dev/null +++ b/internal/onebot/spec.go @@ -0,0 +1,23 @@ +// Package onebot defines onebot protocol struct and some spec info. +package onebot + +//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -pkg onebot -path=./../../coolq/api.go,./../../coolq/api_v12.go -supported -o supported.go + +// Spec OneBot Specification +type Spec struct { + Version int // must be 11 or 12 + SupportedActions []string +} + +/* // TODO: Use this variable +var V11 = &Spec{ + Version: 11, + SupportedActions: supportedV11, +} +*/ + +// V12 OneBot V12 +var V12 = &Spec{ + Version: 12, + SupportedActions: supportedV12, +} diff --git a/internal/onebot/supported.go b/internal/onebot/supported.go new file mode 100644 index 000000000..fe4aed1bc --- /dev/null +++ b/internal/onebot/supported.go @@ -0,0 +1,168 @@ +// Code generated by cmd/api-generator. DO NOT EDIT. + +package onebot + +var supportedV11 = []string{ + ".get_word_slices", + ".handle_quick_operation", + ".ocr_image", + "ocr_image", + "_del_group_notice", + "_get_group_notice", + "_get_model_show", + "_send_group_notice", + "_set_model_show", + "can_send_image", + "can_send_record", + "check_url_safely", + "create_group_file_folder", + "create_guild_role", + "delete_essence_msg", + "delete_friend", + "delete_group_file", + "delete_group_folder", + "delete_guild_role", + "delete_msg", + "delete_unidirectional_friend", + "download_file", + "get_essence_msg_list", + "get_forward_msg", + "get_friend_list", + "get_group_at_all_remain", + "get_group_file_system_info", + "get_group_file_url", + "get_group_files_by_folder", + "get_group_honor_info", + "get_group_info", + "get_group_list", + "get_group_member_info", + "get_group_member_list", + "get_group_msg_history", + "get_group_root_files", + "get_group_system_msg", + "get_guild_channel_list", + "get_guild_list", + "get_guild_member_list", + "get_guild_member_profile", + "get_guild_meta_by_guest", + "get_guild_msg", + "get_guild_roles", + "get_guild_service_profile", + "get_image", + "get_login_info", + "get_msg", + "get_online_clients", + "get_status", + "get_stranger_info", + "get_topic_channel_feeds", + "get_unidirectional_friend_list", + "get_version_info", + "mark_msg_as_read", + "qidian_get_account_info", + "reload_event_filter", + "send_forward_msg", + "send_group_forward_msg", + "send_group_msg", + "send_group_sign", + "send_guild_channel_msg", + "send_msg", + "send_private_forward_msg", + "send_private_msg", + "set_essence_msg", + "set_friend_add_request", + "set_group_add_request", + "set_group_admin", + "set_group_anonymous", + "set_group_anonymous_ban", + "set_group_ban", + "set_group_card", + "set_group_kick", + "set_group_leave", + "set_group_name", + "set_group_portrait", + "set_group_special_title", + "set_group_whole_ban", + "set_guild_member_role", + "set_qq_profile", + "update_guild_role", + "upload_group_file", + "upload_private_file", +} + +var supportedV12 = []string{ + ".get_word_slices", + ".handle_quick_operation", + ".ocr_image", + "ocr_image", + "_del_group_notice", + "_get_group_notice", + "_get_model_show", + "_send_group_notice", + "_set_model_show", + "check_url_safely", + "create_group_file_folder", + "create_guild_role", + "delete_essence_msg", + "delete_friend", + "delete_group_file", + "delete_group_folder", + "delete_guild_role", + "delete_msg", + "delete_unidirectional_friend", + "download_file", + "get_essence_msg_list", + "get_forward_msg", + "get_friend_list", + "get_group_at_all_remain", + "get_group_file_system_info", + "get_group_file_url", + "get_group_files_by_folder", + "get_group_honor_info", + "get_group_info", + "get_group_list", + "get_group_member_info", + "get_group_member_list", + "get_group_msg_history", + "get_group_root_files", + "get_group_system_msg", + "get_guild_channel_list", + "get_guild_list", + "get_guild_member_list", + "get_guild_member_profile", + "get_guild_meta_by_guest", + "get_guild_msg", + "get_guild_roles", + "get_guild_service_profile", + "get_image", + "get_self_info", + "get_msg", + "get_online_clients", + "get_status", + "get_user_info", + "get_topic_channel_feeds", + "get_unidirectional_friend_list", + "mark_msg_as_read", + "qidian_get_account_info", + "reload_event_filter", + "send_group_sign", + "send_guild_channel_msg", + "set_essence_msg", + "set_friend_add_request", + "set_group_add_request", + "set_group_admin", + "set_group_anonymous", + "set_group_anonymous_ban", + "set_group_ban", + "set_group_card", + "set_group_kick", + "set_group_leave", + "set_group_name", + "set_group_portrait", + "set_group_special_title", + "set_group_whole_ban", + "set_guild_member_role", + "set_qq_profile", + "update_guild_role", + "upload_group_file", + "upload_private_file", +} diff --git a/modules/api/api.go b/modules/api/api.go index 9ba870fc3..76b92060d 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -12,15 +12,14 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { return coolq.ConvertIDWithVersion(id, version) } if version == 12 { - if action == "get_supported_actions" { - return coolq.OK([]string{".get_word_slices", ".handle_quick_operation", ".ocr_image", "ocr_image", "_get_group_notice", "_get_model_show", "_send_group_notice", "_set_model_show", "check_url_safely", "create_group_file_folder", "create_guild_role", "delete_essence_msg", "delete_friend", "delete_group_file", "delete_group_folder", "delete_guild_role", "delete_msg", "delete_unidirectional_friend", "download_file", "get_essence_msg_list", "get_forward_msg", "get_friend_list", "get_group_at_all_remain", "get_group_file_system_info", "get_group_file_url", "get_group_files_by_folder", "get_group_honor_info", "get_group_info", "get_group_list", "get_group_member_info", "get_group_member_list", "get_group_msg_history", "get_group_root_files", "get_group_system_msg", "get_guild_channel_list", "get_guild_list", "get_guild_member_list", "get_guild_member_profile", "get_guild_meta_by_guest", "get_guild_msg", "get_guild_roles", "get_guild_service_profile", "get_image", "get_self_info", "get_msg", "get_online_clients", "get_status", "get_user_info", "get_topic_channel_feeds", "get_unidirectional_friend_list", "get_version", "mark_msg_as_read", "qidian_get_account_info", "reload_event_filter", "send_group_sign", "send_guild_channel_msg", "send_message", "set_essence_msg", "set_friend_add_request", "set_group_add_request", "set_group_admin", "set_group_anonymous_ban", "set_group_ban", "set_group_card", "set_group_kick", "set_group_leave", "set_group_name", "set_group_portrait", "set_group_special_title", "set_group_whole_ban", "set_guild_member_role", "set_qq_profile", "update_guild_role", "upload_group_file"}) - } switch action { case "get_self_info": return c.bot.CQGetLoginInfo() case "get_user_info": p0 := p.Get("user_id").Int() return c.bot.CQGetStrangerInfo(p0) + case "get_supported_actions": + return c.bot.CQGetSupportedActions() case "get_version": return c.bot.CQGetVersion() case "send_message": diff --git a/modules/api/caller.go b/modules/api/caller.go index 7105fd422..8a47bd47a 100644 --- a/modules/api/caller.go +++ b/modules/api/caller.go @@ -8,7 +8,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) -//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -path=./../../coolq/api.go,./../../coolq/api_v12.go +//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -pkg api -path=./../../coolq/api.go,./../../coolq/api_v12.go -o api.go // Getter 参数获取 type Getter interface { From 9c0525b3d4f70b620e4b91f5b85f48ab4fbbe802 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 15 Feb 2023 21:49:05 +0800 Subject: [PATCH 526/612] all: use *onebot.Spec --- cmd/api-generator/main.go | 24 +++++++++--------------- coolq/api.go | 27 +++++++++++++-------------- coolq/bot.go | 3 ++- coolq/converter.go | 12 ------------ internal/onebot/spec.go | 12 ++++++++++-- internal/onebot/supported.go | 3 ++- modules/api/api.go | 30 ++++++++++++++---------------- modules/api/caller.go | 5 +++-- server/http.go | 21 +++++++++++++-------- server/websocket.go | 3 ++- 10 files changed, 68 insertions(+), 72 deletions(-) diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index da4823676..031e493ee 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -76,12 +76,10 @@ func (g *generator) genRouter(routers []Router) { g.WriteString("import (\n\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/coolq\"\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/global\"\n") + g.WriteString("\"github.com/Mrs4s/go-cqhttp/internal/onebot\"\n") g.WriteString(")\n\n") - g.WriteString(`func (c *Caller) call(action string, version uint16, p Getter) global.MSG { - var converter coolq.IDConverter = func(id any) any { - return coolq.ConvertIDWithVersion(id,version) - } - if version == 12 { + g.WriteString(`func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG { + if spec.Version == 12 { switch action { `) for _, router := range routers { @@ -89,7 +87,7 @@ func (g *generator) genRouter(routers []Router) { } io.WriteString(g.out, `}}`) io.WriteString(g.out, "\n") - g.WriteString(`if version == 11 { + g.WriteString(`if spec.Version == 11 { switch action { `) for _, router := range routers { @@ -141,14 +139,14 @@ func (g *generator) router(router Router, pathVersion int) { if len(router.Version) == 1 { // 目前来说只需要判断一个版本的情况 check := make([]string, 0, len(router.Version)) for _, ver := range router.Version { - check = append(check, fmt.Sprintf("version != %v", ver)) + check = append(check, fmt.Sprintf("spec.Version != %v", ver)) } fmt.Fprintf(g.out, "if %v {\n", strings.Join(check, " && ")) fmt.Fprintf(g.out, "return coolq.Failed(405, \"VERSION_ERROR\", \"API版本不匹配\")}\n") } for i, p := range router.Params { - if p.Name == "version" || p.Name == "converter" { + if p.Name == "spec" { continue } if p.Default == "" { @@ -166,12 +164,8 @@ func (g *generator) router(router Router, pathVersion int) { if i != 0 { g.WriteString(", ") } - if p.Name == "version" { - fmt.Fprintf(g.out, "version") - continue - } - if p.Name == "converter" { - fmt.Fprintf(g.out, "converter") + if p.Name == "spec" { + fmt.Fprintf(g.out, "spec") continue } fmt.Fprintf(g.out, "p%d", i) @@ -183,7 +177,7 @@ func conv(v, t string) string { switch t { default: panic("unknown type: " + t) - case "gjson.Result", "IDConverter": + case "gjson.Result", "*onebot.Spec", "IDConverter": return v case "int64": return v + ".Int()" diff --git a/coolq/api.go b/coolq/api.go index a11947bc5..e08bada6a 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -328,13 +328,13 @@ func (bot *CQBot) CQGetTopicChannelFeeds(guildID, channelID uint64) global.MSG { // // https://git.io/Jtz1L // @route(get_friend_list) -func (bot *CQBot) CQGetFriendList(version uint16) global.MSG { +func (bot *CQBot) CQGetFriendList(spec *onebot.Spec) global.MSG { fs := make([]global.MSG, 0, len(bot.Client.FriendList)) for _, f := range bot.Client.FriendList { fs = append(fs, global.MSG{ "nickname": f.Nickname, "remark": f.Remark, - "user_id": ConvertIDWithVersion(f.Uin, version), + "user_id": spec.ConvertID(f.Uin), }) } return OK(fs) @@ -400,14 +400,14 @@ func (bot *CQBot) CQDeleteFriend(uin int64) global.MSG { // // https://git.io/Jtz1t // @route(get_group_list) -func (bot *CQBot) CQGetGroupList(noCache bool, converter IDConverter) global.MSG { +func (bot *CQBot) CQGetGroupList(noCache bool, spec *onebot.Spec) global.MSG { gs := make([]global.MSG, 0, len(bot.Client.GroupList)) if noCache { _ = bot.Client.ReloadGroupList() } for _, g := range bot.Client.GroupList { gs = append(gs, global.MSG{ - "group_id": converter(g.Code), + "group_id": spec.ConvertID(g.Code), "group_name": g.Name, "group_create_time": g.GroupCreateTime, "group_level": g.GroupLevel, @@ -422,7 +422,7 @@ func (bot *CQBot) CQGetGroupList(noCache bool, converter IDConverter) global.MSG // // https://git.io/Jtz1O // @route(get_group_info) -func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool, converter IDConverter) global.MSG { +func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool, spec *onebot.Spec) global.MSG { group := bot.Client.FindGroup(groupID) if group == nil || noCache { group, _ = bot.Client.GetGroupInfo(groupID) @@ -436,7 +436,7 @@ func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool, converter IDConver for _, g := range info { if g.Code == groupID { return OK(global.MSG{ - "group_id": converter(g.Code), + "group_id": spec.ConvertID(g.Code), "group_name": g.Name, "group_memo": g.Memo, "group_create_time": 0, @@ -448,7 +448,7 @@ func (bot *CQBot) CQGetGroupInfo(groupID int64, noCache bool, converter IDConver } } else { return OK(global.MSG{ - "group_id": converter(group.Code), + "group_id": spec.ConvertID(group.Code), "group_name": group.Name, "group_create_time": group.GroupCreateTime, "group_level": group.GroupLevel, @@ -1445,7 +1445,7 @@ func (bot *CQBot) CQGetStrangerInfo(userID int64) global.MSG { // CQHandleQuickOperation 隐藏API-对事件执行快速操作 // // https://git.io/Jtz15 -// @route(".handle_quick_operation") +// @route11(".handle_quick_operation") func (bot *CQBot) CQHandleQuickOperation(context, operation gjson.Result) global.MSG { postType := context.Get("post_type").Str @@ -1903,8 +1903,8 @@ func (bot *CQBot) CQSetGroupAnonymousBan(groupID int64, flag string, duration in // // https://git.io/JtzMe // @route(get_status) -func (bot *CQBot) CQGetStatus(version uint16) global.MSG { - if version == 11 { +func (bot *CQBot) CQGetStatus(spec *onebot.Spec) global.MSG { + if spec.Version == 11 { return OK(global.MSG{ "app_initialized": true, "app_enabled": true, @@ -2108,10 +2108,9 @@ func (bot *CQBot) CQReloadEventFilter(file string) global.MSG { // CQGetSupportedActions 获取支持的动作列表 // -// @route12(get_supported_actions) -func (bot *CQBot) CQGetSupportedActions() global.MSG { - // TODO: fix v11 - return OK(onebot.V12.SupportedActions) +// @route(get_supported_actions) +func (bot *CQBot) CQGetSupportedActions(spec *onebot.Spec) global.MSG { + return OK(spec.SupportedActions) } // OK 生成成功返回值 diff --git a/coolq/bot.go b/coolq/bot.go index 74ee11657..89cf8d1a9 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -26,6 +26,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/mime" + "github.com/Mrs4s/go-cqhttp/internal/onebot" ) // CQBot CQBot结构体,存储Bot实例相关配置 @@ -114,7 +115,7 @@ func NewQQBot(cli *client.QQClient) *CQBot { for { <-t.C bot.dispatchEvent("meta_event/heartbeat", global.MSG{ - "status": bot.CQGetStatus(11)["data"], + "status": bot.CQGetStatus(onebot.V11)["data"], "interval": base.HeartbeatInterval.Milliseconds(), }) } diff --git a/coolq/converter.go b/coolq/converter.go index 5ec30c4ed..30720e2a8 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -1,7 +1,6 @@ package coolq import ( - "fmt" "strconv" "strings" @@ -13,9 +12,6 @@ import ( "github.com/Mrs4s/go-cqhttp/global" ) -// IDConverter id 转换 -type IDConverter func(id any) any - func convertGroupMemberInfo(groupID int64, m *client.GroupMemberInfo) global.MSG { sex := "unknown" if m.Gender == 1 { // unknown = 0xff @@ -224,11 +220,3 @@ func toStringMessage(m []message.IMessageElement, source message.Source) string func fU64(v uint64) string { return strconv.FormatUint(v, 10) } - -// ConvertIDWithVersion id 转换 -func ConvertIDWithVersion(v any, version uint16) any { - if version == 12 { - return fmt.Sprint(v) - } - return v -} diff --git a/internal/onebot/spec.go b/internal/onebot/spec.go index c1eb377d1..49afe73cc 100644 --- a/internal/onebot/spec.go +++ b/internal/onebot/spec.go @@ -1,6 +1,8 @@ // Package onebot defines onebot protocol struct and some spec info. package onebot +import "fmt" + //go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -pkg onebot -path=./../../coolq/api.go,./../../coolq/api_v12.go -supported -o supported.go // Spec OneBot Specification @@ -9,15 +11,21 @@ type Spec struct { SupportedActions []string } -/* // TODO: Use this variable +// V11 OneBot V11 var V11 = &Spec{ Version: 11, SupportedActions: supportedV11, } -*/ // V12 OneBot V12 var V12 = &Spec{ Version: 12, SupportedActions: supportedV12, } + +func (s *Spec) ConvertID(id any) any { + if s.Version == 12 { + return fmt.Sprint(id) + } + return id +} diff --git a/internal/onebot/supported.go b/internal/onebot/supported.go index fe4aed1bc..d5357d7cf 100644 --- a/internal/onebot/supported.go +++ b/internal/onebot/supported.go @@ -54,6 +54,7 @@ var supportedV11 = []string{ "get_online_clients", "get_status", "get_stranger_info", + "get_supported_actions", "get_topic_channel_feeds", "get_unidirectional_friend_list", "get_version_info", @@ -91,7 +92,6 @@ var supportedV11 = []string{ var supportedV12 = []string{ ".get_word_slices", - ".handle_quick_operation", ".ocr_image", "ocr_image", "_del_group_notice", @@ -139,6 +139,7 @@ var supportedV12 = []string{ "get_online_clients", "get_status", "get_user_info", + "get_supported_actions", "get_topic_channel_feeds", "get_unidirectional_friend_list", "mark_msg_as_read", diff --git a/modules/api/api.go b/modules/api/api.go index 76b92060d..acac76dd1 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -5,21 +5,17 @@ package api import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/onebot" ) -func (c *Caller) call(action string, version uint16, p Getter) global.MSG { - var converter coolq.IDConverter = func(id any) any { - return coolq.ConvertIDWithVersion(id, version) - } - if version == 12 { +func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG { + if spec.Version == 12 { switch action { case "get_self_info": return c.bot.CQGetLoginInfo() case "get_user_info": p0 := p.Get("user_id").Int() return c.bot.CQGetStrangerInfo(p0) - case "get_supported_actions": - return c.bot.CQGetSupportedActions() case "get_version": return c.bot.CQGetVersion() case "send_message": @@ -30,8 +26,12 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { return c.bot.CQSendMessageV12(p0, p1, p2, p3) } } - if version == 11 { + if spec.Version == 11 { switch action { + case ".handle_quick_operation": + p0 := p.Get("context") + p1 := p.Get("operation") + return c.bot.CQHandleQuickOperation(p0, p1) case "can_send_image": return c.bot.CQCanSendImage() case "can_send_record": @@ -81,10 +81,6 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { case ".get_word_slices": p0 := p.Get("content").String() return c.bot.CQGetWordSlices(p0) - case ".handle_quick_operation": - p0 := p.Get("context") - p1 := p.Get("operation") - return c.bot.CQHandleQuickOperation(p0, p1) case ".ocr_image", "ocr_image": p0 := p.Get("image").String() return c.bot.CQOcrImage(p0) @@ -159,7 +155,7 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { p0 := p.Get("[message_id,id].0").String() return c.bot.CQGetForwardMessage(p0) case "get_friend_list": - return c.bot.CQGetFriendList(version) + return c.bot.CQGetFriendList(spec) case "get_group_at_all_remain": p0 := p.Get("group_id").Int() return c.bot.CQGetAtAllRemain(p0) @@ -182,10 +178,10 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { case "get_group_info": p0 := p.Get("group_id").Int() p1 := p.Get("no_cache").Bool() - return c.bot.CQGetGroupInfo(p0, p1, converter) + return c.bot.CQGetGroupInfo(p0, p1, spec) case "get_group_list": p0 := p.Get("no_cache").Bool() - return c.bot.CQGetGroupList(p0, converter) + return c.bot.CQGetGroupList(p0, spec) case "get_group_member_info": p0 := p.Get("group_id").Int() p1 := p.Get("user_id").Int() @@ -240,7 +236,9 @@ func (c *Caller) call(action string, version uint16, p Getter) global.MSG { p0 := p.Get("no_cache").Bool() return c.bot.CQGetOnlineClients(p0) case "get_status": - return c.bot.CQGetStatus(version) + return c.bot.CQGetStatus(spec) + case "get_supported_actions": + return c.bot.CQGetSupportedActions(spec) case "get_topic_channel_feeds": p0 := p.Get("guild_id").Uint() p1 := p.Get("channel_id").Uint() diff --git a/modules/api/caller.go b/modules/api/caller.go index 8a47bd47a..4dfeacf76 100644 --- a/modules/api/caller.go +++ b/modules/api/caller.go @@ -6,6 +6,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/onebot" ) //go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -pkg api -path=./../../coolq/api.go,./../../coolq/api_v12.go -o api.go @@ -25,13 +26,13 @@ type Caller struct { } // Call specific API -func (c *Caller) Call(action string, version uint16, p Getter) global.MSG { +func (c *Caller) Call(action string, spec *onebot.Spec, p Getter) global.MSG { for _, fn := range c.handlers { if ret := fn(action, p); ret != nil { return ret } } - return c.call(action, version, p) + return c.call(action, spec, p) } // Use add handlers to the API caller diff --git a/server/http.go b/server/http.go index 97661304a..98c59e393 100644 --- a/server/http.go +++ b/server/http.go @@ -26,6 +26,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -58,7 +59,7 @@ type httpServerPost struct { type httpServer struct { api *api.Caller accessToken string - version uint16 + spec *onebot.Spec // onebot spec } // HTTPClient 反向HTTP上报客户端 @@ -158,7 +159,7 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request switch request.Method { case http.MethodPost: // todo: msg pack - if s.version == 12 && strings.Contains(contentType, "application/msgpack") { + if s.spec.Version == 12 && strings.Contains(contentType, "application/msgpack") { log.Warnf("请求 %v 数据类型暂不支持: MsgPack", request.RequestURI) writer.WriteHeader(http.StatusUnsupportedMediaType) return @@ -204,12 +205,12 @@ func (s *httpServer) ServeHTTP(writer http.ResponseWriter, request *http.Request if request.URL.Path == "/" { action := strings.TrimSuffix(ctx.Get("action").Str, "_async") log.Debugf("HTTPServer接收到API调用: %v", action) - response = s.api.Call(action, s.version, ctx.Get("params")) + response = s.api.Call(action, s.spec, ctx.Get("params")) } else { action := strings.TrimPrefix(request.URL.Path, "/") action = strings.TrimSuffix(action, "_async") log.Debugf("HTTPServer接收到API调用: %v", action) - response = s.api.Call(action, s.version, &ctx) + response = s.api.Call(action, s.spec, &ctx) } writer.Header().Set("Content-Type", "application/json; charset=utf-8") @@ -259,11 +260,15 @@ func runHTTP(bot *coolq.CQBot, node yaml.Node) { case conf.Disabled: return } - if conf.Version != 11 && conf.Version != 12 { - conf.Version = 11 - } network, addr := "tcp", conf.Address - s := &httpServer{accessToken: conf.AccessToken, version: conf.Version} + s := &httpServer{accessToken: conf.AccessToken} + switch conf.Version { + default: + // default v11 + s.spec = onebot.V11 + case 12: + s.spec = onebot.V12 + } switch { case conf.Address != "": uri, err := url.Parse(conf.Address) diff --git a/server/websocket.go b/server/websocket.go index 71d68bb4b..f5ef674db 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -22,6 +22,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -476,7 +477,7 @@ func (c *wsConn) handleRequest(_ *coolq.CQBot, payload []byte) { t := strings.TrimSuffix(j.Get("action").Str, "_async") params := j.Get("params") log.Debugf("WS接收到API调用: %v 参数: %v", t, params.Raw) - ret := c.apiCaller.Call(t, 11, params) + ret := c.apiCaller.Call(t, onebot.V11, params) if j.Get("echo").Exists() { ret["echo"] = j.Get("echo").Value() } From 43dd9aa76db6671a14f91ac5088270eaae76a0ae Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 15 Feb 2023 22:29:42 +0800 Subject: [PATCH 527/612] rf: move coolq/cqcode to internal/msg --- cmd/api-generator/main.go | 2 +- coolq/api.go | 5 +- coolq/bot.go | 19 +-- coolq/cqcode.go | 140 ++++++------------ coolq/cqcode/escape.go | 79 ---------- coolq/cqcode_test.go | 6 +- {coolq/cqcode => internal/msg}/element.go | 82 +++++++++- .../msg/element_test.go | 2 +- internal/msg/local.go | 44 ++++++ internal/onebot/spec.go | 3 +- modules/api/caller.go | 2 +- 11 files changed, 193 insertions(+), 191 deletions(-) delete mode 100644 coolq/cqcode/escape.go rename {coolq/cqcode => internal/msg}/element.go (63%) rename coolq/cqcode/all_test.go => internal/msg/element_test.go (96%) create mode 100644 internal/msg/local.go diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index 031e493ee..95260b76b 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -177,7 +177,7 @@ func conv(v, t string) string { switch t { default: panic("unknown type: " + t) - case "gjson.Result", "*onebot.Spec", "IDConverter": + case "gjson.Result", "*onebot.Spec": return v case "int64": return v + ".Int()" diff --git a/coolq/api.go b/coolq/api.go index e08bada6a..345c84041 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -27,6 +27,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/download" + "github.com/Mrs4s/go-cqhttp/internal/msg" "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -851,7 +852,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType for i, elem := range elems { p := &elems[i] switch o := elem.(type) { - case *LocalVideoElement: + case *msg.LocalVideo: w.do(func() { gm, err := bot.uploadLocalVideo(source, o) if err != nil { @@ -860,7 +861,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType *p = gm } }) - case *LocalImageElement: + case *msg.LocalImage: w.do(func() { gm, err := bot.uploadLocalImage(source, o) if err != nil { diff --git a/coolq/bot.go b/coolq/bot.go index 89cf8d1a9..268c28b89 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -26,6 +26,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/mime" + "github.com/Mrs4s/go-cqhttp/internal/msg" "github.com/Mrs4s/go-cqhttp/internal/onebot" ) @@ -147,7 +148,7 @@ func (w *worker) wait() { } // uploadLocalImage 上传本地图片 -func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement) (message.IMessageElement, error) { +func (bot *CQBot) uploadLocalImage(target message.Source, img *msg.LocalImage) (message.IMessageElement, error) { if img.File != "" { f, err := os.Open(img.File) if err != nil { @@ -187,20 +188,20 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement } // uploadLocalVideo 上传本地短视频至群聊 -func (bot *CQBot) uploadLocalVideo(target message.Source, v *LocalVideoElement) (*message.ShortVideoElement, error) { +func (bot *CQBot) uploadLocalVideo(target message.Source, v *msg.LocalVideo) (*message.ShortVideoElement, error) { video, err := os.Open(v.File) if err != nil { return nil, err } defer func() { _ = video.Close() }() - return bot.Client.UploadShortVideo(target, video, v.thumb, 4) + return bot.Client.UploadShortVideo(target, video, v.Thumb, 4) } func removeLocalElement(elements []message.IMessageElement) []message.IMessageElement { var j int for i, e := range elements { switch e.(type) { - case *LocalImageElement, *LocalVideoElement: + case *msg.LocalImage, *msg.LocalVideo: case *message.VoiceElement: // 未上传的语音消息, 也删除 case nil: default: @@ -230,7 +231,7 @@ func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessage for i, m := range elements { p := &elements[i] switch e := m.(type) { - case *LocalImageElement: + case *msg.LocalImage: w.do(func() { m, err := bot.uploadLocalImage(target, e) if err != nil { @@ -248,7 +249,7 @@ func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessage *p = m } }) - case *LocalVideoElement: + case *msg.LocalVideo: w.do(func() { m, err := bot.uploadLocalVideo(target, e) if err != nil { @@ -274,7 +275,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) (in m.Elements = bot.uploadMedia(source, m.Elements) for _, e := range m.Elements { switch i := e.(type) { - case *PokeElement: + case *msg.Poke: if group != nil { if mem := group.FindMember(i.Target); mem != nil { mem.Poke() @@ -319,7 +320,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen m.Elements = bot.uploadMedia(source, m.Elements) for _, e := range m.Elements { switch i := e.(type) { - case *PokeElement: + case *msg.Poke: bot.Client.SendFriendPoke(i.Target) return 0 case *message.MusicShareElement: @@ -421,7 +422,7 @@ func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message. bot.Client.SendGuildMusicShare(guildID, channelID, i) return "-1" // todo: fix this - case *message.VoiceElement, *PokeElement: + case *message.VoiceElement, *msg.Poke: log.Warnf("警告: 频道暂不支持发送 %v 消息", i.Type().String()) continue } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 705fab4f9..9d65ebb3d 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -25,64 +25,24 @@ import ( log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" - "github.com/Mrs4s/go-cqhttp/coolq/cqcode" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/download" "github.com/Mrs4s/go-cqhttp/internal/mime" + "github.com/Mrs4s/go-cqhttp/internal/msg" "github.com/Mrs4s/go-cqhttp/internal/param" ) -/* -var matchReg = regexp.MustCompile(`\[CQ:\w+?.*?]`) -var typeReg = regexp.MustCompile(`\[CQ:(\w+)`) -var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`) -*/ - -// PokeElement 拍一拍 -type PokeElement struct { - Target int64 -} - -// LocalImageElement 本地图片 -type LocalImageElement struct { - Stream io.ReadSeeker - File string - URL string - - Flash bool - EffectID int32 -} - -// LocalVideoElement 本地视频 -type LocalVideoElement struct { - File string - thumb io.ReadSeeker -} +// TODO: move this file to internal/msg, internal/onebot +// TODO: support OneBot V12 const ( maxImageSize = 1024 * 1024 * 30 // 30MB maxVideoSize = 1024 * 1024 * 100 // 100MB ) -// Type implements the message.IMessageElement. -func (e *LocalImageElement) Type() message.ElementType { - return message.Image -} - -// Type impl message.IMessageElement -func (e *LocalVideoElement) Type() message.ElementType { - return message.Video -} - -// Type 获取元素类型ID -func (e *PokeElement) Type() message.ElementType { - // Make message.IMessageElement Happy - return message.At -} - func replyID(r *message.ReplyElement, source message.Source) int32 { id := source.PrimaryID seq := r.ReplySeq @@ -102,11 +62,11 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { // toElements 将消息元素数组转为MSG数组以用于消息上报 // // nolint:govet -func toElements(e []message.IMessageElement, source message.Source) (r []cqcode.Element) { - type pair = cqcode.Pair // simplify code +func toElements(e []message.IMessageElement, source message.Source) (r []msg.Element) { + type pair = msg.Pair // simplify code type pairs = []pair - r = make([]cqcode.Element, 0, len(e)) + r = make([]msg.Element, 0, len(e)) m := &message.SendingMessage{Elements: e} reply := m.FirstOrNil(func(e message.IMessageElement) bool { _, ok := e.(*message.ReplyElement) @@ -115,7 +75,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. if reply != nil && source.SourceType&(message.SourceGroup|message.SourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) id := replyID(replyElem, source) - elem := cqcode.Element{ + elem := msg.Element{ Type: "reply", Data: pairs{ {K: "id", V: strconv.FormatInt(int64(id), 10)}, @@ -132,7 +92,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. r = append(r, elem) } for i, elem := range e { - var m cqcode.Element + var m msg.Element switch o := elem.(type) { case *message.ReplyElement: if base.RemoveReplyAt && i+1 < len(e) { @@ -143,14 +103,14 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. } continue case *message.TextElement: - m = cqcode.Element{ + m = msg.Element{ Type: "text", Data: pairs{ {K: "text", V: o.Content}, }, } case *message.LightAppElement: - m = cqcode.Element{ + m = msg.Element{ Type: "json", Data: pairs{ {K: "data", V: o.Content}, @@ -161,35 +121,35 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. if o.Target != 0 { target = strconv.FormatUint(uint64(o.Target), 10) } - m = cqcode.Element{ + m = msg.Element{ Type: "at", Data: pairs{ {K: "qq", V: target}, }, } case *message.RedBagElement: - m = cqcode.Element{ + m = msg.Element{ Type: "redbag", Data: pairs{ {K: "title", V: o.Title}, }, } case *message.ForwardElement: - m = cqcode.Element{ + m = msg.Element{ Type: "forward", Data: pairs{ {K: "id", V: o.ResId}, }, } case *message.FaceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "face", Data: pairs{ {K: "id", V: strconv.FormatInt(int64(o.Index), 10)}, }, } case *message.VoiceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "record", Data: pairs{ {K: "file", V: o.Name}, @@ -197,7 +157,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. }, } case *message.ShortVideoElement: - m = cqcode.Element{ + m = msg.Element{ Type: "video", Data: pairs{ {K: "file", V: o.Name}, @@ -217,7 +177,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. data = append(data, pair{K: "type", V: "show"}) data = append(data, pair{K: "id", V: strconv.FormatInt(int64(o.EffectID), 10)}) } - m = cqcode.Element{ + m = msg.Element{ Type: "image", Data: data, } @@ -226,7 +186,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. {K: "file", V: hex.EncodeToString(o.Md5) + ".image"}, {K: "url", V: o.Url}, } - m = cqcode.Element{ + m = msg.Element{ Type: "image", Data: data, } @@ -238,33 +198,33 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. if o.Flash { data = append(data, pair{K: "type", V: "flash"}) } - m = cqcode.Element{ + m = msg.Element{ Type: "image", Data: data, } case *message.DiceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "dice", Data: pairs{ {K: "value", V: strconv.FormatInt(int64(o.Value), 10)}, }, } case *message.FingerGuessingElement: - m = cqcode.Element{ + m = msg.Element{ Type: "rps", Data: pairs{ {K: "value", V: strconv.FormatInt(int64(o.Value), 10)}, }, } case *message.MarketFaceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "text", Data: pairs{ {K: "text", V: o.Name}, }, } case *message.ServiceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "xml", Data: pairs{ {K: "data", V: o.Content}, @@ -275,14 +235,14 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. m.Type = "json" } case *message.AnimatedSticker: - m = cqcode.Element{ + m = msg.Element{ Type: "face", Data: pairs{ {K: "id", V: strconv.FormatInt(int64(o.ID), 10)}, {K: "type", V: "sticker"}, }, } - case *LocalImageElement: + case *msg.LocalImage: data := pairs{ {K: "file", V: o.File}, {K: "url", V: o.URL}, @@ -290,7 +250,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. if o.Flash { data = append(data, pair{K: "type", V: "flash"}) } - m = cqcode.Element{ + m = msg.Element{ Type: "image", Data: data, } @@ -536,11 +496,11 @@ func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType } if i > 0 { if base.SplitURL { - for _, txt := range param.SplitURL(cqcode.UnescapeText(raw[:i])) { + for _, txt := range param.SplitURL(msg.UnescapeText(raw[:i])) { r = append(r, message.NewText(txt)) } } else { - r = append(r, message.NewText(cqcode.UnescapeText(raw[:i]))) + r = append(r, message.NewText(msg.UnescapeText(raw[:i]))) } } @@ -585,7 +545,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType if i+1 > len(raw) { return } - d[key] = cqcode.UnescapeValue(raw[:i]) + d[key] = msg.UnescapeValue(raw[:i]) raw = raw[i:] i = 0 } @@ -755,7 +715,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message } } switch img := e.(type) { - case *LocalImageElement: + case *msg.LocalImage: img.Flash = flash img.EffectID = id case *message.GroupImageElement: @@ -849,7 +809,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return img, err } switch img := img.(type) { - case *LocalImageElement: + case *msg.LocalImage: img.Flash = flash img.EffectID = int32(id) case *message.GroupImageElement: @@ -863,14 +823,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return img, err case "poke": t, _ := strconv.ParseInt(d["qq"], 10, 64) - return &PokeElement{Target: t}, nil + return &msg.Poke{Target: t}, nil case "tts": - defer func() { - if r := recover(); r != nil { - m = nil - err = errors.New("tts 转换失败") - } - }() data, err := bot.Client.GetTts(d["text"]) if err != nil { return nil, err @@ -1024,10 +978,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return message.NewFingerGuessing(int32(i)), nil case "xml": resID := d["resid"] - template := cqcode.EscapeValue(d["data"]) + template := msg.EscapeValue(d["data"]) i, _ := strconv.ParseInt(resID, 10, 64) - msg := message.NewRichXml(template, i) - return msg, nil + m := message.NewRichXml(template, i) + return m, nil case "json": resID := d["resid"] i, _ := strconv.ParseInt(resID, 10, 64) @@ -1064,7 +1018,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So if err != nil { return nil, err } - v, ok := file.(*LocalVideoElement) + v, ok := file.(*msg.LocalVideo) if !ok { return file, nil } @@ -1081,7 +1035,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So } data, _ = os.ReadFile(v.File + ".jpg") } - v.thumb = bytes.NewReader(data) + v.Thumb = bytes.NewReader(data) video, _ := os.Open(v.File) defer video.Close() _, _ = video.Seek(4, io.SeekStart) @@ -1135,9 +1089,9 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy } useCacheFile: if video { - return &LocalVideoElement{File: cacheFile}, nil + return &msg.LocalVideo{File: cacheFile}, nil } - return &LocalImageElement{File: cacheFile, URL: f}, nil + return &msg.LocalImage{File: cacheFile, URL: f}, nil } if strings.HasPrefix(f, "file") { fu, err := url.Parse(f) @@ -1158,26 +1112,26 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if info.Size() == 0 || info.Size() >= maxVideoSize { return nil, errors.New("invalid video size") } - return &LocalVideoElement{File: fu.Path}, nil + return &msg.LocalVideo{File: fu.Path}, nil } if info.Size() == 0 || info.Size() >= maxImageSize { return nil, errors.New("invalid image size") } - return &LocalImageElement{File: fu.Path, URL: f}, nil + return &msg.LocalImage{File: fu.Path, URL: f}, nil } if !video && strings.HasPrefix(f, "base64") { b, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(f, "base64://")) if err != nil { return nil, err } - return &LocalImageElement{Stream: bytes.NewReader(b), URL: f}, nil + return &msg.LocalImage{Stream: bytes.NewReader(b), URL: f}, nil } if !video && strings.HasPrefix(f, "base16384") { b, err := b14.UTF82UTF16BE(utils.S2B(strings.TrimPrefix(f, "base16384://"))) if err != nil { return nil, err } - return &LocalImageElement{Stream: bytes.NewReader(b14.Decode(b)), URL: f}, nil + return &msg.LocalImage{Stream: bytes.NewReader(b14.Decode(b)), URL: f}, nil } rawPath := path.Join(global.ImagePath, f) if video { @@ -1194,7 +1148,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return nil, errors.New("invalid video") } if path.Ext(rawPath) != ".video" { - return &LocalVideoElement{File: rawPath}, nil + return &msg.LocalVideo{File: rawPath}, nil } b, _ := os.ReadFile(rawPath) return bot.readVideoCache(b), nil @@ -1203,7 +1157,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if sourceType == message.SourceGuildChannel { cacheFile := path.Join(global.ImagePath, "guild-images", f) if global.PathExists(cacheFile) { - return &LocalImageElement{File: cacheFile}, nil + return &msg.LocalImage{File: cacheFile}, nil } } if strings.HasSuffix(f, ".image") { @@ -1222,7 +1176,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return nil, errors.New("invalid image") } if path.Ext(rawPath) != ".image" { - return &LocalImageElement{File: rawPath, URL: u}, nil + return &msg.LocalImage{File: rawPath, URL: u}, nil } b, err := os.ReadFile(rawPath) if err != nil { @@ -1283,7 +1237,7 @@ func (bot *CQBot) makeShowPic(elem message.IMessageElement, source string, brief if brief == "" { brief = "[分享]我看到一张很赞的图片,分享给你,快来看!" } - if local, ok := elem.(*LocalImageElement); ok { + if local, ok := elem.(*msg.LocalImage); ok { r := rand.Uint32() typ := message.SourceGroup if !group { diff --git a/coolq/cqcode/escape.go b/coolq/cqcode/escape.go deleted file mode 100644 index cee90330b..000000000 --- a/coolq/cqcode/escape.go +++ /dev/null @@ -1,79 +0,0 @@ -// Package cqcode provides CQCode util functions. -package cqcode - -import "strings" - -// EscapeText 将字符串raw中部分字符转义 -// -// - & -> & -// - [ -> [ -// - ] -> ] -func EscapeText(s string) string { - count := strings.Count(s, "&") - count += strings.Count(s, "[") - count += strings.Count(s, "]") - if count == 0 { - return s - } - - // Apply replacements to buffer. - var b strings.Builder - b.Grow(len(s) + count*4) - start := 0 - for i := 0; i < count; i++ { - j := start - for index, r := range s[start:] { - if r == '&' || r == '[' || r == ']' { - j += index - break - } - } - b.WriteString(s[start:j]) - switch s[j] { - case '&': - b.WriteString("&") - case '[': - b.WriteString("[") - case ']': - b.WriteString("]") - } - start = j + 1 - } - b.WriteString(s[start:]) - return b.String() -} - -// EscapeValue 将字符串value中部分字符转义 -// -// - , -> , -// - & -> & -// - [ -> [ -// - ] -> ] -func EscapeValue(value string) string { - ret := EscapeText(value) - return strings.ReplaceAll(ret, ",", ",") -} - -// UnescapeText 将字符串content中部分字符反转义 -// -// - & -> & -// - [ -> [ -// - ] -> ] -func UnescapeText(content string) string { - ret := content - ret = strings.ReplaceAll(ret, "[", "[") - ret = strings.ReplaceAll(ret, "]", "]") - ret = strings.ReplaceAll(ret, "&", "&") - return ret -} - -// UnescapeValue 将字符串content中部分字符反转义 -// -// - , -> , -// - & -> & -// - [ -> [ -// - ] -> ] -func UnescapeValue(content string) string { - ret := strings.ReplaceAll(content, ",", ",") - return UnescapeText(ret) -} diff --git a/coolq/cqcode_test.go b/coolq/cqcode_test.go index 30c163692..9e5dd89ab 100644 --- a/coolq/cqcode_test.go +++ b/coolq/cqcode_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/tidwall/gjson" - "github.com/Mrs4s/go-cqhttp/coolq/cqcode" + "github.com/Mrs4s/go-cqhttp/internal/msg" ) var bot = CQBot{} @@ -44,7 +44,7 @@ const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&98765 func BenchmarkCQCodeEscapeText(b *testing.B) { for i := 0; i < b.N; i++ { ret := bText - cqcode.EscapeText(ret) + msg.EscapeText(ret) } } @@ -64,6 +64,6 @@ func TestCQCodeEscapeText(t *testing.T) { ret = strings.ReplaceAll(ret, "&", "&") ret = strings.ReplaceAll(ret, "[", "[") ret = strings.ReplaceAll(ret, "]", "]") - assert.Equal(t, ret, cqcode.EscapeText(rs)) + assert.Equal(t, ret, msg.EscapeText(rs)) } } diff --git a/coolq/cqcode/element.go b/internal/msg/element.go similarity index 63% rename from coolq/cqcode/element.go rename to internal/msg/element.go index 7c1cc74d8..a787658c1 100644 --- a/coolq/cqcode/element.go +++ b/internal/msg/element.go @@ -1,4 +1,5 @@ -package cqcode +// Package msg 提供了go-cqhttp消息中间表示,CQ码处理等等 +package msg import ( "bytes" @@ -8,6 +9,85 @@ import ( "github.com/Mrs4s/MiraiGo/binary" ) +// @@@ CQ码转义处理 @@@ + +// EscapeText 将字符串raw中部分字符转义 +// +// - & -> & +// - [ -> [ +// - ] -> ] +func EscapeText(s string) string { + count := strings.Count(s, "&") + count += strings.Count(s, "[") + count += strings.Count(s, "]") + if count == 0 { + return s + } + + // Apply replacements to buffer. + var b strings.Builder + b.Grow(len(s) + count*4) + start := 0 + for i := 0; i < count; i++ { + j := start + for index, r := range s[start:] { + if r == '&' || r == '[' || r == ']' { + j += index + break + } + } + b.WriteString(s[start:j]) + switch s[j] { + case '&': + b.WriteString("&") + case '[': + b.WriteString("[") + case ']': + b.WriteString("]") + } + start = j + 1 + } + b.WriteString(s[start:]) + return b.String() +} + +// EscapeValue 将字符串value中部分字符转义 +// +// - , -> , +// - & -> & +// - [ -> [ +// - ] -> ] +func EscapeValue(value string) string { + ret := EscapeText(value) + return strings.ReplaceAll(ret, ",", ",") +} + +// UnescapeText 将字符串content中部分字符反转义 +// +// - & -> & +// - [ -> [ +// - ] -> ] +func UnescapeText(content string) string { + ret := content + ret = strings.ReplaceAll(ret, "[", "[") + ret = strings.ReplaceAll(ret, "]", "]") + ret = strings.ReplaceAll(ret, "&", "&") + return ret +} + +// UnescapeValue 将字符串content中部分字符反转义 +// +// - , -> , +// - & -> & +// - [ -> [ +// - ] -> ] +func UnescapeValue(content string) string { + ret := strings.ReplaceAll(content, ",", ",") + return UnescapeText(ret) +} + +// @@@ 消息中间表示 @@@ + // Element single message type Element struct { Type string diff --git a/coolq/cqcode/all_test.go b/internal/msg/element_test.go similarity index 96% rename from coolq/cqcode/all_test.go rename to internal/msg/element_test.go index ff285c57e..4f37c26e6 100644 --- a/coolq/cqcode/all_test.go +++ b/internal/msg/element_test.go @@ -1,4 +1,4 @@ -package cqcode +package msg import ( "bytes" diff --git a/internal/msg/local.go b/internal/msg/local.go new file mode 100644 index 000000000..9b8e6e01d --- /dev/null +++ b/internal/msg/local.go @@ -0,0 +1,44 @@ +package msg + +import ( + "io" + + "github.com/Mrs4s/MiraiGo/message" +) + +// Poke 拍一拍 +type Poke struct { + Target int64 +} + +// Type 获取元素类型ID +func (e *Poke) Type() message.ElementType { + // Make message.IMessageElement Happy + return message.At +} + +// LocalImage 本地图片 +type LocalImage struct { + Stream io.ReadSeeker + File string + URL string + + Flash bool + EffectID int32 +} + +// Type implements the message.IMessageElement. +func (e *LocalImage) Type() message.ElementType { + return message.Image +} + +// LocalVideo 本地视频 +type LocalVideo struct { + File string + Thumb io.ReadSeeker +} + +// Type impl message.IMessageElement +func (e *LocalVideo) Type() message.ElementType { + return message.Video +} diff --git a/internal/onebot/spec.go b/internal/onebot/spec.go index 49afe73cc..bb4431fcb 100644 --- a/internal/onebot/spec.go +++ b/internal/onebot/spec.go @@ -3,7 +3,7 @@ package onebot import "fmt" -//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -pkg onebot -path=./../../coolq/api.go,./../../coolq/api_v12.go -supported -o supported.go +//go:generate go run ./../../cmd/api-generator -pkg onebot -path=./../../coolq/api.go,./../../coolq/api_v12.go -supported -o supported.go // Spec OneBot Specification type Spec struct { @@ -23,6 +23,7 @@ var V12 = &Spec{ SupportedActions: supportedV12, } +// ConvertID 根据版本转换ID func (s *Spec) ConvertID(id any) any { if s.Version == 12 { return fmt.Sprint(id) diff --git a/modules/api/caller.go b/modules/api/caller.go index 4dfeacf76..674074c40 100644 --- a/modules/api/caller.go +++ b/modules/api/caller.go @@ -9,7 +9,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/onebot" ) -//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -pkg api -path=./../../coolq/api.go,./../../coolq/api_v12.go -o api.go +//go:generate go run ./../../cmd/api-generator -pkg api -path=./../../coolq/api.go,./../../coolq/api_v12.go -o api.go // Getter 参数获取 type Getter interface { From debc1ed1ae692e60fcf10b36d8c314fdbe122982 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 16 Feb 2023 14:49:14 +0800 Subject: [PATCH 528/612] coolq: unified string/array message conversion change to 2 step: (1): parse to []internal/msg.Element, use msg.ParseObject/msg.ParseString (2): transform to []IMessageElement, can share functions --- coolq/api.go | 6 +- coolq/cqcode.go | 626 ++++++------------ internal/msg/element.go | 26 +- internal/msg/element_test.go | 7 +- internal/msg/parse.go | 104 +++ .../msg/parse_test.go | 34 +- 6 files changed, 328 insertions(+), 475 deletions(-) create mode 100644 internal/msg/parse.go rename coolq/cqcode_test.go => internal/msg/parse_test.go (59%) diff --git a/coolq/api.go b/coolq/api.go index 345c84041..b7459b7b2 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1847,7 +1847,11 @@ func (bot *CQBot) CQCanSendRecord() global.MSG { // @route(ocr_image,".ocr_image") // @rename(image_id->image) func (bot *CQBot) CQOcrImage(imageID string) global.MSG { - img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, message.SourceGroup) + // TODO: fix this + var elem msg.Element + elem.Type = "image" + elem.Data = []msg.Pair{{K: "file", V: imageID}} + img, err := bot.makeImageOrVideoElem(elem, false, message.SourceGroup) if err != nil { log.Warnf("load image error: %v", err) return Failed(100, "LOAD_FILE_ERROR", err.Error()) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 9d65ebb3d..9a0f1f3c1 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -11,7 +11,6 @@ import ( "net/url" "os" "path" - "reflect" "runtime" "strconv" "strings" @@ -382,431 +381,163 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { // ConvertStringMessage 将消息字符串转为消息元素数组 func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType) (r []message.IMessageElement) { - var t, key string - d := map[string]string{} + elems := msg.ParseString(raw) + return bot.ConvertElements(elems, sourceType) +} - saveCQCode := func() { - if t == "reply" { // reply 特殊处理 - if len(r) > 0 { - if _, ok := r[0].(*message.ReplyElement); ok { - log.Warnf("警告: 一条信息只能包含一个 Reply 元素.") - return - } - } - mid, err := strconv.Atoi(d["id"]) - customText := d["text"] - switch { - case customText != "": - var elem *message.ReplyElement - var org db.StoredMessage - sender, senderErr := strconv.ParseInt(d["qq"], 10, 64) - if senderErr != nil && err != nil { - log.Warnf("警告: 自定义 Reply 元素中必须包含 Uin 或 id") - break - } - msgTime, timeErr := strconv.ParseInt(d["time"], 10, 64) - if timeErr != nil { - msgTime = time.Now().Unix() - } - messageSeq, seqErr := strconv.ParseInt(d["seq"], 10, 64) - if err == nil { - org, _ = db.GetMessageByGlobalID(int32(mid)) - } - if org != nil { - elem = &message.ReplyElement{ - ReplySeq: org.GetAttribute().MessageSeq, - Sender: org.GetAttribute().SenderUin, - Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertStringMessage(customText, sourceType), - } - if senderErr != nil { - elem.Sender = sender - } - if timeErr != nil { - elem.Time = int32(msgTime) - } - if seqErr != nil { - elem.ReplySeq = int32(messageSeq) - } - } else { - elem = &message.ReplyElement{ - ReplySeq: int32(messageSeq), - Sender: sender, - Time: int32(msgTime), - Elements: bot.ConvertStringMessage(customText, sourceType), - } - } - r = append([]message.IMessageElement{elem}, r...) - case err == nil: - org, err := db.GetMessageByGlobalID(int32(mid)) - if err == nil { - r = append([]message.IMessageElement{ - &message.ReplyElement{ - ReplySeq: org.GetAttribute().MessageSeq, - Sender: org.GetAttribute().SenderUin, - Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertContentMessage(org.GetContent(), sourceType), - }, - }, r...) - } - default: - log.Warnf("警告: Reply 元素中必须包含 text 或 id") - } - return - } - if t == "forward" { // 单独处理转发 - if id, ok := d["id"]; ok { - if fwdMsg := bot.Client.DownloadForwardMessage(id); fwdMsg == nil { - log.Warnf("警告: Forward 信息不存在或已过期") - } else { - r = []message.IMessageElement{fwdMsg} - } - } else { - log.Warnf("警告: Forward 元素中必须包含 id") - } - return +// ConvertObjectMessage 将消息JSON对象转为消息元素数组 +func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType message.SourceType) (r []message.IMessageElement) { + if m.Type == gjson.String { + return bot.ConvertStringMessage(m.Str, sourceType) + } + elems := msg.ParseObject(m) + return bot.ConvertElements(elems, sourceType) +} + +// ConvertContentMessage 将数据库用的 content 转换为消息元素数组 +func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message.SourceType) (r []message.IMessageElement) { + elems := make([]msg.Element, len(content)) + for i, v := range content { + elem := msg.Element{Type: v["type"].(string)} + for k, v := range v["data"].(global.MSG) { + pair := msg.Pair{K: k, V: fmt.Sprint(v)} + elem.Data = append(elem.Data, pair) } - elem, err := bot.ToElement(t, d, sourceType) + elems[i] = elem + } + return bot.ConvertElements(elems, sourceType) +} + +// ConvertElements 将解码后的消息数组转换为MiraiGo表示 +func (bot *CQBot) ConvertElements(elems []msg.Element, sourceType message.SourceType) (r []message.IMessageElement) { + var replyCount int + for _, elem := range elems { + me, err := bot.ConvertElement(elem, sourceType) if err != nil { - org := "[CQ:" + t - for k, v := range d { - org += "," + k + "=" + v - } - org += "]" + // TODO: don't use cqcode format if !base.IgnoreInvalidCQCode { - log.Warnf("转换CQ码 %v 时出现错误: %v 将原样发送.", org, err) - r = append(r, message.NewText(org)) - } else { - log.Warnf("转换CQ码 %v 时出现错误: %v 将忽略.", org, err) + r = append(r, message.NewText(elem.CQCode())) } - return + log.Warnf("转换消息 %v 到MiraiGo Element时出现错误: %v.", elem.CQCode(), err) + continue } - switch i := elem.(type) { + switch i := me.(type) { + case *message.ReplyElement: + if replyCount > 0 { + log.Warnf("警告: 一条信息只能包含一个 Reply 元素.") + break + } + replyCount++ + // 将回复消息放置于第一个 + r = append([]message.IMessageElement{i}, r...) case message.IMessageElement: r = append(r, i) case []message.IMessageElement: r = append(r, i...) } } - - for raw != "" { - i := 0 - for i < len(raw) && !(raw[i] == '[' && i+4 < len(raw) && raw[i:i+4] == "[CQ:") { - i++ - } - if i > 0 { - if base.SplitURL { - for _, txt := range param.SplitURL(msg.UnescapeText(raw[:i])) { - r = append(r, message.NewText(txt)) - } - } else { - r = append(r, message.NewText(msg.UnescapeText(raw[:i]))) - } - } - - if i+4 > len(raw) { - return - } - raw = raw[i+4:] // skip "[CQ:" - i = 0 - for i < len(raw) && raw[i] != ',' && raw[i] != ']' { - i++ - } - if i+1 > len(raw) { - return - } - t = raw[:i] - for k := range d { // clear the map, reuse it - delete(d, k) - } - raw = raw[i:] - i = 0 - for { - if raw[0] == ']' { - saveCQCode() - raw = raw[1:] - break - } - raw = raw[1:] - - for i < len(raw) && raw[i] != '=' { - i++ - } - if i+1 > len(raw) { - return - } - key = raw[:i] - raw = raw[i+1:] // skip "=" - i = 0 - for i < len(raw) && raw[i] != ',' && raw[i] != ']' { - i++ - } - - if i+1 > len(raw) { - return - } - d[key] = msg.UnescapeValue(raw[:i]) - raw = raw[i:] - i = 0 - } - } return } -// ConvertObjectMessage 将消息JSON对象转为消息元素数组 -func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType message.SourceType) (r []message.IMessageElement) { - d := make(map[string]string) - convertElem := func(e gjson.Result) { - t := e.Get("type").Str - if t == "reply" && sourceType&(message.SourceGroup|message.SourcePrivate) != 0 { - if len(r) > 0 { - if _, ok := r[0].(*message.ReplyElement); ok { - log.Warnf("警告: 一条信息只能包含一个 Reply 元素.") - return - } +func (bot *CQBot) reply(elem msg.Element, sourceType message.SourceType) (any, error) { + mid, err := strconv.Atoi(elem.Get("id")) + customText := elem.Get("text") + var re *message.ReplyElement + switch { + case customText != "": + var org db.StoredMessage + sender, senderErr := strconv.ParseInt(elem.Get("user_id"), 10, 64) + if senderErr != nil { + sender, senderErr = strconv.ParseInt(elem.Get("qq"), 10, 64) + } + if senderErr != nil && err != nil { + return nil, errors.New("警告: 自定义 reply 元素中必须包含 user_id 或 id") + } + msgTime, timeErr := strconv.ParseInt(elem.Get("time"), 10, 64) + if timeErr != nil { + msgTime = time.Now().Unix() + } + messageSeq, seqErr := strconv.ParseInt(elem.Get("seq"), 10, 64) + if err == nil { + org, _ = db.GetMessageByGlobalID(int32(mid)) + } + if org != nil { + re = &message.ReplyElement{ + ReplySeq: org.GetAttribute().MessageSeq, + Sender: org.GetAttribute().SenderUin, + Time: int32(org.GetAttribute().Timestamp), + Elements: bot.ConvertStringMessage(customText, sourceType), } - mid, err := strconv.Atoi(e.Get("data.id").String()) - customText := e.Get("data.text").String() - switch { - case customText != "": - var elem *message.ReplyElement - var org db.StoredMessage - sender, senderErr := strconv.ParseInt(e.Get("data.[user_id,qq]").String(), 10, 64) - if senderErr != nil && err != nil { - log.Warnf("警告: 自定义 Reply 元素中必须包含 user_id 或 id") - break - } - msgTime, timeErr := strconv.ParseInt(e.Get("data.time").String(), 10, 64) - if timeErr != nil { - msgTime = time.Now().Unix() - } - messageSeq, seqErr := strconv.ParseInt(e.Get("data.seq").String(), 10, 64) - if err == nil { - org, _ = db.GetMessageByGlobalID(int32(mid)) - } - if org != nil { - elem = &message.ReplyElement{ - ReplySeq: org.GetAttribute().MessageSeq, - Sender: org.GetAttribute().SenderUin, - Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertStringMessage(customText, sourceType), - } - if senderErr != nil { - elem.Sender = sender - } - if timeErr != nil { - elem.Time = int32(msgTime) - } - if seqErr != nil { - elem.ReplySeq = int32(messageSeq) - } - } else { - elem = &message.ReplyElement{ - ReplySeq: int32(messageSeq), - Sender: sender, - Time: int32(msgTime), - Elements: bot.ConvertStringMessage(customText, sourceType), - } - } - r = append([]message.IMessageElement{elem}, r...) - case err == nil: - org, err := db.GetMessageByGlobalID(int32(mid)) - if err == nil { - r = append([]message.IMessageElement{ - &message.ReplyElement{ - ReplySeq: org.GetAttribute().MessageSeq, - Sender: org.GetAttribute().SenderUin, - Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertContentMessage(org.GetContent(), sourceType), - }, - }, r...) - } - default: - log.Warnf("警告: Reply 元素中必须包含 text 或 id") + if senderErr != nil { + re.Sender = sender } - return - } - if t == "forward" { - id := e.Get("data.id").String() - if id == "" { - log.Warnf("警告: Forward 元素中必须包含 id") - } else { - if fwdMsg := bot.Client.DownloadForwardMessage(id); fwdMsg == nil { - log.Warnf("警告: Forward 信息不存在或已过期") - } else { - r = []message.IMessageElement{fwdMsg} - } + if timeErr != nil { + re.Time = int32(msgTime) + } + if seqErr != nil { + re.ReplySeq = int32(messageSeq) } - return + break } - for i := range d { - delete(d, i) + re = &message.ReplyElement{ + ReplySeq: int32(messageSeq), + Sender: sender, + Time: int32(msgTime), + Elements: bot.ConvertStringMessage(customText, sourceType), } - e.Get("data").ForEach(func(key, value gjson.Result) bool { - d[key.Str] = value.String() - return true - }) - elem, err := bot.ToElement(t, d, sourceType) + + case err == nil: + org, err := db.GetMessageByGlobalID(int32(mid)) if err != nil { - log.Warnf("转换CQ码 (%v) 到MiraiGo Element时出现错误: %v 将忽略本段CQ码.", e.Raw, err) - return + return nil, err } - switch i := elem.(type) { - case message.IMessageElement: - r = append(r, i) - case []message.IMessageElement: - r = append(r, i...) + re = &message.ReplyElement{ + ReplySeq: org.GetAttribute().MessageSeq, + Sender: org.GetAttribute().SenderUin, + Time: int32(org.GetAttribute().Timestamp), + Elements: bot.ConvertContentMessage(org.GetContent(), sourceType), } - } - if m.Type == gjson.String { - return bot.ConvertStringMessage(m.Str, sourceType) - } - if m.IsArray() { - m.ForEach(func(_, e gjson.Result) bool { - convertElem(e) - return true - }) - } - if m.IsObject() { - convertElem(m) - } - return -} -// ConvertContentMessage 将数据库用的 content 转换为消息元素数组 -func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message.SourceType) (r []message.IMessageElement) { - for _, c := range content { - data := c["data"].(global.MSG) - switch c["type"] { - case "text": - r = append(r, message.NewText(data["text"].(string))) - case "image": - u, ok := data["url"] - d := make(map[string]string, 2) - if ok { - d["url"] = u.(string) - } - d["file"] = data["file"].(string) - e, err := bot.makeImageOrVideoElem(d, false, sourceType) - if err != nil { - log.Warnf("make image elem error: %v", err) - continue - } - flash, id := false, int32(0) - if t, ok := data["type"]; ok { - if t.(string) == "flash" { - flash = true - } - if t.(string) == "show" { - id := 0 - switch idn := data["id"].(type) { - case int32: - id = int(idn) - case int: - id = idn - case int64: - id = int(idn) - default: - id = int(reflect.ValueOf(data["id"]).Convert(reflect.TypeOf(0)).Int()) - } - if id < 40000 || id >= 40006 { - id = 40000 - } - } - } - switch img := e.(type) { - case *msg.LocalImage: - img.Flash = flash - img.EffectID = id - case *message.GroupImageElement: - img.Flash = flash - img.EffectID = id - switch sub := data["subType"].(type) { - case int64: - img.ImageBizType = message.ImageBizType(sub) - case uint32: - img.ImageBizType = message.ImageBizType(sub) - } - case *message.FriendImageElement: - img.Flash = flash - } - r = append(r, e) - case "at": - switch data["subType"].(string) { - case "all": - r = append(r, message.NewAt(0)) - case "user": - r = append(r, message.NewAt(reflect.ValueOf(data["target"]).Int(), data["display"].(string))) - default: - continue - } - case "redbag": - r = append(r, &message.RedBagElement{ - MsgType: message.RedBagMessageType(data["type"].(int)), - Title: data["title"].(string), - }) - case "forward": - r = append(r, &message.ForwardElement{ - ResId: data["id"].(string), - }) - case "face": - id := int32(0) - switch idn := data["id"].(type) { - case int32: - id = idn - case int: - id = int32(idn) - case int64: - id = int32(idn) - default: - id = int32(reflect.ValueOf(data["id"]).Convert(reflect.TypeOf(0)).Int()) - } - r = append(r, message.NewFace(id)) - case "video": - e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, true, sourceType) - if err != nil { - log.Warnf("make image elem error: %v", err) - continue - } - r = append(r, e) - } + default: + return nil, errors.New("reply消息中必须包含 text 或 id") } - return + return re, nil } -// ToElement 将解码后的CQCode转换为Element. +// ConvertElement 将解码后的消息转换为MiraiGoElement. // // 返回 interface{} 存在三种类型 // // message.IMessageElement []message.IMessageElement nil -func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.SourceType) (m any, err error) { - switch t { +func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType) (m any, err error) { + switch elem.Type { case "text": + text := elem.Get("text") if base.SplitURL { var ret []message.IMessageElement - for _, text := range param.SplitURL(d["text"]) { + for _, text := range param.SplitURL(text) { ret = append(ret, message.NewText(text)) } return ret, nil } - return message.NewText(d["text"]), nil + return message.NewText(text), nil case "image": - img, err := bot.makeImageOrVideoElem(d, false, sourceType) + img, err := bot.makeImageOrVideoElem(elem, false, sourceType) if err != nil { return nil, err } - tp := d["type"] + tp := elem.Get("type") flash, id := false, int64(0) switch tp { case "flash": flash = true case "show": - id, _ = strconv.ParseInt(d["id"], 10, 64) + id, _ = strconv.ParseInt(elem.Get("id"), 10, 64) if id < 40000 || id >= 40006 { id = 40000 } default: - return img, err + return img, nil } switch img := img.(type) { case *msg.LocalImage: @@ -815,24 +546,37 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So case *message.GroupImageElement: img.Flash = flash img.EffectID = int32(id) - i, _ := strconv.ParseInt(d["subType"], 10, 64) + i, _ := strconv.ParseInt(elem.Get("subType"), 10, 64) img.ImageBizType = message.ImageBizType(i) case *message.FriendImageElement: img.Flash = flash } - return img, err + return img, nil + case "reply": + return bot.reply(elem, sourceType) + case "forward": + id := elem.Get("id") + if id != "" { + return nil, errors.New("forward 消息中必须包含 id") + } + fwdMsg := bot.Client.DownloadForwardMessage(id) + if fwdMsg == nil { + return nil, errors.New("forward 消息不存在或已过期") + } + return fwdMsg, nil + case "poke": - t, _ := strconv.ParseInt(d["qq"], 10, 64) + t, _ := strconv.ParseInt(elem.Get("qq"), 10, 64) return &msg.Poke{Target: t}, nil case "tts": - data, err := bot.Client.GetTts(d["text"]) + data, err := bot.Client.GetTts(elem.Get("text")) if err != nil { return nil, err } return &message.VoiceElement{Data: base.ResampleSilk(data)}, nil case "record", "audio": - f := d["file"] - data, err := global.FindFile(f, d["cache"], global.VoicePath) + f := elem.Get("file") + data, err := global.FindFile(f, elem.Get("cache"), global.VoicePath) if err != nil { return nil, err } @@ -848,19 +592,18 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So } return &message.VoiceElement{Data: data}, nil case "face": - id, err := strconv.Atoi(d["id"]) + id, err := strconv.Atoi(elem.Get("id")) if err != nil { return nil, err } - if d["type"] == "sticker" { + if elem.Get("type") == "sticker" { return &message.AnimatedSticker{ID: int32(id)}, nil } return message.NewFace(int32(id)), nil case "mention_all": - d["qq"] = "all" - fallthrough + return message.AtAll(), nil case "at", "mention": - qq := d["qq"] + qq := elem.Get("qq") if qq == "all" { return message.AtAll(), nil } @@ -868,16 +611,17 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So if err != nil { return nil, err } - name := strings.TrimSpace(d["name"]) + name := strings.TrimSpace(elem.Get("name")) if len(name) > 0 { name = "@" + name } return message.NewAt(t, name), nil case "share": - return message.NewUrlShare(d["url"], d["title"], d["content"], d["image"]), nil + return message.NewUrlShare(elem.Get("url"), elem.Get("title"), elem.Get("content"), elem.Get("image")), nil case "music": - if d["type"] == "qq" { - info, err := global.QQMusicSongInfo(d["id"]) + id := elem.Get("id") + if elem.Get("type") == "qq" { + info, err := global.QQMusicSongInfo(id) if err != nil { return nil, err } @@ -887,13 +631,13 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So name := info.Get("track_info.name").Str mid := info.Get("track_info.mid").Str albumMid := info.Get("track_info.album.mid").Str - pinfo, _ := download.Request{URL: "http://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576"}.JSON() + pinfo, _ := download.Request{URL: "https://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576"}.JSON() jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + mid + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare" purl := pinfo.Get("url_mid.data.midurlinfo.0.purl").Str - preview := "http://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg" + preview := "https://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg" content := info.Get("track_info.singer.0.name").Str - if d["content"] != "" { - content = d["content"] + if elem.Get("content") != "" { + content = elem.Get("content") } return &message.MusicShareElement{ MusicType: message.QQMusic, @@ -904,8 +648,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So MusicUrl: purl, }, nil } - if d["type"] == "163" { - info, err := global.NeteaseMusicSongInfo(d["id"]) + if elem.Get("type") == "163" { + info, err := global.NeteaseMusicSongInfo(id) if err != nil { return nil, err } @@ -913,8 +657,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return nil, errors.New("song not found") } name := info.Get("name").Str - jumpURL := "https://y.music.163.com/m/song/" + d["id"] - musicURL := "http://music.163.com/song/media/outer/url?id=" + d["id"] + jumpURL := "https://y.music.163.com/m/song/" + id + musicURL := "http://music.163.com/song/media/outer/url?id=" + id picURL := info.Get("album.picUrl").Str artistName := "" if info.Get("artists.0").Exists() { @@ -929,10 +673,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So MusicUrl: musicURL, }, nil } - if d["type"] == "custom" { - if d["subtype"] != "" { + if elem.Get("type") == "custom" { + if elem.Get("subtype") != "" { var subType int - switch d["subtype"] { + switch elem.Get("subtype") { default: subType = message.QQMusic case "163": @@ -946,59 +690,58 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So } return &message.MusicShareElement{ MusicType: subType, - Title: d["title"], - Summary: d["content"], - Url: d["url"], - PictureUrl: d["image"], - MusicUrl: d["audio"], + Title: elem.Get("title"), + Summary: elem.Get("content"), + Url: elem.Get("url"), + PictureUrl: elem.Get("image"), + MusicUrl: elem.Get("audio"), }, nil } xml := fmt.Sprintf(``, - utils.XmlEscape(d["title"]), d["url"], d["image"], d["audio"], utils.XmlEscape(d["title"]), utils.XmlEscape(d["content"])) + utils.XmlEscape(elem.Get("title")), elem.Get("url"), elem.Get("image"), elem.Get("audio"), utils.XmlEscape(elem.Get("title")), utils.XmlEscape(elem.Get("content"))) return &message.ServiceElement{ Id: 60, Content: xml, SubType: "music", }, nil } - return nil, errors.New("unsupported music type: " + d["type"]) + return nil, errors.New("unsupported music type: " + elem.Get("type")) case "dice": - value := d["value"] + value := elem.Get("value") i, _ := strconv.ParseInt(value, 10, 64) if i < 0 || i > 6 { return nil, errors.New("invalid dice value " + value) } return message.NewDice(int32(i)), nil case "rps": - value := d["value"] + value := elem.Get("value") i, _ := strconv.ParseInt(value, 10, 64) if i < 0 || i > 2 { return nil, errors.New("invalid finger-guessing value " + value) } return message.NewFingerGuessing(int32(i)), nil case "xml": - resID := d["resid"] - template := msg.EscapeValue(d["data"]) + resID := elem.Get("resid") + template := elem.Get("data") i, _ := strconv.ParseInt(resID, 10, 64) m := message.NewRichXml(template, i) return m, nil case "json": - resID := d["resid"] + resID := elem.Get("resid") + data := elem.Get("data") i, _ := strconv.ParseInt(resID, 10, 64) if i == 0 { // 默认情况下走小程序通道 - msg := message.NewLightApp(d["data"]) - return msg, nil + return message.NewLightApp(data), nil } // resid不为0的情况下走富文本通道,后续补全透传service Id,此处暂时不处理 TODO - msg := message.NewRichJson(d["data"]) - return msg, nil + return message.NewRichJson(data), nil case "cardimage": - source := d["source"] - icon := d["icon"] - brief := d["brief"] + source := elem.Get("source") + icon := elem.Get("icon") + brief := elem.Get("brief") parseIntWithDefault := func(name string, origin int64) int64 { - v, _ := strconv.ParseInt(d[name], 10, 64) + v, _ := strconv.ParseInt(elem.Get(name), 10, 64) if v <= 0 { return origin } @@ -1008,13 +751,13 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So maxWidth := parseIntWithDefault("maxwidth", 500) minHeight := parseIntWithDefault("minheight", 200) maxHeight := parseIntWithDefault("maxheight", 1000) - img, err := bot.makeImageOrVideoElem(d, false, sourceType) + img, err := bot.makeImageOrVideoElem(elem, false, sourceType) if err != nil { return nil, errors.New("send cardimage faild") } return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, sourceType == message.SourceGroup) case "video": - file, err := bot.makeImageOrVideoElem(d, true, sourceType) + file, err := bot.makeImageOrVideoElem(elem, true, sourceType) if err != nil { return nil, err } @@ -1026,8 +769,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return v, nil } var data []byte - if cover, ok := d["cover"]; ok { - data, _ = global.FindFile(cover, d["cache"], global.ImagePath) + if cover := elem.Get("cover"); cover != "" { + data, _ = global.FindFile(cover, elem.Get("cache"), global.ImagePath) } else { err = global.ExtractCover(v.File, v.File+".jpg") if err != nil { @@ -1045,7 +788,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So _, _ = video.Seek(0, io.SeekStart) hash, _ := utils.ComputeMd5AndLength(video) cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash)+".mp4") - if !(d["cache"] == "" || d["cache"] == "1") || !global.PathExists(cacheFile) { + if !(elem.Get("cache") == "" || elem.Get("cache") == "1") || !global.PathExists(cacheFile) { err = global.EncodeMP4(v.File, cacheFile) if err != nil { return nil, err @@ -1055,17 +798,14 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So } return v, nil default: - return nil, errors.New("unsupported cq code: " + t) + return nil, errors.New("unsupported message type: " + elem.Type) } } // makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用 -func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceType message.SourceType) (message.IMessageElement, error) { - f := d["file"] - u, ok := d["url"] - if !ok { - u = "" - } +func (bot *CQBot) makeImageOrVideoElem(elem msg.Element, video bool, sourceType message.SourceType) (message.IMessageElement, error) { + f := elem.Get("file") + u := elem.Get("url") if strings.HasPrefix(f, "http") { hash := md5.Sum([]byte(f)) cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash[:])+".cache") @@ -1073,9 +813,9 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if video { maxSize = maxVideoSize } - thread, _ := strconv.Atoi(d["c"]) + thread, _ := strconv.Atoi(elem.Get("c")) exist := global.PathExists(cacheFile) - if exist && (d["cache"] == "" || d["cache"] == "1") { + if exist && (elem.Get("cache") == "" || elem.Get("cache") == "1") { goto useCacheFile } if exist { @@ -1170,8 +910,9 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy } exist := global.PathExists(rawPath) if !exist { - if d["url"] != "" { - return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, sourceType) + if elem.Get("url") != "" { + elem.Data = []msg.Pair{{K: "file", V: elem.Get("url")}} + return bot.makeImageOrVideoElem(elem, false, sourceType) } return nil, errors.New("invalid image") } @@ -1196,7 +937,11 @@ func (bot *CQBot) readImageCache(b []byte, sourceType message.SourceType) (messa r.ReadString() imageURL := r.ReadString() if size == 0 && imageURL != "" { - return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType) + // TODO: fix this + var elem msg.Element + elem.Type = "image" + elem.Data = []msg.Pair{{K: "file", V: imageURL}} + return bot.makeImageOrVideoElem(elem, false, sourceType) } var rsp message.IMessageElement switch sourceType { // nolint:exhaustive @@ -1213,7 +958,10 @@ func (bot *CQBot) readImageCache(b []byte, sourceType message.SourceType) (messa rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size) } if err != nil && imageURL != "" { - return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType) + var elem msg.Element + elem.Type = "image" + elem.Data = []msg.Pair{{K: "file", V: imageURL}} + return bot.makeImageOrVideoElem(elem, false, sourceType) } return rsp, err } diff --git a/internal/msg/element.go b/internal/msg/element.go index a787658c1..b54710308 100644 --- a/internal/msg/element.go +++ b/internal/msg/element.go @@ -88,16 +88,26 @@ func UnescapeValue(content string) string { // @@@ 消息中间表示 @@@ +// Pair key value pair +type Pair struct { + K string + V string +} + // Element single message type Element struct { Type string Data []Pair } -// Pair key value pair -type Pair struct { - K string - V string +// Get 获取指定值 +func (e *Element) Get(k string) string { + for _, datum := range e.Data { + if datum.K == k { + return datum.V + } + } + return "" } // CQCode convert element to cqcode @@ -140,7 +150,7 @@ func (e *Element) MarshalJSON() ([]byte, error) { buf.WriteByte('"') buf.WriteString(data.K) buf.WriteString(`":`) - writeQuote(buf, data.V) + buf.WriteString(QuoteJSON(data.V)) } buf.WriteString(`}}`) }), nil @@ -148,9 +158,10 @@ func (e *Element) MarshalJSON() ([]byte, error) { const hex = "0123456789abcdef" -func writeQuote(b *bytes.Buffer, s string) { +// QuoteJSON 按JSON转义为字符加上双引号 +func QuoteJSON(s string) string { i, j := 0, 0 - + var b strings.Builder b.WriteByte('"') for j < len(s) { c := s[j] @@ -231,4 +242,5 @@ func writeQuote(b *bytes.Buffer, s string) { b.WriteString(s[i:]) b.WriteByte('"') + return b.String() } diff --git a/internal/msg/element_test.go b/internal/msg/element_test.go index 4f37c26e6..775b6aa3b 100644 --- a/internal/msg/element_test.go +++ b/internal/msg/element_test.go @@ -1,7 +1,6 @@ package msg import ( - "bytes" "encoding/json" "testing" ) @@ -14,16 +13,14 @@ func jsonMarshal(s string) string { return string(b) } -func Test_quote(t *testing.T) { +func TestQuoteJSON(t *testing.T) { testcase := []string{ "\u0005", // issue 1773 "\v", } for _, input := range testcase { - var b bytes.Buffer - writeQuote(&b, input) - got := b.String() + got := QuoteJSON(input) expected := jsonMarshal(input) if got != expected { t.Errorf("want %v but got %v", expected, got) diff --git a/internal/msg/parse.go b/internal/msg/parse.go new file mode 100644 index 000000000..9f981b987 --- /dev/null +++ b/internal/msg/parse.go @@ -0,0 +1,104 @@ +package msg + +import ( + "github.com/tidwall/gjson" +) + +// ParseObject 将消息JSON对象转为消息元素数组 +func ParseObject(m gjson.Result) (r []Element) { + convert := func(e gjson.Result) { + var elem Element + elem.Type = e.Get("type").Str + e.Get("data").ForEach(func(key, value gjson.Result) bool { + pair := Pair{K: key.Str, V: value.String()} + elem.Data = append(elem.Data, pair) + return true + }) + r = append(r, elem) + } + + if m.IsArray() { + m.ForEach(func(_, e gjson.Result) bool { + convert(e) + return true + }) + } + if m.IsObject() { + convert(m) + } + return +} + +func text(txt string) Element { + return Element{ + Type: "text", + Data: []Pair{ + { + K: "text", + V: txt, + }, + }, + } +} + +// ParseString 将字符串(CQ码)转为消息元素数组 +func ParseString(raw string) (r []Element) { + var elem Element + for raw != "" { + i := 0 + for i < len(raw) && !(raw[i] == '[' && i+4 < len(raw) && raw[i:i+4] == "[CQ:") { + i++ + } + if i > 0 { + r = append(r, text(UnescapeText(raw[:i]))) + } + + if i+4 > len(raw) { + return + } + raw = raw[i+4:] // skip "[CQ:" + i = 0 + for i < len(raw) && raw[i] != ',' && raw[i] != ']' { + i++ + } + if i+1 > len(raw) { + return + } + elem.Type = raw[:i] + elem.Data = nil // reset data + raw = raw[i:] + i = 0 + for { + if raw[0] == ']' { + r = append(r, elem) + raw = raw[1:] + break + } + raw = raw[1:] + + for i < len(raw) && raw[i] != '=' { + i++ + } + if i+1 > len(raw) { + return + } + key := raw[:i] + raw = raw[i+1:] // skip "=" + i = 0 + for i < len(raw) && raw[i] != ',' && raw[i] != ']' { + i++ + } + + if i+1 > len(raw) { + return + } + elem.Data = append(elem.Data, Pair{ + K: key, + V: UnescapeValue(raw[:i]), + }) + raw = raw[i:] + i = 0 + } + } + return +} diff --git a/coolq/cqcode_test.go b/internal/msg/parse_test.go similarity index 59% rename from coolq/cqcode_test.go rename to internal/msg/parse_test.go index 9e5dd89ab..21998c7d1 100644 --- a/coolq/cqcode_test.go +++ b/internal/msg/parse_test.go @@ -1,22 +1,18 @@ -package coolq +package msg import ( "fmt" "strings" "testing" - "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/utils" "github.com/stretchr/testify/assert" "github.com/tidwall/gjson" - - "github.com/Mrs4s/go-cqhttp/internal/msg" ) -var bot = CQBot{} - -func TestCQBot_ConvertStringMessage(t *testing.T) { - for _, v := range bot.ConvertStringMessage(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`, message.SourcePrivate) { +func TestParseString(t *testing.T) { + // TODO: add more text + for _, v := range ParseString(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`) { fmt.Println(v) } } @@ -26,17 +22,18 @@ var ( benchArray = gjson.Parse(`[{"type":"text","data":{"text":"asdfqwerqwerqwer"}},{"type":"face","data":{"id":"115","text":"111"}},{"type":"text","data":{"text":"asdfasdfasdfasdfasdfasdfasd"}},{"type":"face","data":{"id":"217"}},{"type":"text","data":{"text":"] "}},{"type":"text","data":{"text":"123"}},{"type":"text","data":{"text":" ["}}]`) ) -func BenchmarkCQBot_ConvertStringMessage(b *testing.B) { +func BenchmarkParseString(b *testing.B) { for i := 0; i < b.N; i++ { - bot.ConvertStringMessage(bench, message.SourcePrivate) + ParseString(bench) } b.SetBytes(int64(len(bench))) } -func BenchmarkCQBot_ConvertObjectMessage(b *testing.B) { +func BenchmarkParseObject(b *testing.B) { for i := 0; i < b.N; i++ { - bot.ConvertObjectMessage(benchArray, message.SourcePrivate) + ParseObject(benchArray) } + b.SetBytes(int64(len(benchArray.Raw))) } const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&987654321[]&` @@ -44,16 +41,7 @@ const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&98765 func BenchmarkCQCodeEscapeText(b *testing.B) { for i := 0; i < b.N; i++ { ret := bText - msg.EscapeText(ret) - } -} - -func BenchmarkCQCodeEscapeTextBefore(b *testing.B) { - for i := 0; i < b.N; i++ { - ret := bText - ret = strings.ReplaceAll(ret, "&", "&") - ret = strings.ReplaceAll(ret, "[", "[") - strings.ReplaceAll(ret, "]", "]") + EscapeText(ret) } } @@ -64,6 +52,6 @@ func TestCQCodeEscapeText(t *testing.T) { ret = strings.ReplaceAll(ret, "&", "&") ret = strings.ReplaceAll(ret, "[", "[") ret = strings.ReplaceAll(ret, "]", "]") - assert.Equal(t, ret, msg.EscapeText(rs)) + assert.Equal(t, ret, EscapeText(rs)) } } From c80adf579547642044d409ceb0ced0f70028930d Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 16 Feb 2023 23:28:31 +0800 Subject: [PATCH 529/612] coolq: handle v11/v12 specific message (1) V11: at V12: mention/mention_all (2) V11: record V12: voice --- coolq/api.go | 14 ++-- coolq/api_v12.go | 1 + coolq/cqcode.go | 187 ++++++++++++++++++++++++++++------------------- 3 files changed, 120 insertions(+), 82 deletions(-) diff --git a/coolq/api.go b/coolq/api.go index b7459b7b2..50a90a8ff 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -754,7 +754,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b var elem []message.IMessageElement if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, message.SourceGroup) + elem = bot.ConvertObjectMessage(onebot.V11, m, message.SourceGroup) } else { str := m.String() if str == "" { @@ -764,7 +764,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, message.SourceGroup) + elem = bot.ConvertStringMessage(onebot.V11, str, message.SourceGroup) } } fixAt(elem) @@ -808,7 +808,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R var elem []message.IMessageElement if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, message.SourceGuildChannel) + elem = bot.ConvertObjectMessage(onebot.V11, m, message.SourceGuildChannel) } else { str := m.String() if str == "" { @@ -818,7 +818,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, message.SourceGuildChannel) + elem = bot.ConvertStringMessage(onebot.V11, str, message.SourceGuildChannel) } } fixAt(elem) @@ -928,7 +928,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType } } } - content := bot.ConvertObjectMessage(c, sourceType) + content := bot.ConvertObjectMessage(onebot.V11, c, sourceType) if uin != 0 && name != "" && len(content) > 0 { return &message.ForwardNode{ SenderId: uin, @@ -1021,7 +1021,7 @@ func (bot *CQBot) CQSendPrivateForwardMessage(userID int64, m gjson.Result) glob func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) global.MSG { var elem []message.IMessageElement if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, message.SourcePrivate) + elem = bot.ConvertObjectMessage(onebot.V11, m, message.SourcePrivate) } else { str := m.String() if str == "" { @@ -1030,7 +1030,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Resu if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, message.SourcePrivate) + elem = bot.ConvertStringMessage(onebot.V11, str, message.SourcePrivate) } } mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem}) diff --git a/coolq/api_v12.go b/coolq/api_v12.go index 95a432da5..21c8835e9 100644 --- a/coolq/api_v12.go +++ b/coolq/api_v12.go @@ -29,5 +29,6 @@ func (bot *CQBot) CQGetVersion() global.MSG { // @route12(send_message) // @rename(m->message) func (bot *CQBot) CQSendMessageV12(groupID, userID, detailType string, m gjson.Result) global.MSG { + // TODO: implement return OK(nil) } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 9a0f1f3c1..7d500d0e7 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -31,6 +31,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/download" "github.com/Mrs4s/go-cqhttp/internal/mime" "github.com/Mrs4s/go-cqhttp/internal/msg" + "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/internal/param" ) @@ -62,6 +63,7 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { // // nolint:govet func toElements(e []message.IMessageElement, source message.Source) (r []msg.Element) { + // TODO: support OneBot V12 type pair = msg.Pair // simplify code type pairs = []pair @@ -380,18 +382,18 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { } // ConvertStringMessage 将消息字符串转为消息元素数组 -func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType) (r []message.IMessageElement) { +func (bot *CQBot) ConvertStringMessage(spec *onebot.Spec, raw string, sourceType message.SourceType) (r []message.IMessageElement) { elems := msg.ParseString(raw) - return bot.ConvertElements(elems, sourceType) + return bot.ConvertElements(spec, elems, sourceType) } // ConvertObjectMessage 将消息JSON对象转为消息元素数组 -func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType message.SourceType) (r []message.IMessageElement) { - if m.Type == gjson.String { - return bot.ConvertStringMessage(m.Str, sourceType) +func (bot *CQBot) ConvertObjectMessage(spec *onebot.Spec, m gjson.Result, sourceType message.SourceType) (r []message.IMessageElement) { + if spec.Version == 11 && m.Type == gjson.String { + return bot.ConvertStringMessage(spec, m.Str, sourceType) } elems := msg.ParseObject(m) - return bot.ConvertElements(elems, sourceType) + return bot.ConvertElements(spec, elems, sourceType) } // ConvertContentMessage 将数据库用的 content 转换为消息元素数组 @@ -405,14 +407,14 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message } elems[i] = elem } - return bot.ConvertElements(elems, sourceType) + return bot.ConvertElements(onebot.V11, elems, sourceType) } // ConvertElements 将解码后的消息数组转换为MiraiGo表示 -func (bot *CQBot) ConvertElements(elems []msg.Element, sourceType message.SourceType) (r []message.IMessageElement) { +func (bot *CQBot) ConvertElements(spec *onebot.Spec, elems []msg.Element, sourceType message.SourceType) (r []message.IMessageElement) { var replyCount int for _, elem := range elems { - me, err := bot.ConvertElement(elem, sourceType) + me, err := bot.ConvertElement(spec, elem, sourceType) if err != nil { // TODO: don't use cqcode format if !base.IgnoreInvalidCQCode { @@ -439,7 +441,7 @@ func (bot *CQBot) ConvertElements(elems []msg.Element, sourceType message.Source return } -func (bot *CQBot) reply(elem msg.Element, sourceType message.SourceType) (any, error) { +func (bot *CQBot) reply(spec *onebot.Spec, elem msg.Element, sourceType message.SourceType) (any, error) { mid, err := strconv.Atoi(elem.Get("id")) customText := elem.Get("text") var re *message.ReplyElement @@ -466,7 +468,7 @@ func (bot *CQBot) reply(elem msg.Element, sourceType message.SourceType) (any, e ReplySeq: org.GetAttribute().MessageSeq, Sender: org.GetAttribute().SenderUin, Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertStringMessage(customText, sourceType), + Elements: bot.ConvertStringMessage(spec, customText, sourceType), } if senderErr != nil { re.Sender = sender @@ -483,7 +485,7 @@ func (bot *CQBot) reply(elem msg.Element, sourceType message.SourceType) (any, e ReplySeq: int32(messageSeq), Sender: sender, Time: int32(msgTime), - Elements: bot.ConvertStringMessage(customText, sourceType), + Elements: bot.ConvertStringMessage(spec, customText, sourceType), } case err == nil: @@ -504,12 +506,89 @@ func (bot *CQBot) reply(elem msg.Element, sourceType message.SourceType) (any, e return re, nil } +func (bot *CQBot) voice(elem msg.Element) (m any, err error) { + f := elem.Get("file") + data, err := global.FindFile(f, elem.Get("cache"), global.VoicePath) + if err != nil { + return nil, err + } + if !global.IsAMRorSILK(data) { + mt, ok := mime.CheckAudio(bytes.NewReader(data)) + if !ok { + return nil, errors.New("voice type error: " + mt) + } + data, err = global.EncoderSilk(data) + if err != nil { + return nil, err + } + } + return &message.VoiceElement{Data: data}, nil +} + +func (bot *CQBot) at(id, name string) (m any, err error) { + t, err := strconv.ParseInt(id, 10, 64) + if err != nil { + return nil, err + } + name = strings.TrimSpace(name) + if len(name) > 0 { + name = "@" + name + } + return message.NewAt(t, name), nil +} + +// convertV11 ConvertElement11 +func (bot *CQBot) convertV11(elem msg.Element) (m any, err error, failed bool) { + switch elem.Type { + case "at": + qq := elem.Get("qq") + if qq == "all" { + m = message.AtAll() + break + } + m, err = bot.at(qq, elem.Get("name")) + case "record": + m, err = bot.voice(elem) + default: + failed = true + } + return +} + +// convertV11 ConvertElement11 +func (bot *CQBot) convertV12(elem msg.Element) (m any, err error, failed bool) { + switch elem.Type { + case "mention": + m, err = bot.at(elem.Get("user_id"), elem.Get("name")) + case "mention_all": + m = message.AtAll() + case "voice": + m, err = bot.voice(elem) + default: + failed = true + } + return +} + // ConvertElement 将解码后的消息转换为MiraiGoElement. // // 返回 interface{} 存在三种类型 // // message.IMessageElement []message.IMessageElement nil -func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType) (m any, err error) { +func (bot *CQBot) ConvertElement(spec *onebot.Spec, elem msg.Element, sourceType message.SourceType) (m any, err error) { + var failed bool + switch spec.Version { + case 11: + m, err, failed = bot.convertV11(elem) + case 12: + m, err, failed = bot.convertV12(elem) + default: + panic("invalid onebot version:" + strconv.Itoa(spec.Version)) + } + if !failed { + return m, err + } + switch elem.Type { case "text": text := elem.Get("text") @@ -553,7 +632,7 @@ func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType } return img, nil case "reply": - return bot.reply(elem, sourceType) + return bot.reply(spec, elem, sourceType) case "forward": id := elem.Get("id") if id != "" { @@ -574,23 +653,6 @@ func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType return nil, err } return &message.VoiceElement{Data: base.ResampleSilk(data)}, nil - case "record", "audio": - f := elem.Get("file") - data, err := global.FindFile(f, elem.Get("cache"), global.VoicePath) - if err != nil { - return nil, err - } - if !global.IsAMRorSILK(data) { - mt, ok := mime.CheckAudio(bytes.NewReader(data)) - if !ok { - return nil, errors.New("audio type error: " + mt) - } - data, err = global.EncoderSilk(data) - if err != nil { - return nil, err - } - } - return &message.VoiceElement{Data: data}, nil case "face": id, err := strconv.Atoi(elem.Get("id")) if err != nil { @@ -600,27 +662,12 @@ func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType return &message.AnimatedSticker{ID: int32(id)}, nil } return message.NewFace(int32(id)), nil - case "mention_all": - return message.AtAll(), nil - case "at", "mention": - qq := elem.Get("qq") - if qq == "all" { - return message.AtAll(), nil - } - t, err := strconv.ParseInt(qq, 10, 64) - if err != nil { - return nil, err - } - name := strings.TrimSpace(elem.Get("name")) - if len(name) > 0 { - name = "@" + name - } - return message.NewAt(t, name), nil case "share": return message.NewUrlShare(elem.Get("url"), elem.Get("title"), elem.Get("content"), elem.Get("image")), nil case "music": id := elem.Get("id") - if elem.Get("type") == "qq" { + switch elem.Get("type") { + case "qq": info, err := global.QQMusicSongInfo(id) if err != nil { return nil, err @@ -628,27 +675,22 @@ func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType if !info.Get("track_info").Exists() { return nil, errors.New("song not found") } - name := info.Get("track_info.name").Str - mid := info.Get("track_info.mid").Str - albumMid := info.Get("track_info.album.mid").Str - pinfo, _ := download.Request{URL: "https://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576"}.JSON() - jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + mid + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare" - purl := pinfo.Get("url_mid.data.midurlinfo.0.purl").Str - preview := "https://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg" - content := info.Get("track_info.singer.0.name").Str + albumMid := info.Get("track_info.album.mid").String() + pinfo, _ := download.Request{URL: "https://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + info.Get("track_info.mid").Str + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576"}.JSON() + jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + info.Get("track_info.mid").Str + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare" + content := info.Get("track_info.singer.0.name").String() if elem.Get("content") != "" { content = elem.Get("content") } return &message.MusicShareElement{ MusicType: message.QQMusic, - Title: name, + Title: info.Get("track_info.name").Str, Summary: content, Url: jumpURL, - PictureUrl: preview, - MusicUrl: purl, + PictureUrl: "https://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg", + MusicUrl: pinfo.Get("url_mid.data.midurlinfo.0.purl").String(), }, nil - } - if elem.Get("type") == "163" { + case "163": info, err := global.NeteaseMusicSongInfo(id) if err != nil { return nil, err @@ -656,24 +698,19 @@ func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType if !info.Exists() { return nil, errors.New("song not found") } - name := info.Get("name").Str - jumpURL := "https://y.music.163.com/m/song/" + id - musicURL := "http://music.163.com/song/media/outer/url?id=" + id - picURL := info.Get("album.picUrl").Str artistName := "" if info.Get("artists.0").Exists() { - artistName = info.Get("artists.0.name").Str + artistName = info.Get("artists.0.name").String() } return &message.MusicShareElement{ MusicType: message.CloudMusic, - Title: name, + Title: info.Get("name").String(), Summary: artistName, - Url: jumpURL, - PictureUrl: picURL, - MusicUrl: musicURL, + Url: "https://y.music.163.com/m/song/" + id, + PictureUrl: info.Get("album.picUrl").String(), + MusicUrl: "https://music.163.com/song/media/outer/url?id=" + id, }, nil - } - if elem.Get("type") == "custom" { + case "custom": if elem.Get("subtype") != "" { var subType int switch elem.Get("subtype") { @@ -694,11 +731,11 @@ func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType Summary: elem.Get("content"), Url: elem.Get("url"), PictureUrl: elem.Get("image"), - MusicUrl: elem.Get("audio"), + MusicUrl: elem.Get("voice"), }, nil } - xml := fmt.Sprintf(``, - utils.XmlEscape(elem.Get("title")), elem.Get("url"), elem.Get("image"), elem.Get("audio"), utils.XmlEscape(elem.Get("title")), utils.XmlEscape(elem.Get("content"))) + xml := fmt.Sprintf(`%s%s`, + utils.XmlEscape(elem.Get("title")), elem.Get("url"), elem.Get("image"), elem.Get("voice"), utils.XmlEscape(elem.Get("title")), utils.XmlEscape(elem.Get("content"))) return &message.ServiceElement{ Id: 60, Content: xml, From 90fa530a02d517a46ac04b0a7c32a6c8b58841c9 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 16 Feb 2023 23:31:04 +0800 Subject: [PATCH 530/612] ci: make revive happy --- coolq/cqcode.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 7d500d0e7..4dfdf482c 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -538,7 +538,7 @@ func (bot *CQBot) at(id, name string) (m any, err error) { } // convertV11 ConvertElement11 -func (bot *CQBot) convertV11(elem msg.Element) (m any, err error, failed bool) { +func (bot *CQBot) convertV11(elem msg.Element) (m any, err error, failed bool) { // nolint switch elem.Type { case "at": qq := elem.Get("qq") @@ -555,8 +555,8 @@ func (bot *CQBot) convertV11(elem msg.Element) (m any, err error, failed bool) { return } -// convertV11 ConvertElement11 -func (bot *CQBot) convertV12(elem msg.Element) (m any, err error, failed bool) { +// convertV12 ConvertElement12 +func (bot *CQBot) convertV12(elem msg.Element) (m any, err error, failed bool) { // nolint switch elem.Type { case "mention": m, err = bot.at(elem.Get("user_id"), elem.Get("name")) From 0e5f3ed555eb652624859e8314f24f8f19cec1c3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 19 Feb 2023 14:13:18 +0800 Subject: [PATCH 531/612] server: support ob12 http long polling --- modules/api/caller.go | 4 ++-- server/middlewares.go | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/api/caller.go b/modules/api/caller.go index 674074c40..344920bb7 100644 --- a/modules/api/caller.go +++ b/modules/api/caller.go @@ -17,7 +17,7 @@ type Getter interface { } // Handler 中间件 -type Handler func(action string, p Getter) global.MSG +type Handler func(action string, spe *onebot.Spec, p Getter) global.MSG // Caller api route caller type Caller struct { @@ -28,7 +28,7 @@ type Caller struct { // Call specific API func (c *Caller) Call(action string, spec *onebot.Spec, p Getter) global.MSG { for _, fn := range c.handlers { - if ret := fn(action, p); ret != nil { + if ret := fn(action, spec, p); ret != nil { return ret } } diff --git a/server/middlewares.go b/server/middlewares.go index f783e0f4b..c99634334 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -8,6 +8,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/modules/api" "golang.org/x/time/rate" @@ -26,7 +27,7 @@ type MiddleWares struct { func rateLimit(frequency float64, bucketSize int) api.Handler { limiter := rate.NewLimiter(rate.Limit(frequency), bucketSize) - return func(_ string, _ api.Getter) global.MSG { + return func(_ string, _ *onebot.Spec, _ api.Getter) global.MSG { _ = limiter.Wait(context.Background()) return nil } @@ -45,8 +46,11 @@ func longPolling(bot *coolq.CQBot, maxSize int) api.Handler { } cond.Signal() }) - return func(action string, p api.Getter) global.MSG { - if action != "get_updates" { + return func(action string, spec *onebot.Spec, p api.Getter) global.MSG { + switch { + case spec.Version == 11 && action == "get_updates": // ok + case spec.Version == 12 && action == "get_latest_events": // ok + default: return nil } var ( From 811cfdca98106fc43d3f7e973ab4c6ec0225b455 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 19 Feb 2023 15:00:19 +0800 Subject: [PATCH 532/612] cmd/gocq: support select ticket input method --- cmd/gocq/login.go | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 78638410f..079e9fc11 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -225,25 +225,29 @@ func loginResponseProcessor(res *client.LoginResponse) error { } } -func getTicket(u string) (str string) { +func getTicket(u string) string { + log.Warnf("请选择提交滑块ticket方式:") + log.Warnf("1. 自动提交") + log.Warnf("2. 手动抓取提交") + log.Warn("请输入(1 - 2):") + text := readLine() id := utils.RandomString(8) - log.Warnf("请前往该地址验证 -> %v <- 或输入手动抓取的 ticket:(Enter 提交)", strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id))) - manual := make(chan string, 1) - go func() { - manual <- readLine() - }() - ticker := time.NewTicker(time.Second) - defer ticker.Stop() + auto := !strings.Contains(text, "2") + if auto { + u = strings.ReplaceAll(u, "https://ssl.captcha.qq.com/template/wireless_mqq_captcha.html?", fmt.Sprintf("https://captcha.go-cqhttp.org/captcha?id=%v&", id)) + } + log.Warnf("请前往该地址验证 -> %v ", u) + if !auto { + log.Warn("请输入ticket: (Enter 提交)") + return readLine() + } + for count := 120; count > 0; count-- { - select { - case <-ticker.C: - str = fetchCaptcha(id) - if str != "" { - return - } - case str = <-manual: - return + str := fetchCaptcha(id) + if str != "" { + return str } + time.Sleep(time.Second) } log.Warnf("验证超时") return "" From edfcd41ed681e64961e96da71f7eec2fa982dfa1 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 19 Feb 2023 20:10:46 +0800 Subject: [PATCH 533/612] dep: update MiraiGo --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9b2c7525d..febd212dd 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230214152309-cfd7d92d1f67 + github.com/Mrs4s/MiraiGo v0.0.0-20230219115954-3b97ce341ba8 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 30dce95d1..113bc9c2e 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230214152309-cfd7d92d1f67 h1:KUNf7psf+/2ms6ci9W2ouH+eeAzVpjZv/IW5KIDR8Mg= -github.com/Mrs4s/MiraiGo v0.0.0-20230214152309-cfd7d92d1f67/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230219115954-3b97ce341ba8 h1:TA9aA5uF1oSttyZwAio6GcanEwO6XDGJkznOn+7vs7s= +github.com/Mrs4s/MiraiGo v0.0.0-20230219115954-3b97ce341ba8/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 0c9f7a1f8ff87832a4823c2d5d125e19b07b9bc9 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 20 Feb 2023 14:06:08 +0800 Subject: [PATCH 534/612] fix device --- cmd/gocq/login.go | 2 ++ cmd/gocq/main.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 079e9fc11..23989df5c 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -52,6 +52,7 @@ func readIfTTY(de string) (str string) { } var cli *client.QQClient +var device *client.DeviceInfo // ErrSMSRequestError SMS请求出错 var ErrSMSRequestError = errors.New("sms request error") @@ -168,6 +169,7 @@ func loginResponseProcessor(res *client.LoginResponse) error { cli.Disconnect() cli.Release() cli = client.NewClientEmpty() + cli.UseDevice(device) return qrcodeLogin() case client.NeedCaptcha: log.Warnf("登录需要验证码.") diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 9d194f2d7..fded83763 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -136,7 +136,6 @@ func Main() { log.SetLevel(log.DebugLevel) log.Warnf("已开启Debug模式.") } - var device *client.DeviceInfo if !global.PathExists("device.json") { log.Warn("虚拟设备信息不存在, 将自动生成随机设备.") device = client.GenRandomDevice() @@ -242,6 +241,7 @@ func Main() { cli.Disconnect() cli.Release() cli = newClient() + cli.UseDevice(device) } else { isTokenLogin = true } From cb1604a09817d5a24faf4a0db59567641f925bf3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 20 Feb 2023 15:08:53 +0800 Subject: [PATCH 535/612] update MiraiGo --- coolq/bot.go | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index 268c28b89..531c0f684 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -173,7 +173,7 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *msg.LocalImage) ( } img.Stream = bytes.NewReader(stream.Bytes()) } - i, err := bot.Client.UploadImage(target, img.Stream, 4) + i, err := bot.Client.UploadImage(target, img.Stream) if err != nil { return nil, err } @@ -194,7 +194,7 @@ func (bot *CQBot) uploadLocalVideo(target message.Source, v *msg.LocalVideo) (*m return nil, err } defer func() { _ = video.Close() }() - return bot.Client.UploadShortVideo(target, video, v.Thumb, 4) + return bot.Client.UploadShortVideo(target, video, v.Thumb) } func removeLocalElement(elements []message.IMessageElement) []message.IMessageElement { diff --git a/go.mod b/go.mod index febd212dd..49e18bd89 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230219115954-3b97ce341ba8 + github.com/Mrs4s/MiraiGo v0.0.0-20230220070519-af032dec9677 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 113bc9c2e..bc62e9311 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230219115954-3b97ce341ba8 h1:TA9aA5uF1oSttyZwAio6GcanEwO6XDGJkznOn+7vs7s= -github.com/Mrs4s/MiraiGo v0.0.0-20230219115954-3b97ce341ba8/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230220070519-af032dec9677 h1:ekVLExixkpJybGXsRyQorPZJN7k2b5ZqH+pOgPVfAJM= +github.com/Mrs4s/MiraiGo v0.0.0-20230220070519-af032dec9677/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 1dba273b61014f664c44b0c46165cf98eaf7bddc Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 20 Feb 2023 15:13:15 +0800 Subject: [PATCH 536/612] remove replace in go.mod --- go.mod | 4 +--- go.sum | 5 +++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 49e18bd89..aa779fe34 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect @@ -66,5 +66,3 @@ require ( modernc.org/strutil v1.1.3 // indirect modernc.org/token v1.0.1 // indirect ) - -replace github.com/remyoudompheng/bigfft => github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b diff --git a/go.sum b/go.sum index bc62e9311..7e947cb8d 100644 --- a/go.sum +++ b/go.sum @@ -19,8 +19,6 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b h1:Zt3pFQditAdWTHCOVkiloc9ZauBoWrb37guFV4iIRvE= -github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/fumiama/go-base16384 v1.6.1 h1:4yb4JgmBJDnQtq3XGXXdLrVwEnRpjhMUt4eAcsNeA30= github.com/fumiama/go-base16384 v1.6.1/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= @@ -86,6 +84,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= From 4ceacc38d540e9695a81b426780629b64e3ddb40 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 27 Feb 2023 15:17:27 +0800 Subject: [PATCH 537/612] onebot: move to pkg/onebot --- coolq/api.go | 2 +- coolq/bot.go | 2 +- coolq/cqcode.go | 26 ++++++++++++++----------- global/terminal/double_click.go | 1 - global/terminal/double_click_windows.go | 3 --- go.mod | 2 +- go.sum | 4 ++-- internal/selfupdate/update_others.go | 1 - modules/api/api.go | 2 +- modules/api/caller.go | 2 +- {internal => pkg}/onebot/onebot.go | 0 {internal => pkg}/onebot/spec.go | 0 {internal => pkg}/onebot/supported.go | 0 server/http.go | 2 +- server/middlewares.go | 2 +- server/websocket.go | 2 +- 16 files changed, 25 insertions(+), 26 deletions(-) rename {internal => pkg}/onebot/onebot.go (100%) rename {internal => pkg}/onebot/spec.go (100%) rename {internal => pkg}/onebot/supported.go (100%) diff --git a/coolq/api.go b/coolq/api.go index 50a90a8ff..f54ac67db 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -28,9 +28,9 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/download" "github.com/Mrs4s/go-cqhttp/internal/msg" - "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/filter" + "github.com/Mrs4s/go-cqhttp/pkg/onebot" ) type guildMemberPageToken struct { diff --git a/coolq/bot.go b/coolq/bot.go index 531c0f684..512a1e684 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -27,7 +27,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/mime" "github.com/Mrs4s/go-cqhttp/internal/msg" - "github.com/Mrs4s/go-cqhttp/internal/onebot" + "github.com/Mrs4s/go-cqhttp/pkg/onebot" ) // CQBot CQBot结构体,存储Bot实例相关配置 diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 4dfdf482c..74e8697e9 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -31,8 +31,8 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/download" "github.com/Mrs4s/go-cqhttp/internal/mime" "github.com/Mrs4s/go-cqhttp/internal/msg" - "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/internal/param" + "github.com/Mrs4s/go-cqhttp/pkg/onebot" ) // TODO: move this file to internal/msg, internal/onebot @@ -538,8 +538,11 @@ func (bot *CQBot) at(id, name string) (m any, err error) { } // convertV11 ConvertElement11 -func (bot *CQBot) convertV11(elem msg.Element) (m any, err error, failed bool) { // nolint +func (bot *CQBot) convertV11(elem msg.Element) (m any, ok bool, err error) { switch elem.Type { + default: + // not ok + return case "at": qq := elem.Get("qq") if qq == "all" { @@ -549,24 +552,25 @@ func (bot *CQBot) convertV11(elem msg.Element) (m any, err error, failed bool) { m, err = bot.at(qq, elem.Get("name")) case "record": m, err = bot.voice(elem) - default: - failed = true } + ok = true return } // convertV12 ConvertElement12 -func (bot *CQBot) convertV12(elem msg.Element) (m any, err error, failed bool) { // nolint +func (bot *CQBot) convertV12(elem msg.Element) (m any, ok bool, err error) { switch elem.Type { + default: + // not ok + return case "mention": m, err = bot.at(elem.Get("user_id"), elem.Get("name")) case "mention_all": m = message.AtAll() case "voice": m, err = bot.voice(elem) - default: - failed = true } + ok = true return } @@ -576,16 +580,16 @@ func (bot *CQBot) convertV12(elem msg.Element) (m any, err error, failed bool) { // // message.IMessageElement []message.IMessageElement nil func (bot *CQBot) ConvertElement(spec *onebot.Spec, elem msg.Element, sourceType message.SourceType) (m any, err error) { - var failed bool + var ok bool switch spec.Version { case 11: - m, err, failed = bot.convertV11(elem) + m, ok, err = bot.convertV11(elem) case 12: - m, err, failed = bot.convertV12(elem) + m, ok, err = bot.convertV12(elem) default: panic("invalid onebot version:" + strconv.Itoa(spec.Version)) } - if !failed { + if ok { return m, err } diff --git a/global/terminal/double_click.go b/global/terminal/double_click.go index ff5645221..53f0e3638 100644 --- a/global/terminal/double_click.go +++ b/global/terminal/double_click.go @@ -1,5 +1,4 @@ //go:build !windows -// +build !windows package terminal diff --git a/global/terminal/double_click_windows.go b/global/terminal/double_click_windows.go index 4c3f4e828..b75367b67 100644 --- a/global/terminal/double_click_windows.go +++ b/global/terminal/double_click_windows.go @@ -1,6 +1,3 @@ -//go:build windows -// +build windows - package terminal import ( diff --git a/go.mod b/go.mod index aa779fe34..1c7cc23ea 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230220070519-af032dec9677 + github.com/Mrs4s/MiraiGo v0.0.0-20230223093528-5a89d8a9bff2 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 7e947cb8d..6a3249896 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230220070519-af032dec9677 h1:ekVLExixkpJybGXsRyQorPZJN7k2b5ZqH+pOgPVfAJM= -github.com/Mrs4s/MiraiGo v0.0.0-20230220070519-af032dec9677/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230223093528-5a89d8a9bff2 h1:dXIxqB73+Ur2S6kUB5M6eIh/vTUAL4FWfIXvSPb2dus= +github.com/Mrs4s/MiraiGo v0.0.0-20230223093528-5a89d8a9bff2/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= diff --git a/internal/selfupdate/update_others.go b/internal/selfupdate/update_others.go index 1bd535dd7..40bb6169b 100644 --- a/internal/selfupdate/update_others.go +++ b/internal/selfupdate/update_others.go @@ -1,5 +1,4 @@ //go:build !windows -// +build !windows package selfupdate diff --git a/modules/api/api.go b/modules/api/api.go index acac76dd1..e260e8026 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -5,7 +5,7 @@ package api import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/onebot" + "github.com/Mrs4s/go-cqhttp/pkg/onebot" ) func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG { diff --git a/modules/api/caller.go b/modules/api/caller.go index 344920bb7..2d3acf8f1 100644 --- a/modules/api/caller.go +++ b/modules/api/caller.go @@ -6,7 +6,7 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/onebot" + "github.com/Mrs4s/go-cqhttp/pkg/onebot" ) //go:generate go run ./../../cmd/api-generator -pkg api -path=./../../coolq/api.go,./../../coolq/api_v12.go -o api.go diff --git a/internal/onebot/onebot.go b/pkg/onebot/onebot.go similarity index 100% rename from internal/onebot/onebot.go rename to pkg/onebot/onebot.go diff --git a/internal/onebot/spec.go b/pkg/onebot/spec.go similarity index 100% rename from internal/onebot/spec.go rename to pkg/onebot/spec.go diff --git a/internal/onebot/supported.go b/pkg/onebot/supported.go similarity index 100% rename from internal/onebot/supported.go rename to pkg/onebot/supported.go diff --git a/server/http.go b/server/http.go index 98c59e393..9020d9851 100644 --- a/server/http.go +++ b/server/http.go @@ -26,10 +26,10 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" + "github.com/Mrs4s/go-cqhttp/pkg/onebot" ) // HTTPServer HTTP通信相关配置 diff --git a/server/middlewares.go b/server/middlewares.go index c99634334..e130cf55b 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -8,8 +8,8 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/modules/api" + "github.com/Mrs4s/go-cqhttp/pkg/onebot" "golang.org/x/time/rate" ) diff --git a/server/websocket.go b/server/websocket.go index f5ef674db..baaa471b1 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -22,10 +22,10 @@ import ( "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/modules/api" "github.com/Mrs4s/go-cqhttp/modules/config" "github.com/Mrs4s/go-cqhttp/modules/filter" + "github.com/Mrs4s/go-cqhttp/pkg/onebot" ) type webSocketServer struct { From 9b0fae6346de6057c6fbfb166c17fc3a1df56bdf Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 27 Feb 2023 15:22:19 +0800 Subject: [PATCH 538/612] api-gen: fix import path --- cmd/api-generator/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index 95260b76b..cf96c0653 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -72,11 +72,10 @@ func (g *generator) genRouter(routers []Router) { actions[i] = `"` + actions[i] + `"` } - // TODO: v12 和 all 的 switch-case 由常量改为数组寻址, 以利用 get_supported_actions g.WriteString("import (\n\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/coolq\"\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/global\"\n") - g.WriteString("\"github.com/Mrs4s/go-cqhttp/internal/onebot\"\n") + g.WriteString("\"github.com/Mrs4s/go-cqhttp/pkg/onebot\"\n") g.WriteString(")\n\n") g.WriteString(`func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG { if spec.Version == 12 { From 72173337ae67c726522a062c3ca55878b1508636 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Mon, 27 Feb 2023 16:00:28 +0800 Subject: [PATCH 539/612] api-gen: clean up --- cmd/api-generator/main.go | 131 ++++++++++++-------------------------- modules/api/api.go | 34 +++++----- 2 files changed, 59 insertions(+), 106 deletions(-) diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index cf96c0653..f97d7dd49 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -10,6 +10,7 @@ import ( "go/token" "io" "os" + "reflect" "sort" "strconv" "strings" @@ -28,7 +29,6 @@ type Param struct { type Router struct { Func string - Version []uint16 Path []string PathV11 []string // v11 only PathV12 []string // v12 only @@ -59,62 +59,33 @@ func (g *generator) header() { } func (g *generator) genRouter(routers []Router) { - var actions []string // for onebot v12 get_supported_actions - for _, router := range routers { - if len(router.PathV12) > 0 { - actions = append(actions, router.PathV12...) - } - if len(router.Path) > 0 { - actions = append(actions, router.Path...) - } - } - for i := range actions { - actions[i] = `"` + actions[i] + `"` - } - g.WriteString("import (\n\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/coolq\"\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/global\"\n") g.WriteString("\"github.com/Mrs4s/go-cqhttp/pkg/onebot\"\n") g.WriteString(")\n\n") - g.WriteString(`func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG { - if spec.Version == 12 { + g.WriteString(`func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG {`) + genVer := func(path int) { + g.writef(`if spec.Version == %d { switch action { -`) - for _, router := range routers { - g.router(router, PathV12) - } - io.WriteString(g.out, `}}`) - io.WriteString(g.out, "\n") - g.WriteString(`if spec.Version == 11 { - switch action { - `) - for _, router := range routers { - g.router(router, PathV11) + `, path) + for _, router := range routers { + g.router(router, path) + } + g.WriteString("}}\n") } - io.WriteString(g.out, `}}`) - io.WriteString(g.out, "\n") - io.WriteString(g.out, "switch action {\n") + genVer(PathV11) + genVer(PathV12) + // generic path + g.WriteString("switch action {\n") for _, router := range routers { g.router(router, PathAll) } - io.WriteString(g.out, `}`) - io.WriteString(g.out, "\n") - io.WriteString(g.out, "return coolq.Failed(404, \"API_NOT_FOUND\", \"API不存在\")}") + g.WriteString("}\n") + g.WriteString("return coolq.Failed(404, \"API_NOT_FOUND\", \"API不存在\")}") } func (g *generator) router(router Router, pathVersion int) { - /* - checkVersion := func(v uint16) bool { - for _, ver := range router.Version { - if ver == v { - return true - } - } - return false - } - */ - path := router.Path if pathVersion == PathV11 { path = router.PathV11 @@ -135,26 +106,17 @@ func (g *generator) router(router Router, pathVersion int) { } g.WriteString(":\n") - if len(router.Version) == 1 { // 目前来说只需要判断一个版本的情况 - check := make([]string, 0, len(router.Version)) - for _, ver := range router.Version { - check = append(check, fmt.Sprintf("spec.Version != %v", ver)) - } - fmt.Fprintf(g.out, "if %v {\n", strings.Join(check, " && ")) - fmt.Fprintf(g.out, "return coolq.Failed(405, \"VERSION_ERROR\", \"API版本不匹配\")}\n") - } - for i, p := range router.Params { - if p.Name == "spec" { + if p.Type == "*onebot.Spec" { continue } if p.Default == "" { v := "p.Get(" + strconv.Quote(p.Name) + ")" - fmt.Fprintf(g.out, "p%d := %s\n", i, conv(v, p.Type)) + g.writef("p%d := %s\n", i, conv(v, p.Type)) } else { - fmt.Fprintf(g.out, "p%d := %s\n", i, p.Default) - fmt.Fprintf(g.out, "if pt := p.Get(%s); pt.Exists() {\n", strconv.Quote(p.Name)) - fmt.Fprintf(g.out, "p%d = %s\n}\n", i, conv("pt", p.Type)) + g.writef("p%d := %s\n", i, p.Default) + g.writef("if pt := p.Get(%s); pt.Exists() {\n", strconv.Quote(p.Name)) + g.writef("p%d = %s\n}\n", i, conv("pt", p.Type)) } } @@ -163,11 +125,11 @@ func (g *generator) router(router Router, pathVersion int) { if i != 0 { g.WriteString(", ") } - if p.Name == "spec" { - fmt.Fprintf(g.out, "spec") + if p.Type == "*onebot.Spec" { + g.WriteString("spec") continue } - fmt.Fprintf(g.out, "p%d", i) + g.writef("p%d", i) } g.WriteString(")\n") } @@ -175,7 +137,7 @@ func (g *generator) router(router Router, pathVersion int) { func conv(v, t string) string { switch t { default: - panic("unknown type: " + t) + panic("unsupported type: " + t) case "gjson.Result", "*onebot.Spec": return v case "int64": @@ -208,23 +170,15 @@ func main() { for _, decl := range file.Decls { switch decl := decl.(type) { case *ast.FuncDecl: - if !decl.Name.IsExported() || decl.Recv == nil { - continue - } - if st, ok := decl.Recv.List[0].Type.(*ast.StarExpr); !ok || st.X.(*ast.Ident).Name != "CQBot" { + if !decl.Name.IsExported() || decl.Recv == nil || + typeName(decl.Recv.List[0].Type) != "*CQBot" { continue } router := Router{Func: decl.Name.Name} // compute params for _, p := range decl.Type.Params.List { - var typ string - switch t := p.Type.(type) { - case *ast.Ident: - typ = t.Name - case *ast.SelectorExpr: - typ = t.X.(*ast.Ident).Name + "." + t.Sel.Name - } + typ := typeName(p.Type) for _, name := range p.Names { router.Params = append(router.Params, Param{Name: snakecase(name.Name), Type: typ}) } @@ -261,13 +215,6 @@ func main() { } } } - case "version": - version := strings.Split(args, ",") - for _, v := range version { - if i, err := strconv.ParseUint(v, 10, 16); err == nil { - router.Version = append(router.Version, uint16(i)) - } - } } sort.Slice(router.Path, func(i, j int) bool { return router.Path[i] < router.Path[j] @@ -335,7 +282,7 @@ func unquote(s string) string { func parseMap(input string, sep string) map[string]string { out := make(map[string]string) for _, arg := range strings.Split(input, ",") { - k, v, ok := cut(arg, sep) + k, v, ok := strings.Cut(arg, sep) if !ok { out[k] = "true" } @@ -353,20 +300,13 @@ func match(text string) (string, string) { return "", "" } text = strings.Trim(text, "@)") - cmd, args, ok := cut(text, "(") + cmd, args, ok := strings.Cut(text, "(") if !ok { return "", "" } return cmd, unquote(args) } -func cut(s, sep string) (before, after string, found bool) { - if i := strings.Index(s, sep); i >= 0 { - return s[:i], s[i+len(sep):], true - } - return s, "", false -} - // some abbreviations need translation before transforming ro snake case var replacer = strings.NewReplacer("ID", "Id") @@ -400,3 +340,16 @@ func convDefault(s string, t string) string { } return "" } + +func typeName(x ast.Node) string { + switch x := x.(type) { + case *ast.Ident: + return x.Name + case *ast.SelectorExpr: + return typeName(x.X) + "." + x.Sel.Name + case *ast.StarExpr: + return "*" + typeName(x.X) + default: + panic("unhandled type: " + reflect.TypeOf(x).String()) + } +} diff --git a/modules/api/api.go b/modules/api/api.go index e260e8026..8573a0895 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -9,23 +9,6 @@ import ( ) func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG { - if spec.Version == 12 { - switch action { - case "get_self_info": - return c.bot.CQGetLoginInfo() - case "get_user_info": - p0 := p.Get("user_id").Int() - return c.bot.CQGetStrangerInfo(p0) - case "get_version": - return c.bot.CQGetVersion() - case "send_message": - p0 := p.Get("group_id").String() - p1 := p.Get("user_id").String() - p2 := p.Get("detail_type").String() - p3 := p.Get("message") - return c.bot.CQSendMessageV12(p0, p1, p2, p3) - } - } if spec.Version == 11 { switch action { case ".handle_quick_operation": @@ -77,6 +60,23 @@ func (c *Caller) call(action string, spec *onebot.Spec, p Getter) global.MSG { return c.bot.CQSendPrivateMessage(p0, p1, p2, p3) } } + if spec.Version == 12 { + switch action { + case "get_self_info": + return c.bot.CQGetLoginInfo() + case "get_user_info": + p0 := p.Get("user_id").Int() + return c.bot.CQGetStrangerInfo(p0) + case "get_version": + return c.bot.CQGetVersion() + case "send_message": + p0 := p.Get("group_id").String() + p1 := p.Get("user_id").String() + p2 := p.Get("detail_type").String() + p3 := p.Get("message") + return c.bot.CQSendMessageV12(p0, p1, p2, p3) + } + } switch action { case ".get_word_slices": p0 := p.Get("content").String() From ddd52ca9335cb5c4cf8ec6dfc1848de566b592b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 20:39:25 +0800 Subject: [PATCH 540/612] =?UTF-8?q?feat:=20=E7=A6=81=E7=94=A8=E5=BF=AB?= =?UTF-8?q?=E9=80=9F=E7=BC=96=E8=BE=91&=E4=BC=98=E5=8C=96=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gocq/main.go | 11 ++++++++--- global/terminal/doc.go | 2 +- global/terminal/double_click.go | 2 +- global/terminal/quick_edit.go | 6 ++++++ global/terminal/quick_edit_windows.go | 19 +++++++++++++++++++ global/terminal/vt100.go | 6 ++++++ global/terminal/vt100_windows.go | 19 +++++++++++++++++++ main.go | 4 ++++ 8 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 global/terminal/quick_edit.go create mode 100644 global/terminal/quick_edit_windows.go create mode 100644 global/terminal/vt100.go create mode 100644 global/terminal/vt100_windows.go diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index fded83763..adde588b2 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -41,8 +41,10 @@ var allowStatus = [...]client.UserOnlineStatus{ client.StatusGaming, client.StatusVacationing, client.StatusWatchingTV, client.StatusFitness, } -// Main 启动主程序 -func Main() { +// InitBase 解析参数并检测 +// +// 如果在 windows 下双击打开了程序,程序将在此函数释出脚本后终止 +func InitBase() { base.Parse() if !base.FastStart && terminal.RunningByDoubleClick() { err := terminal.NoMoreDoubleClick() @@ -50,7 +52,7 @@ func Main() { log.Errorf("遇到错误: %v", err) time.Sleep(time.Second * 5) } - return + os.Exit(0) } switch { case base.LittleH: @@ -65,7 +67,10 @@ func Main() { } } base.Init() +} +// Main 启动主程序,必须在 InitBase 之后执行 +func Main() { rotateOptions := []rotatelogs.Option{ rotatelogs.WithRotationTime(time.Hour * 24), } diff --git a/global/terminal/doc.go b/global/terminal/doc.go index 55212d9f2..80afc19f0 100644 --- a/global/terminal/doc.go +++ b/global/terminal/doc.go @@ -1,2 +1,2 @@ -// Package terminal 包含用于检测在windows下是否通过双击运行go-cqhttp的函数 +// Package terminal 包含用于检测在windows下是否通过双击运行go-cqhttp, 禁用快速编辑, 启用VT100的函数 package terminal diff --git a/global/terminal/double_click.go b/global/terminal/double_click.go index 53f0e3638..dbe3d9bc3 100644 --- a/global/terminal/double_click.go +++ b/global/terminal/double_click.go @@ -2,7 +2,7 @@ package terminal -// RunningByDoubleClick 检查是否通过双击直接运行,非Windows系统永远返回false +// RunningByDoubleClick 检查是否通过双击直接运行,非Windows系统永远返回false func RunningByDoubleClick() bool { return false } diff --git a/global/terminal/quick_edit.go b/global/terminal/quick_edit.go new file mode 100644 index 000000000..b85bf71ff --- /dev/null +++ b/global/terminal/quick_edit.go @@ -0,0 +1,6 @@ +package terminal + +// DisableQuickEdit 禁用快速编辑,非Windows系统永远返回nil +func DisableQuickEdit() error { + return nil +} diff --git a/global/terminal/quick_edit_windows.go b/global/terminal/quick_edit_windows.go new file mode 100644 index 000000000..1186b488c --- /dev/null +++ b/global/terminal/quick_edit_windows.go @@ -0,0 +1,19 @@ +package terminal + +import "golang.org/x/sys/windows" + +// DisableQuickEdit 禁用快速编辑 +func DisableQuickEdit() error { + stdin := windows.Handle(os.Stdin.Fd()) + + var mode uint32 + err := windows.GetConsoleMode(stdin, &mode) + if err != nil { + return err + } + + mode &^= windows.ENABLE_QUICK_EDIT_MODE // 禁用快速编辑模式 + mode |= windows.ENABLE_EXTENDED_FLAGS // 启用扩展标志 + + return windows.SetConsoleMode(stdin, mode) +} diff --git a/global/terminal/vt100.go b/global/terminal/vt100.go new file mode 100644 index 000000000..ff135a4e0 --- /dev/null +++ b/global/terminal/vt100.go @@ -0,0 +1,6 @@ +package terminal + +// EnableVT100 启用颜色、控制字符,非Windows系统永远返回nil +func EnableVT100() error { + return nil +} diff --git a/global/terminal/vt100_windows.go b/global/terminal/vt100_windows.go new file mode 100644 index 000000000..b879ae1d4 --- /dev/null +++ b/global/terminal/vt100_windows.go @@ -0,0 +1,19 @@ +package terminal + +import "golang.org/x/sys/windows" + +// EnableVT100 启用颜色、控制字符 +func EnableVT100() error { + stdout := windows.Handle(os.Stdout.Fd()) + + var mode uint32 + err := windows.GetConsoleMode(stdout, &mode) + if err != nil { + return err + } + + mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING // 启用虚拟终端处理 + mode |= windows.ENABLE_PROCESSED_OUTPUT // 启用处理后的输出 + + return windows.SetConsoleMode(stdout, mode) +} diff --git a/main.go b/main.go index 30838f77d..74dbfbe93 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "github.com/Mrs4s/go-cqhttp/cmd/gocq" + "github.com/Mrs4s/go-cqhttp/global/terminal" _ "github.com/Mrs4s/go-cqhttp/db/leveldb" // leveldb 数据库支持 _ "github.com/Mrs4s/go-cqhttp/modules/silk" // silk编码模块 @@ -13,5 +14,8 @@ import ( ) func main() { + gocq.InitBase() + terminal.DisableQuickEdit() + terminal.EnableVT100() gocq.Main() } From 2830676e3bc43b370477c704655ff9e8cb9b4190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 20:44:46 +0800 Subject: [PATCH 541/612] make lint happy --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 74dbfbe93..1c77d8c75 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,7 @@ import ( func main() { gocq.InitBase() - terminal.DisableQuickEdit() - terminal.EnableVT100() + _ = terminal.DisableQuickEdit() + _ = terminal.EnableVT100() gocq.Main() } From 63d9ffa90bb2cce8594371eb41267c2b55f799e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 20:47:40 +0800 Subject: [PATCH 542/612] make lint happy --- global/terminal/quick_edit.go | 2 ++ global/terminal/vt100.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/global/terminal/quick_edit.go b/global/terminal/quick_edit.go index b85bf71ff..afa6b4c23 100644 --- a/global/terminal/quick_edit.go +++ b/global/terminal/quick_edit.go @@ -1,3 +1,5 @@ +//go:build !windows + package terminal // DisableQuickEdit 禁用快速编辑,非Windows系统永远返回nil diff --git a/global/terminal/vt100.go b/global/terminal/vt100.go index ff135a4e0..34bda9e30 100644 --- a/global/terminal/vt100.go +++ b/global/terminal/vt100.go @@ -1,3 +1,5 @@ +//go:build !windows + package terminal // EnableVT100 启用颜色、控制字符,非Windows系统永远返回nil From 1b8ebf55a5df3dbb59e021f3b399720d7f02ab2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 20:49:54 +0800 Subject: [PATCH 543/612] make lint happy --- global/terminal/quick_edit_windows.go | 6 +++++- global/terminal/vt100_windows.go | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/global/terminal/quick_edit_windows.go b/global/terminal/quick_edit_windows.go index 1186b488c..3ac9aef40 100644 --- a/global/terminal/quick_edit_windows.go +++ b/global/terminal/quick_edit_windows.go @@ -1,6 +1,10 @@ package terminal -import "golang.org/x/sys/windows" +import ( + "os" + + "golang.org/x/sys/windows" +) // DisableQuickEdit 禁用快速编辑 func DisableQuickEdit() error { diff --git a/global/terminal/vt100_windows.go b/global/terminal/vt100_windows.go index b879ae1d4..0105435fb 100644 --- a/global/terminal/vt100_windows.go +++ b/global/terminal/vt100_windows.go @@ -1,6 +1,10 @@ package terminal -import "golang.org/x/sys/windows" +import ( + "os" + + "golang.org/x/sys/windows" +) // EnableVT100 启用颜色、控制字符 func EnableVT100() error { From dbddd18e3af9546a6935e8999a940a58845a71ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 21:14:35 +0800 Subject: [PATCH 544/612] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gocq/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index adde588b2..bcedbbc04 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -43,7 +43,9 @@ var allowStatus = [...]client.UserOnlineStatus{ // InitBase 解析参数并检测 // -// 如果在 windows 下双击打开了程序,程序将在此函数释出脚本后终止 +// 如果在 windows 下双击打开了程序,程序将在此函数释出脚本后终止; +// 如果传入 -h 参数,程序将打印帮助后终止; +// 如果传入 -d 参数,程序将在启动 daemon 后终止。 func InitBase() { base.Parse() if !base.FastStart && terminal.RunningByDoubleClick() { From c4d703dc866e2a199d73b1def6be658322e3524b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:11:05 +0800 Subject: [PATCH 545/612] fix mouse scroll --- global/terminal/quick_edit_windows.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/global/terminal/quick_edit_windows.go b/global/terminal/quick_edit_windows.go index 3ac9aef40..81fa7ecc7 100644 --- a/global/terminal/quick_edit_windows.go +++ b/global/terminal/quick_edit_windows.go @@ -19,5 +19,8 @@ func DisableQuickEdit() error { mode &^= windows.ENABLE_QUICK_EDIT_MODE // 禁用快速编辑模式 mode |= windows.ENABLE_EXTENDED_FLAGS // 启用扩展标志 + mode &^= windows.ENABLE_MOUSE_INPUT // 禁用鼠标输入 + mode |= windows.ENABLE_PROCESSED_INPUT // 启用控制输入 + return windows.SetConsoleMode(stdin, mode) } From d867451ef63d23970e3c3379eef3590ffe53a782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:40:52 +0800 Subject: [PATCH 546/612] fix input --- global/terminal/quick_edit_windows.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/global/terminal/quick_edit_windows.go b/global/terminal/quick_edit_windows.go index 81fa7ecc7..3a1dfaadc 100644 --- a/global/terminal/quick_edit_windows.go +++ b/global/terminal/quick_edit_windows.go @@ -22,5 +22,11 @@ func DisableQuickEdit() error { mode &^= windows.ENABLE_MOUSE_INPUT // 禁用鼠标输入 mode |= windows.ENABLE_PROCESSED_INPUT // 启用控制输入 + mode &^= windows.ENABLE_INSERT_MODE // 禁用插入模式 + mode |= windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT // 启用输入回显&逐行输入 + + mode &^= windows.ENABLE_WINDOW_INPUT // 禁用窗口输入 + mode &^= windows.ENABLE_VIRTUAL_TERMINAL_INPUT // 禁用虚拟终端输入 + return windows.SetConsoleMode(stdin, mode) } From 377d7af2c1b73ee003ecd168a20585d0174cdfc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:47:23 +0800 Subject: [PATCH 547/612] add RestoreInputMode --- global/terminal/quick_edit.go | 5 +++++ global/terminal/quick_edit_windows.go | 12 ++++++++++++ main.go | 1 + 3 files changed, 18 insertions(+) diff --git a/global/terminal/quick_edit.go b/global/terminal/quick_edit.go index afa6b4c23..1ed25e46a 100644 --- a/global/terminal/quick_edit.go +++ b/global/terminal/quick_edit.go @@ -2,6 +2,11 @@ package terminal +// RestoreInputMode 还原输入模式,非Windows系统永远返回nil +func RestoreInputMode() error { + return nil +} + // DisableQuickEdit 禁用快速编辑,非Windows系统永远返回nil func DisableQuickEdit() error { return nil diff --git a/global/terminal/quick_edit_windows.go b/global/terminal/quick_edit_windows.go index 3a1dfaadc..dbcd9ff4d 100644 --- a/global/terminal/quick_edit_windows.go +++ b/global/terminal/quick_edit_windows.go @@ -6,6 +6,17 @@ import ( "golang.org/x/sys/windows" ) +var inputmode uint32 + +// RestoreInputMode 还原输入模式 +func RestoreInputMode() error { + if inputmode == 0 { + return nil + } + stdin := windows.Handle(os.Stdin.Fd()) + return windows.SetConsoleMode(stdin, mode) +} + // DisableQuickEdit 禁用快速编辑 func DisableQuickEdit() error { stdin := windows.Handle(os.Stdin.Fd()) @@ -15,6 +26,7 @@ func DisableQuickEdit() error { if err != nil { return err } + inputmode = mode mode &^= windows.ENABLE_QUICK_EDIT_MODE // 禁用快速编辑模式 mode |= windows.ENABLE_EXTENDED_FLAGS // 启用扩展标志 diff --git a/main.go b/main.go index 1c77d8c75..90f5b208d 100644 --- a/main.go +++ b/main.go @@ -18,4 +18,5 @@ func main() { _ = terminal.DisableQuickEdit() _ = terminal.EnableVT100() gocq.Main() + _ = terminal.RestoreInputMode() } From 07b1e6b72e62f65be7a1f19d850ab50fdbf59bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:50:23 +0800 Subject: [PATCH 548/612] fix RestoreInputMode --- global/terminal/quick_edit_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global/terminal/quick_edit_windows.go b/global/terminal/quick_edit_windows.go index dbcd9ff4d..6e6bd9570 100644 --- a/global/terminal/quick_edit_windows.go +++ b/global/terminal/quick_edit_windows.go @@ -14,7 +14,7 @@ func RestoreInputMode() error { return nil } stdin := windows.Handle(os.Stdin.Fd()) - return windows.SetConsoleMode(stdin, mode) + return windows.SetConsoleMode(stdin, inputmode) } // DisableQuickEdit 禁用快速编辑 From 291942357bcbe339fcf8a30acd2ff4923eb0b6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Wed, 1 Mar 2023 20:56:28 +0800 Subject: [PATCH 549/612] =?UTF-8?q?fix:=20=E5=9C=A8=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=97=B6=E4=B8=8D=E7=A6=81=E7=94=A8=E5=BF=AB=E9=80=9F=E7=BC=96?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gocq/main.go | 13 +++++++++++-- global/signal_unix.go | 6 +++--- main.go | 4 +++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index bcedbbc04..43798d356 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -71,8 +71,8 @@ func InitBase() { base.Init() } -// Main 启动主程序,必须在 InitBase 之后执行 -func Main() { +// PrepareData 准备 log, 缓存, 数据库, 必须在 InitBase 之后执行 +func PrepareData() { rotateOptions := []rotatelogs.Option{ rotatelogs.WithRotationTime(time.Hour * 24), } @@ -108,7 +108,10 @@ func Main() { if err := db.Open(); err != nil { log.Fatalf("打开数据库失败: %v", err) } +} +// LoginInteract 登录交互, 可能需要键盘输入, 必须在 InitBase, PrepareData 之后执行 +func LoginInteract() { var byteKey []byte arg := os.Args if len(arg) > 1 { @@ -336,7 +339,13 @@ func Main() { servers.Run(coolq.NewQQBot(cli)) log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") +} +// WaitSignal 在新线程检查更新和网络并等待信号, 必须在 InitBase, PrepareData, LoginInteract 之后执行 +// +// - 直接返回: os.Interrupt, syscall.SIGTERM +// - dump stack: syscall.SIGQUIT, syscall.SIGUSR1 +func WaitSignal() { go func() { selfupdate.CheckUpdate() selfdiagnosis.NetworkDiagnosis(cli) diff --git a/global/signal_unix.go b/global/signal_unix.go index 96e623d10..6ee65dc7e 100644 --- a/global/signal_unix.go +++ b/global/signal_unix.go @@ -14,9 +14,9 @@ import ( func SetupMainSignalHandler() <-chan struct{} { mainOnce.Do(func() { mainStopCh = make(chan struct{}) - mc := make(chan os.Signal, 3) + mc := make(chan os.Signal, 4) closeOnce := sync.Once{} - signal.Notify(mc, os.Interrupt, syscall.SIGTERM, syscall.SIGUSR1) + signal.Notify(mc, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGUSR1) go func() { for { switch <-mc { @@ -24,7 +24,7 @@ func SetupMainSignalHandler() <-chan struct{} { closeOnce.Do(func() { close(mainStopCh) }) - case syscall.SIGUSR1: + case syscall.SIGQUIT, syscall.SIGUSR1: dumpStack() } } diff --git a/main.go b/main.go index 90f5b208d..b3dcdf1ea 100644 --- a/main.go +++ b/main.go @@ -15,8 +15,10 @@ import ( func main() { gocq.InitBase() + gocq.PrepareData() + gocq.LoginInteract() _ = terminal.DisableQuickEdit() _ = terminal.EnableVT100() - gocq.Main() + gocq.WaitSignal() _ = terminal.RestoreInputMode() } From c3840a59880b8f21ff6a751ca248a0484724cbe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 2 Mar 2023 13:25:21 +0800 Subject: [PATCH 550/612] =?UTF-8?q?feat:=20=E5=9C=A8=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E6=97=B6=E8=AE=BE=E7=BD=AE=E6=A0=87=E9=A2=98=E4=B8=BA=20`go-cq?= =?UTF-8?q?http=20=E7=89=88=E6=9C=AC=20=E7=89=88=E6=9D=83`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/terminal/title.go | 15 +++++++++++++ global/terminal/title_windows.go | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 global/terminal/title.go create mode 100644 global/terminal/title_windows.go diff --git a/global/terminal/title.go b/global/terminal/title.go new file mode 100644 index 000000000..b2fc8d78d --- /dev/null +++ b/global/terminal/title.go @@ -0,0 +1,15 @@ +//go:build !windows + +package terminal + +import ( + "fmt" + "time" + + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +func init() { + // 设置标题 + fmt.Printf("\033]0;go-cqhttp "+base.Version+" © 2020 - %d Mrs4s"+"\007", time.Now().Year()) +} diff --git a/global/terminal/title_windows.go b/global/terminal/title_windows.go new file mode 100644 index 000000000..1302db082 --- /dev/null +++ b/global/terminal/title_windows.go @@ -0,0 +1,38 @@ +package terminal + +import ( + "fmt" + "syscall" + "time" + "unsafe" + + "golang.org/x/sys/windows" + + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +var ( + //go:linkname modkernel32 golang.org/x/sys/windows.modkernel32 + modkernel32 *windows.LazyDLL + procSetConsoleTitle = modkernel32.NewProc("SetConsoleTitleW") +) + +//go:linkname errnoErr golang.org/x/sys/windows.errnoErr +func errnoErr(e syscall.Errno) error + +func setConsoleTitle(title string) (err error) { + var p0 *uint16 + p0, err = syscall.UTF16PtrFromString(title) + if err != nil { + return + } + r1, _, e1 := syscall.Syscall(procSetConsoleTitle.Addr(), 1, uintptr(unsafe.Pointer(p0)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func init() { + _ = setConsoleTitle(fmt.Sprintf("go-cqhttp "+base.Version+" © 2020 - %d Mrs4s", time.Now().Year())) +} From 04c44464961c7ffadd56212283e2ee1baa9ffb0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 2 Mar 2023 15:08:50 +0800 Subject: [PATCH 551/612] fix: linkname --- global/terminal/title.go | 4 ++-- global/terminal/title_windows.go | 25 ++++++++----------------- main.go | 1 + 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/global/terminal/title.go b/global/terminal/title.go index b2fc8d78d..806f1ede4 100644 --- a/global/terminal/title.go +++ b/global/terminal/title.go @@ -9,7 +9,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" ) -func init() { - // 设置标题 +// SetTitle 设置标题为 go-cqhttp `版本` `版权` +func SetTitle() { fmt.Printf("\033]0;go-cqhttp "+base.Version+" © 2020 - %d Mrs4s"+"\007", time.Now().Year()) } diff --git a/global/terminal/title_windows.go b/global/terminal/title_windows.go index 1302db082..098d62847 100644 --- a/global/terminal/title_windows.go +++ b/global/terminal/title_windows.go @@ -11,28 +11,19 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" ) -var ( - //go:linkname modkernel32 golang.org/x/sys/windows.modkernel32 - modkernel32 *windows.LazyDLL - procSetConsoleTitle = modkernel32.NewProc("SetConsoleTitleW") -) - -//go:linkname errnoErr golang.org/x/sys/windows.errnoErr -func errnoErr(e syscall.Errno) error - -func setConsoleTitle(title string) (err error) { - var p0 *uint16 - p0, err = syscall.UTF16PtrFromString(title) +func setConsoleTitle(title string) error { + p0, err := syscall.UTF16PtrFromString(title) if err != nil { - return + return err } - r1, _, e1 := syscall.Syscall(procSetConsoleTitle.Addr(), 1, uintptr(unsafe.Pointer(p0)), 0, 0) + r1, _, err := windows.NewLazySystemDLL("kernel32.dll").NewProc("SetConsoleTitleW").Call(uintptr(unsafe.Pointer(p0))) if r1 == 0 { - err = errnoErr(e1) + return err } - return + return nil } -func init() { +// SetTitle 设置标题为 go-cqhttp `版本` `版权` +func SetTitle() { _ = setConsoleTitle(fmt.Sprintf("go-cqhttp "+base.Version+" © 2020 - %d Mrs4s", time.Now().Year())) } diff --git a/main.go b/main.go index 90f5b208d..a63f864fd 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( ) func main() { + terminal.SetTitle() gocq.InitBase() _ = terminal.DisableQuickEdit() _ = terminal.EnableVT100() From bef2ba6f087e3d1315ce8459509667fa0318c399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Thu, 2 Mar 2023 21:27:40 +0800 Subject: [PATCH 552/612] feat(windows): add icon and metadata --- .gitignore | 4 ++ .goreleaser.yml | 3 ++ README.md | 2 +- winres/.gitignore | 1 + winres/gen/json.go | 118 +++++++++++++++++++++++++++++++++++++++++++++ winres/icon.png | Bin 0 -> 83660 bytes winres/icon16.png | Bin 0 -> 2558 bytes winres/init.go | 4 ++ 8 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 winres/.gitignore create mode 100644 winres/gen/json.go create mode 100644 winres/icon.png create mode 100644 winres/icon16.png create mode 100644 winres/init.go diff --git a/.gitignore b/.gitignore index 4066bc3ec..f6233d0f5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,10 @@ internal/btree/*.db # binary builds go-cqhttp +*.exe # macos .DS_Store + +# windwos rc +*.syso diff --git a/.goreleaser.yml b/.goreleaser.yml index c37988653..b6c335d18 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -3,6 +3,9 @@ env: before: hooks: - go mod tidy + - go install github.com/tc-hib/go-winres@latest + - go generate winres/init.go + - go-winres make release: draft: true discussion_category_name: General diff --git a/README.md b/README.md index bf0fea1e2..8efa36cbb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

    - go-cqhttp + go-cqhttp

    diff --git a/winres/.gitignore b/winres/.gitignore new file mode 100644 index 000000000..87e127f65 --- /dev/null +++ b/winres/.gitignore @@ -0,0 +1 @@ +winres.json \ No newline at end of file diff --git a/winres/gen/json.go b/winres/gen/json.go new file mode 100644 index 000000000..415e6dc33 --- /dev/null +++ b/winres/gen/json.go @@ -0,0 +1,118 @@ +// Package main generates winres.json +package main + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "strings" + "time" + + "github.com/Mrs4s/go-cqhttp/internal/base" +) + +const js = `{ + "RT_GROUP_ICON": { + "APP": { + "0000": [ + "icon.png", + "icon16.png" + ] + } + }, + "RT_MANIFEST": { + "#1": { + "0409": { + "identity": { + "name": "go-cqhttp", + "version": "%s" + }, + "description": "", + "minimum-os": "vista", + "execution-level": "as invoker", + "ui-access": false, + "auto-elevate": false, + "dpi-awareness": "system", + "disable-theming": false, + "disable-window-filtering": false, + "high-resolution-scrolling-aware": false, + "ultra-high-resolution-scrolling-aware": false, + "long-path-aware": false, + "printer-driver-isolation": false, + "gdi-scaling": false, + "segment-heap": false, + "use-common-controls-v6": false + } + } + }, + "RT_VERSION": { + "#1": { + "0000": { + "fixed": { + "file_version": "%s", + "product_version": "%s", + "timestamp": "%s" + }, + "info": { + "0409": { + "Comments": "Golang implementation of cqhttp.", + "CompanyName": "Mrs4s", + "FileDescription": "https://github.com/Mrs4s/go-cqhttp", + "FileVersion": "%s", + "InternalName": "", + "LegalCopyright": "©️ 2020 - %d Mrs4s. All Rights Reserved.", + "LegalTrademarks": "", + "OriginalFilename": "GOCQHTTP.EXE", + "PrivateBuild": "", + "ProductName": "go-cqhttp", + "ProductVersion": "%s", + "SpecialBuild": "" + } + } + } + } + } +}` + +const timeformat = `2006-01-02T15:04:05+08:00` + +func main() { + f, err := os.Create("winres.json") + if err != nil { + panic(err) + } + defer f.Close() + v := "" + if base.Version == "(devel)" { + vartag := bytes.NewBuffer(nil) + vartagcmd := exec.Command("git", "tag", "--sort=committerdate") + vartagcmd.Stdout = vartag + err = vartagcmd.Run() + if err != nil { + panic(err) + } + s := strings.Split(vartag.String(), "\n") + v = s[len(s)-2] + } else { + v = base.Version + } + i := strings.Index(v, "-") // remove -rc / -beta + if i <= 0 { + i = len(v) + } + commitcnt := strings.Builder{} + commitcnt.WriteString(v[1:i]) + commitcnt.WriteByte('.') + commitcntcmd := exec.Command("git", "rev-list", "--count", "master") + commitcntcmd.Stdout = &commitcnt + err = commitcntcmd.Run() + if err != nil { + panic(err) + } + fv := commitcnt.String()[:commitcnt.Len()-1] + _, err = fmt.Fprintf(f, js, fv, fv, v, time.Now().Format(timeformat), fv, time.Now().Year(), v) + if err != nil { + panic(err) + } +} diff --git a/winres/icon.png b/winres/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9725af68a99a4b075d7b4bee8bbd497ed658edbb GIT binary patch literal 83660 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 Date: Sun, 5 Mar 2023 18:04:06 +0800 Subject: [PATCH 553/612] dep: update MiraiGo --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1c7cc23ea..641fe45c5 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230223093528-5a89d8a9bff2 + github.com/Mrs4s/MiraiGo v0.0.0-20230305095902-b8f6dadd53b8 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 6a3249896..b3070fb16 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230223093528-5a89d8a9bff2 h1:dXIxqB73+Ur2S6kUB5M6eIh/vTUAL4FWfIXvSPb2dus= -github.com/Mrs4s/MiraiGo v0.0.0-20230223093528-5a89d8a9bff2/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230305095902-b8f6dadd53b8 h1:q2g/gUPbJKhDfZI9HlB1dSbQtfPIMK94w531iLp0nTQ= +github.com/Mrs4s/MiraiGo v0.0.0-20230305095902-b8f6dadd53b8/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 485d5c0df924b1084a938b5407cb215773033f55 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 5 Mar 2023 18:24:37 +0800 Subject: [PATCH 554/612] dep: update MiraiGo --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 641fe45c5..04dc56248 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230305095902-b8f6dadd53b8 + github.com/Mrs4s/MiraiGo v0.0.0-20230305102225-a42218b851d8 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index b3070fb16..08784a851 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230305095902-b8f6dadd53b8 h1:q2g/gUPbJKhDfZI9HlB1dSbQtfPIMK94w531iLp0nTQ= -github.com/Mrs4s/MiraiGo v0.0.0-20230305095902-b8f6dadd53b8/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230305102225-a42218b851d8 h1:0RMPpAHY5W5rdDVRtrc5nWtDCnmrR5E9L6wi9y1uhAc= +github.com/Mrs4s/MiraiGo v0.0.0-20230305102225-a42218b851d8/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 278d6260c80875be0fc2f414ccac1d112eb716fe Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 5 Mar 2023 19:36:50 +0800 Subject: [PATCH 555/612] feat: implement t544 energy --- cmd/gocq/login.go | 28 ++++++++++++++++++++++++++++ internal/download/download.go | 11 ++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 23989df5c..f71f537aa 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -3,18 +3,22 @@ package gocq import ( "bufio" "bytes" + "encoding/hex" "fmt" "image" "image/png" + "net/http" "os" "strings" "time" "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" + "github.com/Mrs4s/MiraiGo/warpper" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" "gopkg.ilharper.com/x/isatty" "github.com/Mrs4s/go-cqhttp/global" @@ -23,6 +27,10 @@ import ( var console = bufio.NewReader(os.Stdin) +func init() { + warpper.DandelionEnergy = energy +} + func readLine() (str string) { str, _ = console.ReadString('\n') str = strings.TrimSpace(str) @@ -266,3 +274,23 @@ func fetchCaptcha(id string) string { } return "" } + +func energy(id string, salt []byte) []byte { + // temporary solution + response, err := download.Request{ + Method: http.MethodPost, + URL: "https://captcha.go-cqhttp.org/sdk/dandelion/energy", + Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + Body: bytes.NewReader([]byte(fmt.Sprintf("id=%s&salt=%s", id, hex.EncodeToString(salt)))), + }.Bytes() + if err != nil { + log.Errorf("获取T544时出现问题: %v", err) + return nil + } + sign, err := hex.DecodeString(gjson.GetBytes(response, "result").String()) + if err != nil { + log.Errorf("获取T544时出现问题: %v", err) + return nil + } + return sign +} diff --git a/internal/download/download.go b/internal/download/download.go index 84fe64c1f..b94f53b57 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -42,13 +42,18 @@ const UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 // Request is a file download request type Request struct { + Method string URL string Header map[string]string Limit int64 + Body io.Reader } func (r Request) do() (*http.Response, error) { - req, err := http.NewRequest(http.MethodGet, r.URL, nil) + if r.Method == "" { + r.Method = http.MethodGet + } + req, err := http.NewRequest(r.Method, r.URL, r.Body) if err != nil { return nil, err } @@ -79,7 +84,7 @@ func (r Request) body() (io.ReadCloser, error) { return resp.Body, err } -// Bytes 对给定URL发送Get请求,返回响应主体 +// Bytes 对给定URL发送请求,返回响应主体 func (r Request) Bytes() ([]byte, error) { rd, err := r.body() if err != nil { @@ -89,7 +94,7 @@ func (r Request) Bytes() ([]byte, error) { return io.ReadAll(rd) } -// JSON 发送GET请求, 并转换响应为JSON +// JSON 发送请求, 并转换响应为JSON func (r Request) JSON() (gjson.Result, error) { rd, err := r.body() if err != nil { From a704009484c3fca5f82e27af45f84b4a3c05136b Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 5 Mar 2023 19:49:43 +0800 Subject: [PATCH 556/612] fix: login message error --- cmd/gocq/login.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index f71f537aa..37da2cf88 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -222,9 +222,7 @@ func loginResponseProcessor(res *client.LoginResponse) error { os.Exit(0) case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError: msg := res.ErrorMessage - if strings.Contains(msg, "版本") { - msg = "密码错误或账号被冻结" - } else if strings.Contains(msg, "冻结") { + if strings.Contains(msg, "冻结") { log.Fatalf("账号被冻结") } log.Warnf("登录失败: %v", msg) From 414f0674317db72c5eee06f8a3ace3d76e9076f5 Mon Sep 17 00:00:00 2001 From: Johnny Hsieh <32300164+mnixry@users.noreply.github.com> Date: Tue, 7 Mar 2023 23:53:12 +0800 Subject: [PATCH 557/612] Update GitHub Workflows actions version (#1958) * Update CI workflow actions version * Update lint workflow actions version * Update release workflow actions version * Update Docker build workflow actions version * disable lint cache to prevent bugs * Update golinter action to latest * Update Go to 1.20 in CI workflow --- .github/workflows/build_docker_image.yml | 14 +++++++------- .github/workflows/ci.yml | 14 ++++---------- .github/workflows/golint.yml | 6 +++--- .github/workflows/release.yml | 6 +++--- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build_docker_image.yml b/.github/workflows/build_docker_image.yml index 8f09138a5..47eb06833 100644 --- a/.github/workflows/build_docker_image.yml +++ b/.github/workflows/build_docker_image.yml @@ -20,10 +20,10 @@ jobs: contents: read steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set time zone - uses: szenius/set-timezone@v1.0 + uses: szenius/set-timezone@v1.1 with: timezoneLinux: "Asia/Shanghai" timezoneMacos: "Asia/Shanghai" @@ -37,7 +37,7 @@ jobs: # password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GHCR - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.repository_owner }} @@ -45,7 +45,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: | ghcr.io/${{ github.repository }} @@ -61,14 +61,14 @@ jobs: type=semver,pattern={{major}} - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v4 with: context: . push: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eebf2ac18..562a1d543 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,18 +24,12 @@ jobs: goarch: "386" fail-fast: true steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Go environment - uses: actions/setup-go@v2.1.3 + uses: actions/setup-go@v3 with: + cache: true go-version: '1.20' - - name: Cache downloaded module - uses: actions/cache@v2 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ matrix.goos }}-${{ matrix.goarch }}-${{ hashFiles('**/go.sum') }} - name: Build binary file env: GOOS: ${{ matrix.goos }} @@ -49,7 +43,7 @@ jobs: export LD_FLAGS="-w -s -X github.com/Mrs4s/go-cqhttp/internal/base.Version=${COMMIT_ID::7}" go build -o "output/$BINARY_NAME" -trimpath -ldflags "$LD_FLAGS" . - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: ${{ !github.head_ref }} with: name: ${{ matrix.goos }}_${{ matrix.goarch }} diff --git a/.github/workflows/golint.yml b/.github/workflows/golint.yml index 75d451a6e..2f14da847 100644 --- a/.github/workflows/golint.yml +++ b/.github/workflows/golint.yml @@ -7,15 +7,15 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Go environment - uses: actions/setup-go@v2.1.3 + uses: actions/setup-go@v3 with: go-version: '1.20' - name: golangci-lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3 with: version: latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b3496afaf..39c74224d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,17 +10,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: '1.20' - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2 + uses: goreleaser/goreleaser-action@v4 with: version: latest args: release --rm-dist From 6eb7a7a857c79133e954dab1d44a680d523873f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 9 Mar 2023 21:56:55 +0800 Subject: [PATCH 558/612] fix: go mod --- go.sum | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/go.sum b/go.sum index 2d1b485f6..c5fdfc821 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/FloatTech/sqlite v1.5.7 h1:Bvo4LSojcZ6dVtbHrkqvt6z4v8e+sj0G5PSUIvdawsk= github.com/FloatTech/sqlite v1.5.7/go.mod h1:zFbHzRfB+CJ+VidfjuVbrcin3DAz283F7hF1hIeHzpY= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw= @@ -19,14 +21,14 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b h1:Zt3pFQditAdWTHCOVkiloc9ZauBoWrb37guFV4iIRvE= -github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/fumiama/go-base16384 v1.6.1 h1:4yb4JgmBJDnQtq3XGXXdLrVwEnRpjhMUt4eAcsNeA30= github.com/fumiama/go-base16384 v1.6.1/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM= github.com/fumiama/go-hide-param v0.1.4 h1:y7TRTzZMdCH9GOXnIzU3B+1BSkcmvejVGmGsz4t0DGU= github.com/fumiama/go-hide-param v0.1.4/go.mod h1:vJkQlJIEI56nIyp7tCQu1/2QOyKtZpudsnJkGk9U1aY= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4= +github.com/fzls/logger v1.1.1 h1:9ZteZN4pxh16MVsjlNgCurQb6qBDxKYDKKq7zifWHIU= +github.com/fzls/logger v1.1.1/go.mod h1:FBZoU1PuXNCQBNr19NZjUWCq1vV6/FoGwc3D9q6of/0= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -45,6 +47,10 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= +github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -52,8 +58,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -86,7 +92,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= @@ -103,6 +111,10 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290 h1:2yISRb1sPRr0lUye5M/Co4uvyj7Y1JfF/7nq2vayF4g= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.290/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290 h1:m7y3kw3JfAmyIiz/mwWEcuV+WayjrxrfZpiqW94uJU4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tbp v1.0.290/go.mod h1:m27bboRL7amS9hl10+/OD8J3sIp0xyeu3kKrEYEpsvY= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -118,6 +130,8 @@ github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -160,6 +174,7 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -178,8 +193,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= From 2fbaa0cdd8cda8f46c6ad2dd742672096069420c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Thu, 9 Mar 2023 22:03:27 +0800 Subject: [PATCH 559/612] fix: build error --- coolq/bot.go | 2 +- qqrobot/command.go | 28 ++++++++++++++-------------- qqrobot/util.go | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/coolq/bot.go b/coolq/bot.go index eddce1066..677cf8ff3 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -313,7 +313,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) (in // 魔改一下,兼容过长的消息,使得青龙面板那边可以发送很多个账号的统计信息 // 禁用 UseHighwayMessage,否则很多消息发不出去,提示 Protocol -> sendPacket msg error: 46 - bot.Client.UseHighwayMessage = false + // bot.Client.UseHighwayMessage = false msgLen := message.EstimateLength(m.Elements) if msgLen <= MaxMessageSize { ret = bot.Client.SendGroupMessage(groupID, m) diff --git a/qqrobot/command.go b/qqrobot/command.go index 84d2a006e..6dd7d403b 100644 --- a/qqrobot/command.go +++ b/qqrobot/command.go @@ -8,9 +8,9 @@ import ( "strings" "time" + "github.com/Mrs4s/go-cqhttp/internal/msg" "github.com/pkg/errors" - "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/qqrobot/qinglong" "github.com/Mrs4s/MiraiGo/message" @@ -18,7 +18,7 @@ import ( ) // 2021/10/02 5:25 by fzls -func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (msg string, extraReplies []message.IMessageElement, err error) { +func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (rspMsg string, extraReplies []message.IMessageElement, err error) { var match []string if match = commandregexAddwhitelist.FindStringSubmatch(commandStr); len(match) == len(commandregexAddwhitelist.SubexpNames()) { // full_match|ruleName|qq @@ -31,10 +31,10 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms rule.Config.ExcludeQQs = append(rule.Config.ExcludeQQs, qq) logger.Info("【Command】", commandStr) - if len(msg) != 0 { - msg += " | " + if len(rspMsg) != 0 { + rspMsg += " | " } - msg += fmt.Sprintf("已将【%v】加入到规则【%v】的白名单", qq, ruleName) + rspMsg += fmt.Sprintf("已将【%v】加入到规则【%v】的白名单", qq, ruleName) } } else if match = commandregexRulenamelist.FindStringSubmatch(commandStr); len(match) == len(commandregexRulenamelist.SubexpNames()) { for _, rule := range r.Rules { @@ -42,10 +42,10 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms continue } - if len(msg) == 0 { - msg += "规则集合:" + if len(rspMsg) == 0 { + rspMsg += "规则集合:" } - msg += ", " + rule.Config.Name + rspMsg += ", " + rule.Config.Name } } else if match = commandregexBuycard.FindStringSubmatch(commandStr); len(match) == len(commandregexBuycard.SubexpNames()) { now := time.Now() @@ -70,12 +70,12 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms return "", nil, err } - err = json.Unmarshal(out, &msg) + err = json.Unmarshal(out, &rspMsg) if err != nil { return "", nil, err } - if strings.Contains(msg, "成功发送以下卡片") { + if strings.Contains(rspMsg, "成功发送以下卡片") { image, err := r._makeLocalImage("https://z3.ax1x.com/2020/12/16/r1yWZT.png") if err == nil { extraReplies = append(extraReplies, image) @@ -94,7 +94,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms return "", nil, err } - err = json.Unmarshal(out, &msg) + err = json.Unmarshal(out, &rspMsg) if err != nil { return "", nil, err } @@ -107,7 +107,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms return fmt.Sprintf("没有找到歌曲:%v", musicName), nil, nil } - msg = fmt.Sprintf("请欣赏歌曲:%v", musicName) + rspMsg = fmt.Sprintf("请欣赏歌曲:%v", musicName) extraReplies = append(extraReplies, musicElem) } else if match = commandRegexQinglongChart.FindStringSubmatch(commandStr); len(match) == len(commandRegexQinglongChart.SubexpNames()) { // full_match|参数 @@ -119,7 +119,7 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms } chartPath := qinglong.QueryChartPath(cookieInfo) - extraReplies = append(extraReplies, &coolq.LocalImageElement{File: chartPath}) + extraReplies = append(extraReplies, &msg.LocalImage{File: chartPath}) extraReplies = append(extraReplies, message.NewText(cookieInfo.ToChatMessage())) } else if match = commandRegexQinglongSummary.FindStringSubmatch(commandStr); len(match) == len(commandRegexQinglongSummary.SubexpNames()) { // full_match|参数 @@ -149,5 +149,5 @@ func (r *QQRobot) processCommand(commandStr string, m *message.GroupMessage) (ms return "", nil, errors.Errorf("没有找到该指令哦") } - return msg, extraReplies, nil + return rspMsg, extraReplies, nil } diff --git a/qqrobot/util.go b/qqrobot/util.go index ff4eec375..0f472250f 100644 --- a/qqrobot/util.go +++ b/qqrobot/util.go @@ -13,10 +13,10 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/go-cqhttp/internal/download" + "github.com/Mrs4s/go-cqhttp/internal/msg" "github.com/gookit/color" logger "github.com/sirupsen/logrus" - "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/global" ) @@ -53,7 +53,7 @@ func (r *QQRobot) _makeLocalImage(imageURL string) (message.IMessageElement, err } hasCacheFile: - return &coolq.LocalImageElement{File: cacheFile}, nil + return &msg.LocalImage{File: cacheFile}, nil } func (r *QQRobot) ocr(groupImageElement *message.GroupImageElement) (ocrResultString string) { From 91facb54ce1cbd802da41a1525f161736302f302 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 11 Mar 2023 01:12:57 +0800 Subject: [PATCH 560/612] update dep --- cmd/gocq/login.go | 14 +++++++------- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 37da2cf88..b2092eb90 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -14,7 +14,7 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" - "github.com/Mrs4s/MiraiGo/warpper" + "github.com/Mrs4s/MiraiGo/wrapper" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -28,7 +28,7 @@ import ( var console = bufio.NewReader(os.Stdin) func init() { - warpper.DandelionEnergy = energy + wrapper.DandelionEnergy = energy } func readLine() (str string) { @@ -273,22 +273,22 @@ func fetchCaptcha(id string) string { return "" } -func energy(id string, salt []byte) []byte { +func energy(uin uint64, id string, salt []byte) ([]byte, error) { // temporary solution response, err := download.Request{ Method: http.MethodPost, URL: "https://captcha.go-cqhttp.org/sdk/dandelion/energy", Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, - Body: bytes.NewReader([]byte(fmt.Sprintf("id=%s&salt=%s", id, hex.EncodeToString(salt)))), + Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&id=%s&salt=%s", uin, id, hex.EncodeToString(salt)))), }.Bytes() if err != nil { log.Errorf("获取T544时出现问题: %v", err) - return nil + return nil, err } sign, err := hex.DecodeString(gjson.GetBytes(response, "result").String()) if err != nil { log.Errorf("获取T544时出现问题: %v", err) - return nil + return nil, err } - return sign + return sign, nil } diff --git a/go.mod b/go.mod index 04dc56248..b8e98172e 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230305102225-a42218b851d8 + github.com/Mrs4s/MiraiGo v0.0.0-20230310170800-80b6b2843637 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 08784a851..fb4dc1487 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230305102225-a42218b851d8 h1:0RMPpAHY5W5rdDVRtrc5nWtDCnmrR5E9L6wi9y1uhAc= -github.com/Mrs4s/MiraiGo v0.0.0-20230305102225-a42218b851d8/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230310170800-80b6b2843637 h1:6Gmc1Y4LLhLFaniN6QbYelXOANOuXwlknmT6+ao7BZw= +github.com/Mrs4s/MiraiGo v0.0.0-20230310170800-80b6b2843637/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 069f9d1335d18004a25ec17d46e2dd37681f9298 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 11 Mar 2023 01:29:33 +0800 Subject: [PATCH 561/612] fix: block login process when account has been banned --- cmd/gocq/login.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index b2092eb90..390a513a5 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -169,6 +169,8 @@ func loginResponseProcessor(res *client.LoginResponse) error { if strings.Contains(text, "1") { ticket := getTicket(res.VerifyUrl) if ticket == "" { + log.Infof("按 Enter 继续....") + readLine() os.Exit(0) } res, err = cli.SubmitTicket(ticket) @@ -223,11 +225,14 @@ func loginResponseProcessor(res *client.LoginResponse) error { case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError: msg := res.ErrorMessage if strings.Contains(msg, "冻结") { - log.Fatalf("账号被冻结") + log.Warnf("账号被冻结") + log.Infof("按 Enter 继续....") + readLine() + os.Exit(0) } log.Warnf("登录失败: %v", msg) - log.Infof("按 Enter 或等待 5s 后继续....") - readLineTimeout(time.Second * 5) + log.Infof("按 Enter 继续....") + readLine() os.Exit(0) } } From a0e3291725a0ef3acfd175cf606a569d0a9c9636 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 11 Mar 2023 01:30:45 +0800 Subject: [PATCH 562/612] feat: display login error code --- cmd/gocq/login.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 390a513a5..200d6b0bd 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -224,13 +224,7 @@ func loginResponseProcessor(res *client.LoginResponse) error { os.Exit(0) case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError: msg := res.ErrorMessage - if strings.Contains(msg, "冻结") { - log.Warnf("账号被冻结") - log.Infof("按 Enter 继续....") - readLine() - os.Exit(0) - } - log.Warnf("登录失败: %v", msg) + log.Warnf("登录失败: %v Code: %v", msg, res.Code) log.Infof("按 Enter 继续....") readLine() os.Exit(0) From 6a291840d73108fbf0dc07096dc53b747a84699d Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 13 Mar 2023 00:44:37 +0800 Subject: [PATCH 563/612] update dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b8e98172e..ec1d3e84d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230310170800-80b6b2843637 + github.com/Mrs4s/MiraiGo v0.0.0-20230312141222-653b853ca5ac github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index fb4dc1487..be40eae7d 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230310170800-80b6b2843637 h1:6Gmc1Y4LLhLFaniN6QbYelXOANOuXwlknmT6+ao7BZw= -github.com/Mrs4s/MiraiGo v0.0.0-20230310170800-80b6b2843637/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230312141222-653b853ca5ac h1:DhqFiVOZeBpKtcJHdrLPmCSxmqRUi7RwxbwiOqg8ScI= +github.com/Mrs4s/MiraiGo v0.0.0-20230312141222-653b853ca5ac/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 3f4630b6d18cfb08d9fab298a8546e2fd3bcf2ef Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 13 Mar 2023 01:01:43 +0800 Subject: [PATCH 564/612] ref: improve protocol display name --- cmd/gocq/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 43798d356..e5e201fcc 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -216,7 +216,7 @@ func LoginInteract() { time.Sleep(time.Second * 5) } log.Info("开始尝试登录并同步消息...") - log.Infof("使用协议: %s", device.Protocol) + log.Infof("使用协议: %s", device.Protocol.Version()) cli = newClient() cli.UseDevice(device) isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt From 8124879c77026823359aa655100619f3f1285f77 Mon Sep 17 00:00:00 2001 From: Ju4tCode <42488585+yanyongyu@users.noreply.github.com> Date: Mon, 13 Mar 2023 14:08:55 +0800 Subject: [PATCH 565/612] :sparkles: update docker action --- .github/workflows/build_docker_image.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_docker_image.yml b/.github/workflows/build_docker_image.yml index 47eb06833..c5096e059 100644 --- a/.github/workflows/build_docker_image.yml +++ b/.github/workflows/build_docker_image.yml @@ -4,6 +4,7 @@ on: push: branches: - 'master' + - 'dev' # Sequence of patterns matched against refs/tags tags: - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 @@ -71,6 +72,8 @@ jobs: uses: docker/build-push-action@v4 with: context: . - push: true + push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max From 40e4f405253bf54bbe6689ded38d080fe4e55f02 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 14 Mar 2023 20:53:17 +0800 Subject: [PATCH 566/612] db/sqlite: change ttl to millisecond --- db/sqlite3/sqlite3.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index 50970579c..7db6feb1f 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -15,6 +15,7 @@ import ( "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/utils" + "github.com/Mrs4s/go-cqhttp/db" ) @@ -38,7 +39,7 @@ func init() { if !conf.Enable { return nil } - return &database{db: new(sql.Sqlite), ttl: conf.CacheTTL} + return &database{db: new(sql.Sqlite), ttl: conf.CacheTTL * time.Millisecond} }) } From 0b106d8ef573b36c3e2f78282e4d3fda7ec173d3 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 14 Mar 2023 20:56:11 +0800 Subject: [PATCH 567/612] db/sqlite: use ParseDuration --- db/sqlite3/sqlite3.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/db/sqlite3/sqlite3.go b/db/sqlite3/sqlite3.go index 7db6feb1f..a4f1256b7 100644 --- a/db/sqlite3/sqlite3.go +++ b/db/sqlite3/sqlite3.go @@ -11,6 +11,7 @@ import ( sql "github.com/FloatTech/sqlite" "github.com/pkg/errors" + log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" "github.com/Mrs4s/MiraiGo/binary" @@ -27,8 +28,8 @@ type database struct { // config mongodb 相关配置 type config struct { - Enable bool `yaml:"enable"` - CacheTTL time.Duration `yaml:"cachettl"` + Enable bool `yaml:"enable"` + CacheTTL string `yaml:"cachettl"` } func init() { @@ -39,7 +40,11 @@ func init() { if !conf.Enable { return nil } - return &database{db: new(sql.Sqlite), ttl: conf.CacheTTL * time.Millisecond} + duration, err := time.ParseDuration(conf.CacheTTL) + if err != nil { + log.Fatalf("illegal ttl config: %v", err) + } + return &database{db: new(sql.Sqlite), ttl: duration} }) } From 0312f05f6e74856bf6863da0c6ef448270f714c6 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 15 Mar 2023 02:33:04 +0800 Subject: [PATCH 568/612] feat: basic protocol auto-updater --- cmd/gocq/main.go | 46 +++++++++++++++++++++++++++++++ global/fs.go | 2 ++ go.mod | 2 +- go.sum | 4 +-- modules/config/config.go | 15 +++++----- modules/config/default_config.yml | 2 ++ 6 files changed, 61 insertions(+), 10 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index e5e201fcc..65ff11ab5 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -14,8 +14,10 @@ import ( "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" + "github.com/Mrs4s/go-cqhttp/internal/download" para "github.com/fumiama/go-hide-param" rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "golang.org/x/crypto/pbkdf2" "golang.org/x/term" @@ -102,6 +104,7 @@ func PrepareData() { mkCacheDir(global.VideoPath, "视频") mkCacheDir(global.CachePath, "发送图片") mkCacheDir(path.Join(global.ImagePath, "guild-images"), "频道图片缓存") + mkCacheDir(global.VersionsPath, "版本缓存") cache.Init() db.Init() @@ -221,6 +224,17 @@ func LoginInteract() { cli.UseDevice(device) isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt isTokenLogin := false + + // 加载本地版本信息, 一般是在上次登录时保存的 + versionFile := path.Join(global.VersionsPath, fmt.Sprint(int(cli.Device().Protocol))+".json") + if global.PathExists(versionFile) { + b, err := os.ReadFile(versionFile) + if err == nil { + _ = cli.Device().Protocol.Version().UpdateFromJson(b) + } + log.Infof("从文件 %s 读取协议版本 %v.", versionFile, cli.Device().Protocol.Version()) + } + saveToken := func() { base.AccountToken = cli.GenToken() _ = os.WriteFile("session.token", base.AccountToken, 0o644) @@ -262,6 +276,20 @@ func LoginInteract() { cli.PasswordMd5 = base.PasswordHash } if !isTokenLogin { + if !base.Account.DisableProtocolUpdate { + oldVersionName := device.Protocol.Version().String() + remoteVersion, err := getRemoteLatestProtocolVersion(int(device.Protocol.Version().Protocol)) + if err == nil { + if err = device.Protocol.Version().UpdateFromJson(remoteVersion); err == nil { + if device.Protocol.Version().String() != oldVersionName { + log.Infof("已自动更新协议版本: %s -> %s", oldVersionName, device.Protocol.Version().String()) + } + _ = os.WriteFile(versionFile, remoteVersion, 0o644) + } + } else if err.Error() != "remote version unavailable" { + log.Warnf("检查协议更新失败: %v", err) + } + } if !isQRCodeLogin { if err := commonLogin(); err != nil { log.Fatalf("登录时发生致命错误: %v", err) @@ -408,6 +436,24 @@ func newClient() *client.QQClient { return c } +// TODO: mirror support +var remoteVersions = map[int]string{ + 1: "https://raw.githubusercontent.com/RomiChan/protocol-versions/master/android_phone.json", + 6: "https://raw.githubusercontent.com/RomiChan/protocol-versions/master/android_pad.json", +} + +func getRemoteLatestProtocolVersion(protocolType int) ([]byte, error) { + url, ok := remoteVersions[protocolType] + if !ok { + return nil, errors.New("remote version unavailable") + } + response, err := download.Request{URL: url}.Bytes() + if err != nil { + return nil, err + } + return response, nil +} + type protocolLogger struct{} const fromProtocol = "Protocol -> " diff --git a/global/fs.go b/global/fs.go index aebcf5116..cfa9bd366 100644 --- a/global/fs.go +++ b/global/fs.go @@ -27,6 +27,8 @@ const ( VoicePath = "data/voices" // VideoPath go-cqhttp使用的视频缓存目录 VideoPath = "data/videos" + // VersionsPath go-cqhttp使用的版本信息目录 + VersionsPath = "data/versions" // CachePath go-cqhttp使用的缓存目录 CachePath = "data/cache" // DumpsPath go-cqhttp使用错误转储目录 diff --git a/go.mod b/go.mod index ec1d3e84d..d7abd9428 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230312141222-653b853ca5ac + github.com/Mrs4s/MiraiGo v0.0.0-20230314181550-5e7ade149fff github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index be40eae7d..76c1d5f37 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230312141222-653b853ca5ac h1:DhqFiVOZeBpKtcJHdrLPmCSxmqRUi7RwxbwiOqg8ScI= -github.com/Mrs4s/MiraiGo v0.0.0-20230312141222-653b853ca5ac/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230314181550-5e7ade149fff h1:VOc0uHpfTQde3c+1hdXbvuZiinF3bm3Qc7eNf6waonk= +github.com/Mrs4s/MiraiGo v0.0.0-20230314181550-5e7ade149fff/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= diff --git a/modules/config/config.go b/modules/config/config.go index 22d9879dc..53e2abf29 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -28,13 +28,14 @@ type Reconnect struct { // Account 账号配置 type Account struct { - Uin int64 `yaml:"uin"` - Password string `yaml:"password"` - Encrypt bool `yaml:"encrypt"` - Status int `yaml:"status"` - ReLogin *Reconnect `yaml:"relogin"` - UseSSOAddress bool `yaml:"use-sso-address"` - AllowTempSession bool `yaml:"allow-temp-session"` + Uin int64 `yaml:"uin"` + Password string `yaml:"password"` + Encrypt bool `yaml:"encrypt"` + Status int `yaml:"status"` + ReLogin *Reconnect `yaml:"relogin"` + UseSSOAddress bool `yaml:"use-sso-address"` + AllowTempSession bool `yaml:"allow-temp-session"` + DisableProtocolUpdate bool `yaml:"disable-protocol-update"` } // Config 总配置文件 diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 9e4acd8ea..56d51f7bf 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -15,6 +15,8 @@ account: # 账号相关 use-sso-address: true # 是否允许发送临时会话消息 allow-temp-session: false + # 是否禁用协议更新 + disable-protocol-update: false heartbeat: # 心跳频率, 单位秒 From 1d0b513b96c66efb05922dcfbd8e40282802cd86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 18:35:19 +0000 Subject: [PATCH 569/612] ci(chore): Fix stylings --- cmd/gocq/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 65ff11ab5..e7f40bbfe 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -14,7 +14,6 @@ import ( "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" - "github.com/Mrs4s/go-cqhttp/internal/download" para "github.com/fumiama/go-hide-param" rotatelogs "github.com/lestrrat-go/file-rotatelogs" "github.com/pkg/errors" @@ -22,6 +21,8 @@ import ( "golang.org/x/crypto/pbkdf2" "golang.org/x/term" + "github.com/Mrs4s/go-cqhttp/internal/download" + "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" From 3a60e081f23c393fd4963e5681cf7b085a230e84 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Fri, 17 Mar 2023 22:30:13 +0800 Subject: [PATCH 570/612] feat: protocol updater supports fallback to ghproxy --- cmd/gocq/main.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index e7f40bbfe..28a18debf 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -278,12 +278,15 @@ func LoginInteract() { } if !isTokenLogin { if !base.Account.DisableProtocolUpdate { + log.Infof("正在检查协议更新...") oldVersionName := device.Protocol.Version().String() remoteVersion, err := getRemoteLatestProtocolVersion(int(device.Protocol.Version().Protocol)) if err == nil { if err = device.Protocol.Version().UpdateFromJson(remoteVersion); err == nil { if device.Protocol.Version().String() != oldVersionName { log.Infof("已自动更新协议版本: %s -> %s", oldVersionName, device.Protocol.Version().String()) + } else { + log.Infof("协议已经是最新版本") } _ = os.WriteFile(versionFile, remoteVersion, 0o644) } @@ -437,7 +440,6 @@ func newClient() *client.QQClient { return c } -// TODO: mirror support var remoteVersions = map[int]string{ 1: "https://raw.githubusercontent.com/RomiChan/protocol-versions/master/android_phone.json", 6: "https://raw.githubusercontent.com/RomiChan/protocol-versions/master/android_pad.json", @@ -450,7 +452,7 @@ func getRemoteLatestProtocolVersion(protocolType int) ([]byte, error) { } response, err := download.Request{URL: url}.Bytes() if err != nil { - return nil, err + return download.Request{URL: "https://ghproxy.com/" + url}.Bytes() } return response, nil } From 73bd3c92f39ed6a53131ecac7de1e7e54f6daa7c Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 18 Mar 2023 10:22:40 +0800 Subject: [PATCH 571/612] update dep & add limitation for qrcode login --- cmd/gocq/login.go | 27 ++++++++------------------- cmd/gocq/main.go | 5 +++++ go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 200d6b0bd..fb8615413 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -161,26 +161,15 @@ func loginResponseProcessor(res *client.LoginResponse) error { var text string switch res.Error { case client.SliderNeededError: - log.Warnf("登录需要滑条验证码, 请选择验证方式: ") - log.Warnf("1. 使用浏览器抓取滑条并登录") - log.Warnf("2. 使用手机QQ扫码验证 (需要手Q和gocq在同一网络下).") - log.Warn("请输入(1 - 2):") - text = readIfTTY("1") - if strings.Contains(text, "1") { - ticket := getTicket(res.VerifyUrl) - if ticket == "" { - log.Infof("按 Enter 继续....") - readLine() - os.Exit(0) - } - res, err = cli.SubmitTicket(ticket) - continue + log.Warnf("登录需要滑条验证码, 请验证后重试.") + ticket := getTicket(res.VerifyUrl) + if ticket == "" { + log.Infof("按 Enter 继续....") + readLine() + os.Exit(0) } - cli.Disconnect() - cli.Release() - cli = client.NewClientEmpty() - cli.UseDevice(device) - return qrcodeLogin() + res, err = cli.SubmitTicket(ticket) + continue case client.NeedCaptcha: log.Warnf("登录需要验证码.") _ = os.WriteFile("captcha.jpg", res.CaptchaImage, 0o644) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 28a18debf..b0e2e678a 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -226,6 +226,11 @@ func LoginInteract() { isQRCodeLogin := (base.Account.Uin == 0 || len(base.Account.Password) == 0) && !base.Account.Encrypt isTokenLogin := false + if isQRCodeLogin && cli.Device().Protocol != 2 { + log.Warn("当前协议不支持二维码登录, 请配置账号密码登录.") + os.Exit(0) + } + // 加载本地版本信息, 一般是在上次登录时保存的 versionFile := path.Join(global.VersionsPath, fmt.Sprint(int(cli.Device().Protocol))+".json") if global.PathExists(versionFile) { diff --git a/go.mod b/go.mod index d7abd9428..4b30d234e 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230314181550-5e7ade149fff + github.com/Mrs4s/MiraiGo v0.0.0-20230317162854-fd83d24f6794 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index 76c1d5f37..57e3dad8e 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230314181550-5e7ade149fff h1:VOc0uHpfTQde3c+1hdXbvuZiinF3bm3Qc7eNf6waonk= -github.com/Mrs4s/MiraiGo v0.0.0-20230314181550-5e7ade149fff/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230317162854-fd83d24f6794 h1:V2hkbdJhTGX6tfwEsCg53rUCx/skTGBfwRMHB5/hy7E= +github.com/Mrs4s/MiraiGo v0.0.0-20230317162854-fd83d24f6794/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 4ad7da7a9a87272c9c336e64fabfa74605e69b59 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 18 Mar 2023 10:27:48 +0800 Subject: [PATCH 572/612] feat: add sign-server flag --- cmd/gocq/login.go | 7 ++++++- internal/base/flag.go | 30 ++++++++++++++++-------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index fb8615413..4fb278ca1 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -15,6 +15,7 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/wrapper" + "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -263,9 +264,13 @@ func fetchCaptcha(id string) string { func energy(uin uint64, id string, salt []byte) ([]byte, error) { // temporary solution + signServer := "https://captcha.go-cqhttp.org/sdk/dandelion/energy" + if base.SignServerOverwrite != "" { + signServer = base.SignServerOverwrite + } response, err := download.Request{ Method: http.MethodPost, - URL: "https://captcha.go-cqhttp.org/sdk/dandelion/energy", + URL: signServer, Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&id=%s&salt=%s", uin, id, hex.EncodeToString(salt)))), }.Bytes() diff --git a/internal/base/flag.go b/internal/base/flag.go index b9331f41c..5e546b5f8 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -23,20 +23,21 @@ var ( // config file flags var ( - Debug bool // 是否开启 debug 模式 - RemoveReplyAt bool // 是否删除reply后的at - ExtraReplyData bool // 是否上报额外reply信息 - IgnoreInvalidCQCode bool // 是否忽略无效CQ码 - SplitURL bool // 是否分割URL - ForceFragmented bool // 是否启用强制分片 - SkipMimeScan bool // 是否跳过Mime扫描 - ConvertWebpImage bool // 是否转换Webp图片 - ReportSelfMessage bool // 是否上报自身消息 - UseSSOAddress bool // 是否使用服务器下发的新地址进行重连 - LogForceNew bool // 是否在每次启动时强制创建全新的文件储存日志 - LogColorful bool // 是否启用日志颜色 - FastStart bool // 是否为快速启动 - AllowTempSession bool // 是否允许发送临时会话信息 + Debug bool // 是否开启 debug 模式 + RemoveReplyAt bool // 是否删除reply后的at + ExtraReplyData bool // 是否上报额外reply信息 + IgnoreInvalidCQCode bool // 是否忽略无效CQ码 + SplitURL bool // 是否分割URL + ForceFragmented bool // 是否启用强制分片 + SkipMimeScan bool // 是否跳过Mime扫描 + ConvertWebpImage bool // 是否转换Webp图片 + ReportSelfMessage bool // 是否上报自身消息 + UseSSOAddress bool // 是否使用服务器下发的新地址进行重连 + LogForceNew bool // 是否在每次启动时强制创建全新的文件储存日志 + LogColorful bool // 是否启用日志颜色 + FastStart bool // 是否为快速启动 + AllowTempSession bool // 是否允许发送临时会话信息 + SignServerOverwrite string // 使用特定的服务器进行签名 PostFormat string // 上报格式 string or array Proxy string // 存储 proxy_rewrite,用于设置代理 @@ -60,6 +61,7 @@ func Parse() { flag.StringVar(&LittleWD, "w", "", "cover the working directory") d := flag.Bool("D", false, "debug mode") flag.BoolVar(&FastStart, "faststart", false, "skip waiting 5 seconds") + flag.StringVar(&SignServerOverwrite, "sign-server", "", "use special server to sign tlv") flag.Parse() if *d { From dd33cd959819cb021de6d3816bbfbfb3a0b085f1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 02:28:25 +0000 Subject: [PATCH 573/612] ci(chore): Fix stylings --- cmd/gocq/login.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 4fb278ca1..099cdcc16 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -15,13 +15,14 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/wrapper" - "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "gopkg.ilharper.com/x/isatty" + "github.com/Mrs4s/go-cqhttp/internal/base" + "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/download" ) From 174d99f94b31cee8ca4b9f4a56e6d95d4ec51601 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 18 Mar 2023 10:35:19 +0800 Subject: [PATCH 574/612] fix: add timeout setting for default http client --- internal/download/download.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/download/download.go b/internal/download/download.go index b94f53b57..ff8a7033d 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/pkg/errors" "github.com/tidwall/gjson" @@ -32,6 +33,7 @@ var client = &http.Client{ MaxIdleConns: 0, MaxIdleConnsPerHost: 999, }, + Timeout: time.Second * 5, } // ErrOverSize 响应主体过大时返回此错误 From 1815ed769da3dcc449a83d7a18ca98e73e6747fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 10:41:27 +0800 Subject: [PATCH 575/612] chore(deps): bump golang.org/x/image from 0.3.0 to 0.5.0 (#1913) Bumps [golang.org/x/image](https://github.com/golang/image) from 0.3.0 to 0.5.0. - [Release notes](https://github.com/golang/image/releases) - [Commits](https://github.com/golang/image/compare/v0.3.0...v0.5.0) --- updated-dependencies: - dependency-name: golang.org/x/image dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 4b30d234e..1a375c0fb 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/wdvxdr1123/go-silk v0.0.0-20210316130616-d47b553def60 go.mongodb.org/mongo-driver v1.11.0 golang.org/x/crypto v0.3.0 - golang.org/x/image v0.3.0 + golang.org/x/image v0.5.0 golang.org/x/sys v0.2.0 golang.org/x/term v0.2.0 golang.org/x/time v0.2.0 @@ -53,7 +53,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/text v0.7.0 // indirect golang.org/x/tools v0.1.12 // indirect lukechampine.com/uint128 v1.2.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect diff --git a/go.sum b/go.sum index 57e3dad8e..a33c58409 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/image v0.3.0 h1:HTDXbdK9bjfSWkPzDJIw89W8CAtfFGduujWs33NLLsg= -golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A= +golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= +golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -178,8 +178,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE= golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From e6e30c0a1083740b2a9735fdafe7087e86b0c5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sat, 18 Mar 2023 11:39:05 +0800 Subject: [PATCH 576/612] fix(goreleaser): git rev-list --count master --- .github/workflows/release.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 39c74224d..d7bfbd894 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,11 +9,6 @@ jobs: goreleaser: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: Set up Go uses: actions/setup-go@v3 with: @@ -23,7 +18,7 @@ jobs: uses: goreleaser/goreleaser-action@v4 with: version: latest - args: release --rm-dist + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3ccc2c608703b53ea5f9eb18f37934bc3089f0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sat, 18 Mar 2023 11:50:40 +0800 Subject: [PATCH 577/612] fix(goreleaser): checkout --- .github/workflows/release.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d7bfbd894..1efc1373d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,17 @@ jobs: goreleaser: runs-on: ubuntu-latest steps: + - name: Checkout + run: | + git version + ls -hl + git config --global --add safe.directory /home/runner/work/go-cqhttp/go-cqhttp + git init /home/runner/work/go-cqhttp/go-cqhttp + cd /home/runner/work/go-cqhttp/go-cqhttp + git remote add origin https://github.com/Mrs4s/go-cqhttp + git config --local gc.auto 0 + git pull origin master + - name: Set up Go uses: actions/setup-go@v3 with: From 98c2a2218ac4cb8d24336bdfa7fc4a39f1b0d95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sat, 18 Mar 2023 11:50:40 +0800 Subject: [PATCH 578/612] fix(goreleaser): checkout --- .github/workflows/release.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d7bfbd894..1efc1373d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,17 @@ jobs: goreleaser: runs-on: ubuntu-latest steps: + - name: Checkout + run: | + git version + ls -hl + git config --global --add safe.directory /home/runner/work/go-cqhttp/go-cqhttp + git init /home/runner/work/go-cqhttp/go-cqhttp + cd /home/runner/work/go-cqhttp/go-cqhttp + git remote add origin https://github.com/Mrs4s/go-cqhttp + git config --local gc.auto 0 + git pull origin master + - name: Set up Go uses: actions/setup-go@v3 with: From d77dc9ef6453b4cb0e755b5dcf143439420fa6b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sat, 18 Mar 2023 11:54:29 +0800 Subject: [PATCH 579/612] fix(goreleaser): checkout --- .github/workflows/release.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1efc1373d..35f4ace97 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,13 +12,7 @@ jobs: - name: Checkout run: | git version - ls -hl - git config --global --add safe.directory /home/runner/work/go-cqhttp/go-cqhttp - git init /home/runner/work/go-cqhttp/go-cqhttp - cd /home/runner/work/go-cqhttp/go-cqhttp - git remote add origin https://github.com/Mrs4s/go-cqhttp - git config --local gc.auto 0 - git pull origin master + git clone https://github.com/Mrs4s/go-cqhttp.git /home/runner/work/go-cqhttp/go-cqhttp - name: Set up Go uses: actions/setup-go@v3 From 588728aa62e4059239b139e4e08913b9f00cc96c Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 18 Mar 2023 13:47:39 +0800 Subject: [PATCH 580/612] log: print code 235 reason --- cmd/gocq/login.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 099cdcc16..1fa4a820b 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -216,6 +216,9 @@ func loginResponseProcessor(res *client.LoginResponse) error { case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError: msg := res.ErrorMessage log.Warnf("登录失败: %v Code: %v", msg, res.Code) + if res.Code == 235 { + log.Warnf("请删除 device.json 后重试.") + } log.Infof("按 Enter 继续....") readLine() os.Exit(0) From 82ecf19480368d02496168f8c397b0c96b87c0c0 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 18 Mar 2023 14:04:13 +0800 Subject: [PATCH 581/612] fix #1989 --- coolq/cqcode.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 74e8697e9..7cb8a7eec 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -545,6 +545,9 @@ func (bot *CQBot) convertV11(elem msg.Element) (m any, ok bool, err error) { return case "at": qq := elem.Get("qq") + if qq == "" { + qq = elem.Get("target") + } if qq == "all" { m = message.AtAll() break From 008d546f1a2e501427a75e5abccb7b6d3ad86b1d Mon Sep 17 00:00:00 2001 From: EnderFx <1501721149@qq.com> Date: Mon, 20 Mar 2023 10:34:05 +0800 Subject: [PATCH 582/612] Update cqcode.go (#2001) fix #1998 --- coolq/cqcode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 7cb8a7eec..c7751b524 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -642,7 +642,7 @@ func (bot *CQBot) ConvertElement(spec *onebot.Spec, elem msg.Element, sourceType return bot.reply(spec, elem, sourceType) case "forward": id := elem.Get("id") - if id != "" { + if id == "" { return nil, errors.New("forward 消息中必须包含 id") } fwdMsg := bot.Client.DownloadForwardMessage(id) From 43ff36e3e8e46fada2a92f6f4130c972e26d670b Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 27 Mar 2023 09:40:02 +0800 Subject: [PATCH 583/612] update dep. fix #2017 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1a375c0fb..ed06f019c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230317162854-fd83d24f6794 + github.com/Mrs4s/MiraiGo v0.0.0-20230319092203-62d60f132203 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index a33c58409..c3e17fbcf 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230317162854-fd83d24f6794 h1:V2hkbdJhTGX6tfwEsCg53rUCx/skTGBfwRMHB5/hy7E= -github.com/Mrs4s/MiraiGo v0.0.0-20230317162854-fd83d24f6794/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230319092203-62d60f132203 h1:H/bZoEPwRK/TgqqFxtQGY1gO5Vj28/LskhT5wIyzxlo= +github.com/Mrs4s/MiraiGo v0.0.0-20230319092203-62d60f132203/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From d70d66d6d76a165324770e45fe331d413d9676de Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 27 Mar 2023 09:51:47 +0800 Subject: [PATCH 584/612] fix #2010 --- internal/download/download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/download/download.go b/internal/download/download.go index ff8a7033d..a7b7e06f3 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -33,7 +33,7 @@ var client = &http.Client{ MaxIdleConns: 0, MaxIdleConnsPerHost: 999, }, - Timeout: time.Second * 5, + Timeout: time.Second * 15, } // ErrOverSize 响应主体过大时返回此错误 From 6a17c70689e9add1ba689948cb396c2565c95df8 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 1 Apr 2023 16:41:16 +0800 Subject: [PATCH 585/612] update dep --- cmd/gocq/login.go | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 1fa4a820b..f5ab95bc5 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -266,7 +266,7 @@ func fetchCaptcha(id string) string { return "" } -func energy(uin uint64, id string, salt []byte) ([]byte, error) { +func energy(uin uint64, id string, appVersion string, salt []byte) ([]byte, error) { // temporary solution signServer := "https://captcha.go-cqhttp.org/sdk/dandelion/energy" if base.SignServerOverwrite != "" { @@ -276,7 +276,7 @@ func energy(uin uint64, id string, salt []byte) ([]byte, error) { Method: http.MethodPost, URL: signServer, Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, - Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&id=%s&salt=%s", uin, id, hex.EncodeToString(salt)))), + Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&id=%s&salt=%s&version=%s", uin, id, hex.EncodeToString(salt), appVersion))), }.Bytes() if err != nil { log.Errorf("获取T544时出现问题: %v", err) diff --git a/go.mod b/go.mod index ed06f019c..4262ef2db 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230319092203-62d60f132203 + github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5 github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index c3e17fbcf..5312818df 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230319092203-62d60f132203 h1:H/bZoEPwRK/TgqqFxtQGY1gO5Vj28/LskhT5wIyzxlo= -github.com/Mrs4s/MiraiGo v0.0.0-20230319092203-62d60f132203/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5 h1:E4fIQ0l/LNZK44NjdViRb/hx4cIeHXyQFPzzkx7cjVE= +github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= From 43c6e3dcf5b85042f6135d357e62b45309c6d1de Mon Sep 17 00:00:00 2001 From: Janet-Baker <36568027+Janet-Baker@users.noreply.github.com> Date: Sat, 1 Apr 2023 21:46:04 +0800 Subject: [PATCH 586/612] fix https://github.com/Mrs4s/go-cqhttp/issues/2036 (#2040) https://pkg.go.dev/os/exec#hdr-Executables_in_the_current_directory --- global/codec.go | 9 +++++++++ modules/silk/codec.go | 3 +++ 2 files changed, 12 insertions(+) diff --git a/global/codec.go b/global/codec.go index 6a5f088cf..0c155a43c 100644 --- a/global/codec.go +++ b/global/codec.go @@ -33,9 +33,15 @@ func EncoderSilk(data []byte) ([]byte, error) { // EncodeMP4 将给定视频文件编码为MP4 func EncodeMP4(src string, dst string) error { // -y 覆盖文件 cmd1 := exec.Command("ffmpeg", "-i", src, "-y", "-c", "copy", "-map", "0", dst) + if errors.Is(cmd1.Err, exec.ErrDot) { + cmd1.Err = nil + } err := cmd1.Run() if err != nil { cmd2 := exec.Command("ffmpeg", "-i", src, "-y", "-c:v", "h264", "-c:a", "mp3", dst) + if errors.Is(cmd2.Err, exec.ErrDot) { + cmd2.Err = nil + } return errors.Wrap(cmd2.Run(), "convert mp4 failed") } return err @@ -44,5 +50,8 @@ func EncodeMP4(src string, dst string) error { // -y 覆盖文件 // ExtractCover 获取给定视频文件的Cover func ExtractCover(src string, target string) error { cmd := exec.Command("ffmpeg", "-i", src, "-y", "-ss", "0", "-frames:v", "1", target) + if errors.Is(cmd.Err, exec.ErrDot) { + cmd.Err = nil + } return errors.Wrap(cmd.Run(), "extract video cover failed") } diff --git a/modules/silk/codec.go b/modules/silk/codec.go index 19118c68e..26f9ca6f4 100644 --- a/modules/silk/codec.go +++ b/modules/silk/codec.go @@ -32,6 +32,9 @@ func encode(record []byte, tempName string) (silkWav []byte, err error) { // 2.转换pcm pcmPath := path.Join(silkCachePath, tempName+".pcm") cmd := exec.Command("ffmpeg", "-i", rawPath, "-f", "s16le", "-ar", "24000", "-ac", "1", pcmPath) + if errors.Is(cmd.Err, exec.ErrDot) { + cmd.Err = nil + } if base.Debug { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr From 6ad62a2642b342fa304c4160b1fa648e0d307955 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 1 Apr 2023 22:02:21 +0800 Subject: [PATCH 587/612] impl: t544 sign algorithm --- cmd/gocq/encryption/config.go | 3 + cmd/gocq/encryption/t544/cpuid_amd64.go | 10 + cmd/gocq/encryption/t544/cpuid_amd64.s | 15 + cmd/gocq/encryption/t544/data.go | 30 + cmd/gocq/encryption/t544/encryption.go | 53 ++ cmd/gocq/encryption/t544/encryption_amd64.s | 637 ++++++++++++++++++ .../encryption/t544/encryption_generic.go | 117 ++++ cmd/gocq/encryption/t544/t544.go | 95 +++ cmd/gocq/encryption/t544/t544_stub.go | 7 + cmd/gocq/encryption/t544/transform.go | 21 + cmd/gocq/login.go | 13 +- 11 files changed, 999 insertions(+), 2 deletions(-) create mode 100644 cmd/gocq/encryption/config.go create mode 100644 cmd/gocq/encryption/t544/cpuid_amd64.go create mode 100644 cmd/gocq/encryption/t544/cpuid_amd64.s create mode 100644 cmd/gocq/encryption/t544/data.go create mode 100644 cmd/gocq/encryption/t544/encryption.go create mode 100644 cmd/gocq/encryption/t544/encryption_amd64.s create mode 100644 cmd/gocq/encryption/t544/encryption_generic.go create mode 100644 cmd/gocq/encryption/t544/t544.go create mode 100644 cmd/gocq/encryption/t544/t544_stub.go create mode 100644 cmd/gocq/encryption/t544/transform.go diff --git a/cmd/gocq/encryption/config.go b/cmd/gocq/encryption/config.go new file mode 100644 index 000000000..9bba4276f --- /dev/null +++ b/cmd/gocq/encryption/config.go @@ -0,0 +1,3 @@ +package encryption + +var T544Signer = map[string]func(int64, []byte) []byte{} diff --git a/cmd/gocq/encryption/t544/cpuid_amd64.go b/cmd/gocq/encryption/t544/cpuid_amd64.go new file mode 100644 index 000000000..a8e21a9cb --- /dev/null +++ b/cmd/gocq/encryption/t544/cpuid_amd64.go @@ -0,0 +1,10 @@ +//go:build amd64 + +package t544 + +func cpuid(op uint32) (eax, ebx, ecx, edx uint32) + +var canusesse2 = func() bool { + _, _, _, d := cpuid(1) + return d&(1<<26) > 0 +}() diff --git a/cmd/gocq/encryption/t544/cpuid_amd64.s b/cmd/gocq/encryption/t544/cpuid_amd64.s new file mode 100644 index 000000000..98f50a23b --- /dev/null +++ b/cmd/gocq/encryption/t544/cpuid_amd64.s @@ -0,0 +1,15 @@ +//go:build amd64 +// +build amd64 + +// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. + +// func cpuid(op uint32) (eax, ebx, ecx, edx uint32) +TEXT ·cpuid(SB), 7, $0 + XORQ CX, CX + MOVL op+0(FP), AX + CPUID + MOVL AX, eax+8(FP) + MOVL BX, ebx+12(FP) + MOVL CX, ecx+16(FP) + MOVL DX, edx+20(FP) + RET diff --git a/cmd/gocq/encryption/t544/data.go b/cmd/gocq/encryption/t544/data.go new file mode 100644 index 000000000..2c0da7bd2 --- /dev/null +++ b/cmd/gocq/encryption/t544/data.go @@ -0,0 +1,30 @@ +//go:build amd64 + +package t544 + +import ( + "archive/zip" + "bytes" + "io" + "unsafe" + + base14 "github.com/fumiama/go-base16384" +) + +var cryptoData = base14.DecodeFromString( + `戒縰幐一丂一一一娠煳樨刀一乀一圀一吷垌脲妘璖蘀丄跾縀一丆橶縐硍笂儸祠婔瓛伄荰且涵箁澜徴唭嵛庋儉犛埫渀皇淛僎侻昰豪楃愨煬尴豀仵矫栐攂縑千瞷劕瞆繍嵜久劒蕨捅臛佤峺蔗毑襱濄葬呟歍癩覥乆萦厰橤刭翖燈伞榠计賎卯嘭测庆蜖予秶浤熦盭偊光紥礌宒梩伂埧禀嚢曉葊杂箼碒播傸类本煻縢萄宴妡藆調矿筌峸螸翣椶儮扴儳贶擣懪皵孽犠嫛縻萍詌奠蛂竀膜蝆茺耈哣絇爃崵蝝滷楷榏篐棔赦僷舆俨剁椦夤垫睩廜胐弑荜藑斛螥呿臬牴竚聈慑蒔猆珟悑譀弭氐桓历哉孔滁勥始浏崜幕仨仞才簦潈纓竔肕浹戯垁捒崒腖坭撅橅婨搅臛孤槟佖濚襹椄梬菀噭癝冾毥蘢嘅旵曫侃懿棘毱蕵烙椁琼傡祤羶椆攨褆枳劭莟蚳蕚巬嘙矲嘬咯彅祫襥幦膯狝楹繦箻潷稛蛿復纉潬攘掜啲滅篇屫悱欧堶矖幁蔝督丝薢聁薊惣橹砖璧碸禱泭賽翲羞俟槗胅費党痼箝玀腅秨唇興溱虝小汁綡它侖叔芴虭墌嚇螖溈熀秓灻媖搫碸綢贷腎嫵兂囟疾貎跧瑘豄殿爥蓆恆榎芤矟訦憄峙坈圪趕熪亙栣柒珯倍塢衑偦楫峾械纙垺爘垜秝謩蒎捤疩汋豽蛢储嗩嵆潲橙蠧市誣楱嬚微茊睙荻肙漦柩荝讞癓禯墕謰墪摏欚笃螼礑皆沵塪艫狁稪索琴夂憤妱堂觛嚹衦竍觠糐翏舭懒莚萻支楰貖许偵呫爔蓐粀艰糬梃枯俏壶謐刂觡枇庿祛妬催歡繩跽洊澎儰覄瞇劾峮穛奤嫆笪孼氁志耡瀀脦壂粲盋宇妕毃嫉娛旍痹吜衦掺臽罦元罧糴淘户惻謵笾囂觔蜐損斢燖莻笠蕟栫其寅祛某簘宭椦觛臃歭脥訍蕱柫絻凯昷翮畗渘球簑忪蛢劂萾娯崎蜃槭斔作曹湲蓣申茮岃撕燩屑譞盼萎裉紦嶰溃簻惆刻簪刵泴恌巰庸筏匸乢誒唛痍徵茽卼帿荛垮浥荝烷桫琯澿涹豘臐跩獌茾藈烧詺豘緗景拾樚滿訄一跿茄稌刔一一渀一一利绰琚乀一丈一傀一侑獣槙呒螉睮匳繑唁丌丬儧脦跛蕇偀掤概蜣裴檮柬欖篠曣葛矊幜晎妌繀胵綔畍偰蟞挚堧賨粚嗩璘匷崍惹幡簱偫泍呸竕尡硱渔豶厾篁擈痘絨茤乯蕞滱簇勃茶絀塐蟖瞅繭唕解誤笉惖肑暙廬罼豚狆咎揊訉妖品沕疗稉狏猊痮觢瑟窇愨椡摓谴祰蒄扫噃盃蜭裣挃崺肖息揁莽焼溒崓荸晩美炏曣聱褎幘珘矐肖滗羶懶帔萉藲璐媹譈哶仜悻衴盈旋穽蠮焛獜粏腨咈猉惼癹竻蘗澃茀氭懺愰灿岷外犅言一跿茄稌刔一一渀一一奚壾脚乀一丈一傀一侕籣槙呒螉睮匴奱攁丈縔冇她赛巭貯恿丞坓玺珏窴萙椓蘈嵌艎悧訇楬砒缎甍菲稻撟摤蘎仛汈奉社忢楉盒曇水碱蜖偧姪繵栤讞恋琗尔沌臔纱懛窃奢筇窴冄榭情憞妡縍磱嬯苜螜惃斓庭繤蕬蝂竨猞腥胒壠纎嵭癞盢抬拱袒笓懚囔幭紒崋詘窄豜爒祽桾礬旬伩觟渺湩穓蒧漞睱亻俸糋华賸帬招狣吼藭圎塘俖佰斯眎緔欇璘撈哷焿涬唖椥暌劧裤蚸担砬氎卼牛禤芗怰條娯啿蒇舆蛂槊睝晽滩譙夌牜一跿茄稌刔一一渀一一塺絏懡偀一亀一僀一俑潢椙揶効灩榓坶氨焱向何盧毿箲贿侁贴劽捝如翕浱杤毴膔謻攴毹菵厽蕴豢矄莕藗瑐苃籷藧斐摗緙藺琒圆奇疃吏儌円祇熁玎籾粆粸说孡羑羙绌婵蟯桚痎喽粦葔歧吽榾涕扖裘橧蓼衒涙虡荟眭矼解窨脸脸堌戌笞蚼簠裏莲薒榘觮杣妌膌膂幅垖摫堫構疜糆琺蟶窩膞椙衟撨旊崾慷擪晁祿宧襼櫗蝾窡眵汳毘覹襟呡譼圫絖蠺章嵻柾瞬崌沌瑎汪祏啜泪嬰氰朜簞尘瑞沬蚼筌葮賒羫粸敳熉埽罾癪覴謳斶夿澭粑肵賫臎姭椬沫濵洈焳焳滨盫覃羙纘繰庴賳縴翗喽瀮唽同蕟禡禎崝浯晳獾槧枅屾澶穷蕴簹疅葴翪蓪壳稝蕬緭乻催贕沩覰俘腤嶩燗讯攦孺唽吞脃夦球敵岒媻崜櫻泓田翗唜賞苧聾試葛惻怍矮皬设抝蛂襹襻恺婻脫裥惫矷焌秳烛籧甍尋弛泌呺膉瓠慁溫脤湋旜請搜蝺裨胵盳廢癫膞姉峫裷控姱琚峬乶娽擋糟柛朌棟脜桋侎泟浩楫寪矷祷狭溏嬯枎唍咂压例澫澣溢艵翇藓捇旞讕婽词婵蔷猹襻熝艪犦苼蕯籸碻灳翽汗蝍篡紌珌文乕僱狎蚣綄掴賟獭罏揼彥嚷熼簹糯洭滌蜺璸蛱菲茦撰諡绑砙当请欯臌珨端戔蓆袘柵政杹烾罓消奟葟柎攼歸紭冊伖燖槫膻眵襴尧澣罀烁脳焳溨苑蟓覫艑矣觓舞毨徑覻堫敾蓊粜苳烾纲愸螾淐森葓薸码焏掋糶豔掇喙见筗詄摰螝趷荧篢禳俘滪昘簙稺爓幷絣奜泺咸裻瓨盫豿佳瓚曧絟噝凟祣厽寫臄爯药碹睜娙甎偻蟖珐诋蓓奟尟続苍憽盭寝咃匃姟浍挝涴氨婜婻萲翗俯枃屘罻搒误曋唜耳汷掹主掝寮矎腽桢许殑呇呧冒瞃仅绝碿穑蟒跆傼撺磎击苴苮受薫專羓僱滈蝱摱櫳奚泷菑菷矬孲譋慛汫澸涏至糷巑粏棹筄矔挗曃襦塸篕纹貘菻翀僓觇跽姷煏卺肔獳脑洿緽垵蘩拡諑盱縲翇嵗眣懧榟棂藧瀸蟫圎毟袮桝浜蕟梾毁厯曅捇僡磡盀琲槒氹廋螰矅螻袚嫸禛絓井篹板慅敜喇疎否薾螰剐孜宫簎痞羧緼徢覸榾抙掯蓛繳碎竧峿桕櫅虨瘘粚咸忲製賓壼膒祹设搝汹撸婤橦樳儑樹樴栽欝橱臩帥跻糆士盫臞羔嵻橅纮葛袥孑臲篷菴婮慹敬倘嫎寪洃玩苹袲簵蜺毬罬磴玴藏泼聅擽琰搐朝檞簏夬溒姌藗棸怘咭簏唗痚纗蟼参屽舃巢况棹嵮豼犊浞萞觥耱苃葠楽徣櫿嗡瞤詶磗紸藕蓣澥牗橮咎傊心儃瞖慎伮讬坳矷择盓濷絫楳氋疷簻詮琗臖藞罢恩蔮毸螕簾儖嘿吞褹攋诧确盱詔撀犴永塘賻烲舨婐岼槲滵扽窖筨熮搌膃乕矣膧薱獏臸榟慅薄觷嶗睴覵唲蕴柡樝拗椛庰籯薿稣潽簥禦煡滌虰想僞荗覛诟吜倆夆奌膌膂盓忧懮桡大粴朚觚芷簏唾莆簽蒍訬狭萚献糮胢攑蒭谼珇甓羵荢蒠蠏泌呢冖篺筌崨畭捜旜豃倏腻坸痓趼嚇愬粔九罹讬硻薲睎嗞训筱褌綇瓮腽誧礕官峄嗣蟽柖贘氮旪懕裺癆慪蟌淯愷虿姦蟮蛢誾伫貇嗹畽趶唽褰滀荵诏媵濯籭煏泺蠚蛫狳竁甬暪檿艑螎紝蕳磮湋赃妺奧菥玽蛖泻肖崴競蚊諬啺机涹徂懗菗痶覢櫎仂元垶敊倬賺炲褺泷茵志蔹焅漦濕旇芏璷懒费椻歡浾葏蛍愍睷螕碢评詫椵讘咇瑇卯卹偍襳睷豟凁织盁牺湸蚓慡蔢璃嵻塘賻磴穥臞薐蟌筡膞榻薝喸瞋浗婋苏緆斎毼瀩嬮磰珥譧淞堃會泌呦熛紅夃秆磬膕獳赿栠蚅膇竁璮蚹讈圶聑苑菇喽焮泶掩蛳薵笯糯俄覉茵讌粵燦殰苿搢糷尵莪夿愠奨岈洱棋溢幾诛愍蝹礖斾号諏志脆柃滢灍羖憅嬆薙媅宜嵻硴話觚肇絻峓田羷蝼肩費菍畛籆刿哬紥疺疎楡婧毰秆瓽憛硘誩嶝簏兆燇之嗟欸蟞叹煖疦覿曃斞專抟汰蘨猜窪粏嫝涭焍泷猕荴衏巶榷肾沚詪蘒笷榑漏羓埣矅蜽脴槫燦僉硋熇兆渝嫝叏猩欵杮咎傈得埋盱牱瑰脌歱翏玩諗埣尝曟喹补狻沥翟煐杺撾媴賕譯瞖籷羒反珼擱斝滯儃姏崙視瓕綇環覍纹赖詗劂奌窰枌瓖攽罹枖岸賺蒲蟩褭臞讑溙签厼憫哽胙褉嫩能柗栝姥姫豓留孰瘘稙篟癣孶蠵痥豉慆燇下兖燏介啜嬚崜儬橳譴捱请宵濏綧瘻嵥槳缓孟尔見粺箶蚊燕秵旘孩綟甩愌婎赑箷汗蝂肬柱幹絿籣棹柔蕻娽琇冂灑胊狡桥犑硣觮楫兜絻咖诏管秎斝败奒瀛蝉攻岓臟囖哱挎篮橁蠱枫赦蝸綼磣罠乢艹蚉胴艵诧硪賾矗璝懖哈栤听啞讬硻滱觙勃觏儑楟櫱屚艶赴藫员慳裙裢腷厔嶣蝴豻綦奻畷甡炗偅仅蛓翕绠皔檲盹倭畻谶趴蕱惥臑滍觘胗姝憥儶啲塨嫻袊胬襾淠攀丿践伬億匀一丠一一丄嶝簠傀帀一伀丂縀丁艡暛呕讈籢桛渀万跾曟啷緉祯罌世檯賗磝窨夥诺摑贊蕒炯甩唬僟讓垍燿洰芥蝼攝滄挄翈許灘厚俄瘎妬疲歂昲繨楮撨匣絚脩蛋虅嵄仭嘿夕箫妾屒狅焿廯碾舴苎卅豀痵仲涨扨興豊欸譯奭的廿諴竐繏穟珑佼悝谽朗得宂住蔈炩倡哮簅寥蘯槠媎溤爘牜纴裆塆揤汹誃殶寕憪擅槓硥沫溋矠献唩祌榣歴嗒觘簩績筙犀巘屡孕覘椄殞虾垁厧枎猦翨涧展堷蛊吤宿螐璄呤笏稕妱昄一跿茄稌刔一一渀一一尊终簊侀一丘一僀一俑潢椙揶媹灩榀一槿蜀一一一倃偂绐蘐吒历激昔椝叄満纐稴専滲芜蜶儂煣皸爎偏脒媨帘怖咇么楁拙旡幹桎檛佡荍卿暆但翑橈咅著譅吜功琤材民毚抵皀纐稴専囎枻蕻灃栊瓊贉煘粷嗲堏奉膓昪巯松畘稺瞢緬庸螯湼誣亣曨簤儢絇熵疍僬秺炌宋樿撾倸圿婈肠蠧譳蘚曰烮芥祃蚇痙瓮爆仭覻蓐琳埂蓕弙炖蒇商杈呪脥噬嗕蛾嬩紖攠糔羨孁獿苦爑墘詔喇媏憫択薢煺獔涘慜瓝摽栏穋獡艗凹蜵矝氄袧揇圔柽燗甔繋纁昔墼箬襢拨稆曕牖籷攒殙掰莟傖潲巧旀蒥谛穒兛厩藅匟榖杏眠彰怸蜰嬭聋袦令揑峱潆捷朷蚎化斄愞贷櫞牼裐衽洐筚犵絾幷堢來什殽瓚萋兿赘堸愙狠毬晬掷祺緢嫅奩枕炳莽卒榌肐嫄尦峉恱苨汃显禁毙奬薥倴蝍蚲蘟繥篒擌惏琵侨囇玚菄蚓膮櫒倥傿瑻弁痉絺凃瑆絓栯嶽宰廩嵯彂畑瞽淕粩涨贰璨珖磿胘晇粳裐洤蒚觧杷疙褎炸法嵬粺橗昳笻秀焼澶襭帼嚾蛤炌簐擫疃屻亻膊昤賆槮呄緶台堄萷摶纨艈業蛄瞗螼羫咥掤娖愢穩癸独碽溪觐嫊矮藸抳碗壳荜舮九擸哒糃寫娣抾台衁祚謞耪彍烐濹炏箫檣肇娷夣嶾帢薬憮恴章爷懐桛炓茍溹熒擏壇嚮桗吠剡岉臤弲禺矿乨趠褦粰檢胮瑸滂擳纈獙擮槊妜榘貏怺啳款絨萹虹謾噋匥棞財覄坟廼溨菴儋貁碕勜繮槬诶爑瑣珼菓翹藻激敇柯涫孓勥認綹蜑猰欎寜斑蒳浆扖惓羾撰簎犯窌庘傶犿巃虛棦瘭兡移墔焃燠嘸昤侌灢嚰樞垒獧佐搪榞牆膺篱唊癎梇挰喯巫擥剂謽禙莡蓌膧搚臩狆牑昌蝳沅塽圡慚圍萩烊褳幽烑塬凑徿洯等溗焴箶姀畜媎贐嫕粒哰朸擹聻爈獌證榠窹俻擾譸羆桙谷癶聑淽罦疸杷賌牝皅巟膠捨猟懪祘礕贞哙嶾捼粋茊繋蛧譀疩埠玷繎弿玍纄赤薚勉喾刾賯蘽芐泋姌络忺梞蚟居袨屒礁觋忏夗矧噎寯啇贵挪磙痜蝰澦栜詶婂卂橱咡簡婷繜幩譹菂氃朥侯意朶度畀呯茁哽扵胃厛歡忧尘曷肴熴聡蔈爾仱痙屗絲螗栰捾瀉筮栁篻垕灌墺蚧菘湞篰畗瘑肃貓揯烘嵒彔築盭硝徬峦檉捊猰猱皴嵉柟產戍筋悒燑樄贖亍徲扌瀺兏讝沺怅嫿涹碨欧牣詮噔禵盼箮蒰磅甌籚主瞨傿楆塷艂譮剫唁裍甮畔腋厐疖斎祶勲奉堭搣搁刦磜舮爽誩樒谠埊絵房罖債葙謆哪寽瓅峑堗羿尶悩薗慌潘睞痝溱擛峨斠统朸砜帓畼讳椅炽苺衧哉欳氕搸凴慈罴茁菌杴予懕媧犲詷夣衈瑄洤滠塚抜誗褴涖諒槈赳捰胼船广繋婀捵翇塷羄瞏匪莄艶藬失肷孿爠蛁艶淰芏櫆胿廢祟姅諁罞榣緱伇峞文蔁俼崯巔倎賿跞積螒笩貑係枹硻罖簒粻襅琨貎菣嗗槨偿漜挭曙喜杏砦瘾扩咣珣帐七跿戒縰幐一丂一一一勗脫噘一丄一一夀一商厉穥旙僦垥籢昹氦涇牣螸呖妝潤蛁湀七跿戒縰幐一丂一一一幗堷幜一丄一一夀一商厉穥旙惦垥糲屝喜貨敁哏蓟礩糥趚纲一仿跔劰娐戀一亀一一丿罨貜簀一净一下一万待灬杗葢螉睮估儑刀帀丰屰缚襅催縩瘠澇傂皞婄庑爔欃僁忑穼唃叅谀一巿譁夁亅丁帀一倀一一丰溍腰瘐一丁一两一一一一一一一一一丘蔦嫌耮暚擥伬伂匀佀一丈一一丁傜崉澠刀一亀一瘀一一一一一一七刐一朙搶讑献暚擥伬伂匀佀一丈一一丂莢緬羠刀一亀一瘀一一一一一一万娔一杛琶讑献暚擥伬伂匀佀一丈一一丂疫苴谐爀一嘀一稀一一一一一一下战一欘搦羕浡妘璖蝁夁亅丁帀一倀一一乃蟛瀀瘄一一帀丬一一一一一一一僼帀丝吖垱獟暋琦玹幋乀潀乐一丂一一一蘫儢蘨吀一习一夀一一一一一一七耑一商厉穥旘胦垥籐惀帡帀戀一亀一一丄菳壈搀一伀一下一一一一一一一严吀万待灬杗葂螉睮戒縐噐且一一渀一一刕肍舗一丁一一僀一一一一一一一朆一俑潢椙揶抹灩榔劰刈戀匀一丠一一丏詚嶧妀一仰一丂縀一一一一一一丩侀丁艡暛呕讘籢桛猄稔吀一一爀圀赀帀凬昀一一㴃`, +) + +var cryptoZip, _ = zip.NewReader(bytes.NewReader(cryptoData), int64(len(cryptoData))) + +func readData[T any](name string) T { + f, err := cryptoZip.Open(name) + if err != nil { + panic(err) + } + data, err := io.ReadAll(f) + if err != nil { + panic(err) + } + return *(*T)(*(*unsafe.Pointer)(unsafe.Pointer(&data))) +} diff --git a/cmd/gocq/encryption/t544/encryption.go b/cmd/gocq/encryption/t544/encryption.go new file mode 100644 index 000000000..75c91d2cb --- /dev/null +++ b/cmd/gocq/encryption/t544/encryption.go @@ -0,0 +1,53 @@ +//go:build amd64 + +package t544 + +import ( + "encoding/binary" + "hash/crc32" + "io" +) + +var crc32Table = func() (tab crc32.Table) { + f, err := cryptoZip.Open("crc32.bin") + if err != nil { + panic(err) + } + data, err := io.ReadAll(f) + if err != nil { + panic(err) + } + for i := range tab { + tab[i] = binary.LittleEndian.Uint32(data[i*4 : (i+1)*4]) + } + return +}() + +func tencentCrc32(tab *crc32.Table, b []byte) uint32 + +func sub_a([]byte, []uint32) + +func sub_b([]byte, []uint32) + +func sub_c(*[16][16]byte, []byte) + +func sub_d(*[16]byte, []byte) + +func sub_e(*[256][6]byte, []byte) + +func sub_f(*[16]byte, *[15]uint32, *[16][16]byte) (w [44]uint32) + +func sub_aa(int, *[16][2][16][16]byte, *[16]byte, []byte) byte + +// transformInner see com/tencent/mobileqq/dt/model/FEBound +func transformInner(*[0x15]byte, *[32][16]byte) + +func initState(*state, []byte, []byte, uint64) + +func (c *state) init(key []byte, data []byte, counter uint64, nr uint8) { + c.nr = nr + c.p = 0 + initState(c, key, data, counter) +} + +func sub_ad([]uint32) diff --git a/cmd/gocq/encryption/t544/encryption_amd64.s b/cmd/gocq/encryption/t544/encryption_amd64.s new file mode 100644 index 000000000..6460c97db --- /dev/null +++ b/cmd/gocq/encryption/t544/encryption_amd64.s @@ -0,0 +1,637 @@ +//go:build amd64 +// +build amd64 + +#include "textflag.h" + +DATA LC0<>+0(SB)/4, $1634760805 +DATA LC0<>+4(SB)/4, $857760878 +DATA LC0<>+8(SB)/4, $2036477234 +DATA LC0<>+12(SB)/4, $1797285236 +GLOBL LC0<>(SB), NOPTR, $16 + +TEXT ·sub_a(SB), NOSPLIT, $0-48 + MOVQ ·a+0(FP), DI + MOVQ ·b+24(FP), CX + MOVQ CX, DX + MOVBLZX 3(CX), CX + XORB CX, (DI) + MOVBLZX 2(DX), CX + XORB CX, 1(DI) + MOVBLZX 1(DX), CX + XORB CX, 2(DI) + MOVBLZX (DX), CX + XORB CX, 3(DI) + MOVBLZX 7(DX), CX + XORB CX, 4(DI) + MOVBLZX 6(DX), CX + XORB CX, 5(DI) + MOVBLZX 5(DX), CX + XORB CX, 6(DI) + MOVBLZX 4(DX), CX + XORB CX, 7(DI) + MOVBLZX 11(DX),CX + XORB CX, 8(DI) + MOVBLZX 10(DX),CX + XORB CX, 9(DI) + MOVBLZX 9(DX), CX + XORB CX,10(DI) + MOVBLZX 8(DX), CX + XORB CX,11(DI) + MOVBLZX 15(DX),CX + XORB CX,12(DI) + MOVBLZX 14(DX),CX + XORB CX,13(DI) + MOVBLZX 13(DX),CX + XORB CX,14(DI) + MOVBLZX 12(DX),DX + XORB DL,15(DI) + RET + +TEXT ·sub_b(SB), NOSPLIT, $0-48 + MOVQ ·a+0(FP), DI + MOVQ ·b+24(FP), CX + MOVQ CX, DX + MOVBLZX 3(CX), CX + XORB CX, (DI) + MOVBLZX 6(DX), CX + XORB CX, 1(DI) + MOVBLZX 9(DX), CX + XORB CX, 2(DI) + MOVBLZX 12(DX),CX + XORB CX, 3(DI) + MOVBLZX 7(DX), CX + XORB CX, 4(DI) + MOVBLZX 10(DX),CX + XORB CX, 5(DI) + MOVBLZX 13(DX),CX + XORB CX, 6(DI) + MOVBLZX (DX), CX + XORB CX,7(DI) + MOVBLZX 11(DX),CX + XORB CX,8(DI) + MOVBLZX 14(DX),CX + XORB CX,9(DI) + MOVBLZX 1(DX), CX + XORB CX,10(DI) + MOVBLZX 4(DX), CX + XORB CX,11(DI) + MOVBLZX 15(DX),CX + XORB CX,12(DI) + MOVBLZX 2(DX), CX + XORB CX,13(DI) + MOVBLZX 5(DX), CX + XORB CX,14(DI) + MOVBLZX 8(DX), DX + XORB DL,15(DI) + RET + + +TEXT ·sub_c(SB), NOSPLIT, $0-32 + MOVQ ·a+0(FP), DI + MOVQ ·b+8(FP), SI + MOVQ SI, AX + MOVBLZX (SI), SI + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 1(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, (AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 2(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 1(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 3(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 2(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 4(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 3(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 5(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 4(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 6(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 5(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 7(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 6(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 8(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 7(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 9(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 8(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 10(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 9(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 11(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 10(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 12(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 11(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 13(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 12(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 14(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 13(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVBLZX 15(AX), SI + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 14(AX) + MOVL SI, CX + ANDL $15, SI + SHRL $4, CX + SHLL $4, CX + ADDL SI, CX + MOVLQSX CX, CX + MOVBLZX (DI)(CX*1), CX + MOVB CX, 15(AX) + RET + +TEXT ·sub_d(SB), NOSPLIT, $16-32 + MOVQ ·t+0(FP), BX + MOVQ ·s+8(FP), SI + MOVOU (SI), X0 + MOVOU X0, in-16(SP) + MOVQ SI, DI + ADDQ $15, DI + MOVB $16, CX + STD +lop: + LEAQ -1(CX), AX + XLAT + MOVBLZX in-16(SP)(AX*1), AX + STOSB + LOOP lop + RET + +TEXT ·sub_e(SB), NOSPLIT, $0-32 + MOVQ ·a+0(FP), DI + MOVQ ·n+8(FP), SI + MOVQ $4, AX +lop: + MOVBQZX -4(SI)(AX*4), DX + MOVBQZX -3(SI)(AX*4), CX + MOVBQZX -2(SI)(AX*4), R10 + MOVBQZX -1(SI)(AX*4), R8 + LEAQ (DX)(DX*2), R9 + LEAQ (R9*2), R9 + LEAQ (CX)(CX*2), R11 + LEAQ (R11*2), R11 + LEAQ (R10)(R10*2), BX + LEAQ (BX*2), BX + MOVB DX, R13 + XORB CX, DX + XORB R10, CX + MOVB (DI)(R9*1), R12 + XORB 1(DI)(R11*1), R12 + XORB R8, R10 + XORB R12, R10 + MOVB R10, -4(SI)(AX*4) + MOVB (DI)(R11*1), R10 + XORB 1(DI)(BX*1), R10 + XORB R8, R13 + XORB R10, R13 + MOVB R13, -3(SI)(AX*4) + MOVB (DI)(BX*1), R10 + LEAQ (R8)(R8*2), R8 + LEAQ (R8*2), R8 + XORB 1(DI)(R8*1), R10 + XORB R10, DX + MOVB DX, -2(SI)(AX*4) + MOVB 1(DI)(R9*1), DX + XORB (DI)(R8*1), DX + XORB DX, CX + MOVB CX, -1(SI)(AX*4) + DECB AX + JNZ lop + RET + +TEXT sub_ab(SB), NOSPLIT, $0-24 + MOVQ ·s+0(FP), DI + MOVQ ·w+8(FP), SI + MOVL SI, AX + MOVL SI, CX + MOVL SI, DX + SHRL $28, AX + SHRL $24, CX + ANDL $15, CX + SALL $4, AX + ADDL CX, AX + MOVBLZX SI, CX + MOVBLZX (DI)(AX*1), AX + MOVBLZX (DI)(CX*1), CX + SALL $24, AX + ORL CX, AX + MOVL SI, CX + SHRL $8, SI + SHRL $8, CX + ANDL $15, SI + ANDL $240, CX + ADDL SI, CX + MOVBLZX (DI)(CX*1), CX + SALL $8, CX + ORL CX, AX + MOVL DX, CX + SHRL $16, DX + SHRL $16, CX + ANDL $15, DX + ANDL $240, CX + ADDL CX, DX + MOVBLZX (DI)(DX*1), DX + SALL $16, DX + ORL DX, AX + MOVQ AX, ·retval+16(FP) + RET + +TEXT ·sub_f(SB), NOSPLIT, $24-68 + MOVQ ·k+0(FP), DI + MOVQ ·r+8(FP), SI + MOVQ ·s+16(FP), DX + MOVQ $·w+24(FP), CX + MOVQ CX, R10 + MOVQ SI, R9 + MOVQ DX, R8 + MOVL $4, BX + MOVL (DI), AX + BSWAPL AX + MOVL AX, (CX) + MOVL 4(DI), AX + BSWAPL AX + MOVL AX, 4(CX) + MOVL 8(DI), AX + BSWAPL AX + MOVL AX, 8(CX) + MOVL 12(DI), AX + BSWAPL AX + MOVL AX, 12(CX) + JMP inner +for: + XORL -16(R10)(BX*4), AX + MOVL AX, (R10)(BX*4) + ADDQ $1, BX + CMPQ BX, $44 + JE end +inner: + MOVL -4(R10)(BX*4), AX + TESTB $3, BX + JNE for + ROLL $8, AX + MOVQ R8, 0(SP) + MOVL AX, 8(SP) + CALL sub_ab(SB) + MOVQ 16(SP), AX + LEAL -1(BX), DX + SARL $2, DX + MOVLQSX DX, DX + XORL (R9)(DX*4), AX + JMP for +end: + RET + +TEXT ·sub_aa(SB), NOSPLIT, $0-56 + MOVQ ·i+0(FP), DI + MOVQ ·t+8(FP), SI + MOVQ ·b+16(FP), DX + MOVQ ·m+24(FP), CX + MOVL DI, AX + MOVLQSX DI, DI + MOVQ SI, R8 + MOVQ DX, SI + MOVBLZX (CX)(DI*1), CX + ANDL $15, AX + MOVBLZX (SI)(AX*1), SI + MOVQ AX, DX + MOVL CX, AX + SALQ $9, DX + ANDL $15, CX + SHRB $4, AX + MOVL SI, DI + ADDQ R8, DX + SALQ $4, CX + ANDL $15, AX + SHRB $4, DI + ANDL $15, SI + SALQ $4, AX + ANDL $15, DI + ADDQ DX, AX + ADDQ CX, DX + MOVBLZX (AX)(DI*1), AX + SALL $4, AX + ORB 256(SI)(DX*1), AX + MOVQ AX, ·retval+48(FP) + RET + +// func transformInner(x *[0x15]byte, tab *[32][16]byte) +TEXT ·transformInner(SB), NOSPLIT, $0-16 + MOVQ ·x+0(FP), DI + MOVQ ·tab+8(FP), SI + MOVQ DI, AX + MOVL $1, CX + MOVQ SI, DI + MOVQ AX, SI +lop: + MOVBLZX (SI), R8 + LEAL -1(CX), AX + ADDQ $1, SI + ANDL $31, AX + MOVL R8, DX + SALL $4, AX + ANDL $15, R8 + SHRB $4, DX + MOVBLZX DX, DX + ADDL DX, AX + CDQE + MOVBLSX (DI)(AX*1), AX + SALL $4, AX + MOVL AX, DX + MOVL CX, AX + ADDL $2, CX + ANDL $31, AX + SALL $4, AX + ADDL R8, AX + CDQE + ORB (DI)(AX*1), DX + MOVB DX, -1(SI) + CMPL CX, $43 + JNE lop + RET + +TEXT ·initState(SB), NOSPLIT, $0-64 + MOVQ ·c+0(FP), DI + MOVQ ·key+8(FP), SI + MOVQ ·data+32(FP), R8 + MOVQ ·counter+56(FP), AX + MOVOA LC0<>(SB), X0 + MOVUPS X0, (DI) + MOVOU (SI), X1 + MOVOU (DI), X3 + MOVUPS X1, 16(DI) + MOVOU 16(SI), X2 + MOVQ AX, 48(DI) + MOVUPS X2, 32(DI) + MOVQ (R8), AX + MOVUPS X3, 64(DI) + MOVQ AX, 56(DI) + MOVQ 48(DI), AX + MOVUPS X1, 80(DI) + MOVUPS X2, 96(DI) + MOVUPS X6,112(DI) + RET + +TEXT ·sub_ad(SB), NOSPLIT, $8-24 + MOVQ ·a+0(FP), DI + MOVQ DI, AX + MOVL 40(DI), R10 + MOVL 12(DI), R12 + MOVL 44(DI), BP + MOVL 16(DI), DX + MOVL (DI), R15 + MOVL 48(DI), R9 + MOVL 20(DI), SI + MOVL 32(DI), R11 + ADDL DX, R15 + MOVL 4(DI), R14 + MOVL 52(DI), R8 + XORL R15, R9 + MOVL 24(DI), CX + MOVL 8(DI), R13 + ROLL $16, R9 + ADDL SI, R14 + MOVL 36(DI), BX + MOVL 56(DI), DI + ADDL R9, R11 + XORL R14, R8 + ADDL CX, R13 + XORL R11, DX + ROLL $16, R8 + XORL R13, DI + ROLL $12, DX + ADDL R8, BX + ROLL $16, DI + ADDL DX, R15 + XORL BX, SI + ADDL DI, R10 + XORL R15, R9 + ROLL $12, SI + XORL R10, CX + ROLL $8, R9 + ADDL SI, R14 + ROLL $12, CX + ADDL R9, R11 + XORL R14, R8 + ADDL CX, R13 + XORL R11, DX + ROLL $8, R8 + XORL R13, DI + ROLL $7, DX + LEAL (BX)(R8*1), BX + ROLL $8, DI + MOVL DX, tmp0-8(SP) + MOVL 28(AX), DX + XORL BX, SI + MOVL BX, tmp1-4(SP) + MOVL R10, BX + MOVL 60(AX), R10 + ROLL $7, SI + ADDL DI, BX + ADDL DX, R12 + ADDL SI, R15 + XORL R12, R10 + XORL BX, CX + ROLL $16, R10 + ROLL $7, CX + ADDL R10, BP + ADDL CX, R14 + XORL BP, DX + XORL R14, R9 + ROLL $12, DX + ROLL $16, R9 + ADDL DX, R12 + XORL R12, R10 + ROLL $8, R10 + ADDL R10, BP + XORL R15, R10 + ROLL $16, R10 + XORL BP, DX + ADDL R9, BP + ADDL R10, BX + ROLL $7, DX + XORL BP, CX + XORL BX, SI + ROLL $12, SI + ADDL SI, R15 + XORL R15, R10 + MOVL R15, (AX) + ROLL $8, R10 + ADDL R10, BX + MOVL R10, 60(AX) + XORL BX, SI + MOVD BX, X1 + ROLL $7, SI + ROLL $12, CX + ADDL DX, R13 + XORL R13, R8 + ADDL CX, R14 + MOVL SI, 20(AX) + ROLL $16, R8 + XORL R14, R9 + MOVL R14, 4(AX) + ADDL R8, R11 + ROLL $8, R9 + XORL R11, DX + ADDL R9, BP + MOVL R9, 48(AX) + ROLL $12, DX + XORL BP, CX + MOVD BP, X2 + ADDL DX, R13 + ROLL $7, CX + PUNPCKLLQ X2, X1 + XORL R13, R8 + MOVL CX, 24(AX) + ROLL $8, R8 + MOVL R13, 8(AX) + ADDL R8, R11 + XORL R11, DX + MOVD R11, X0 + ROLL $7, DX + MOVL DX, 28(AX) + MOVL R8, 52(AX) + MOVL tmp0-8(SP), SI + MOVL tmp1-4(SP), CX + ADDL SI, R12 + XORL R12, DI + ROLL $16, DI + ADDL DI, CX + XORL CX, SI + MOVL SI, DX + ROLL $12, DX + ADDL DX, R12 + XORL R12, DI + MOVL R12, 12(AX) + ROLL $8, DI + ADDL DI, CX + MOVL DI, 56(AX) + MOVD CX, X3 + XORL CX, DX + PUNPCKLLQ X3, X0 + ROLL $7, DX + PUNPCKLQDQ X1, X0 + MOVL DX, 16(AX) + MOVUPS X0, 32(AX) + RET + +// func tencentCrc32(tab *crc32.Table, b []byte) uint32 +TEXT ·tencentCrc32(SB), NOSPLIT, $0-40 + MOVQ ·tab+0(FP), DI + MOVQ ·bptr+8(FP), SI + MOVQ ·bngas+16(FP), DX + TESTQ DX, DX + JE quickend + ADDQ SI, DX + MOVL $-1, AX +lop: + MOVBLZX (SI), CX + ADDQ $1, SI + XORL AX, CX + SHRL $8, AX + MOVBLZX CX, CX + XORL (DI)(CX*4), AX + CMPQ SI, DX + JNE lop + NOTL AX + MOVQ AX, ·bngas+32(FP) + RET +quickend: + XORL AX, AX + RET + diff --git a/cmd/gocq/encryption/t544/encryption_generic.go b/cmd/gocq/encryption/t544/encryption_generic.go new file mode 100644 index 000000000..e74c351e7 --- /dev/null +++ b/cmd/gocq/encryption/t544/encryption_generic.go @@ -0,0 +1,117 @@ +//go:build amd64 + +package t544 + +import ( + "encoding/binary" + "io" +) + +type encryptionData struct { + tableA [16][2][16][16]byte + tableB [16][16]byte + tableC [256][6]byte + tableD [16]byte + tableE [16]byte + tableF [15]uint32 +} + +type state struct { + state [16]uint32 // 16 + orgstate [16]uint32 // 16 + nr uint8 + p uint8 +} + +var crypto = encryptionData{ + tableA: readData[[16][2][16][16]byte]("table_a.bin"), + tableB: readData[[16][16]byte]("table_b.bin"), + tableC: readData[[256][6]byte]("table_c.bin"), + tableD: readData[[16]byte]("table_d.bin"), + tableE: readData[[16]byte]("table_e.bin"), + tableF: func() (tab [15]uint32) { + f, err := cryptoZip.Open("table_f.bin") + if err != nil { + panic(err) + } + data, err := io.ReadAll(f) + if err != nil { + panic(err) + } + for i := range tab { + tab[i] = binary.LittleEndian.Uint32(data[i*4 : (i+1)*4]) + } + return + }(), +} + +func (e *encryptionData) tencentEncryptB(p1 []byte, p2 []uint32) { + const c = 10 + for r := 0; r < 9; r++ { + sub_d(&e.tableD, p1) + sub_b(p1, p2[r*4:(r+1)*4]) + sub_c(&e.tableB, p1) + sub_e(&e.tableC, p1) + } + sub_d(&e.tableD, p1) + sub_b(p1, p2[(c-1)*4:c*4]) + sub_c(&e.tableB, p1) + sub_a(p1, p2[c*4:(c+1)*4]) +} + +func (e *encryptionData) tencentEncryptionB(c []byte, m []byte) (out [0x15]byte) { + var buf [16]byte + w := sub_f(&e.tableE, &e.tableF, &e.tableB) + + for i := range out { + if (i & 0xf) == 0 { + copy(buf[:], c) + e.tencentEncryptB(buf[:], w[:]) + for j := 15; j >= 0; j-- { + c[j]++ + if c[j] != 0 { + break + } + } + } + out[i] = sub_aa(i, &e.tableA, &buf, m) + } + + return +} + +func tencentEncryptionA(input, key, data []byte) { + var s state + s.init(key, data, 0, 20) + s.encrypt(input) +} + +func (c *state) encrypt(data []byte) { + bp := 0 + dataLen := uint32(len(data)) + for dataLen > 0 { + if c.p == 0 { + for i := uint8(0); i < c.nr; i += 2 { + sub_ad(c.state[:]) + } + for i := 0; i < 16; i++ { + c.state[i] += c.orgstate[i] + } + } + var sb [16 * 4]byte + for i, v := range c.state { + binary.LittleEndian.PutUint32(sb[i*4:(i+1)*4], v) + } + for c.p != 64 && dataLen != 0 { + data[bp] ^= sb[c.p] + c.p++ + bp++ + dataLen-- + } + if c.p >= 64 { + c.p = 0 + c.orgstate[12]++ + c.state = c.orgstate + } + } +} diff --git a/cmd/gocq/encryption/t544/t544.go b/cmd/gocq/encryption/t544/t544.go new file mode 100644 index 000000000..53f732e60 --- /dev/null +++ b/cmd/gocq/encryption/t544/t544.go @@ -0,0 +1,95 @@ +//go:build amd64 + +package t544 + +import ( + "crypto/md5" + "crypto/rc4" + "encoding/binary" + "math/rand" + "unsafe" + + "github.com/Mrs4s/go-cqhttp/cmd/gocq/encryption" +) + +const ( + keyTable = "$%&()+,-456789:?ABCDEEFGHIJabcdefghijkopqrstuvwxyz" + table2 = "!#$%&)+.0123456789:=>?@ABCDEFGKMNabcdefghijkopqrst" +) + +var ( + magic = uint64(0x6EEDCF0DC4675540) + key1 = [8]byte{'a', '$', '(', 'e', 'T', '7', '*', '@'} + key2 = [8]byte{'&', 'O', '9', '!', '>', '6', 'X', ')'} +) + +func init() { + if canusesse2 { + encryption.T544Signer["8.9.35.10440"] = sign + } +} + +// sign t544 v8.9.35.10440 +// special thanks to the anonymous contributor who provided the algorithm +func sign(curr int64, input []byte) []byte { + curr %= 1000000 + input = append(input, []byte{byte(curr >> 24), byte(curr >> 16), byte(curr >> 8), byte(curr)}...) + var kt [4 + 32 + 4]byte + r := rand.New(rand.NewSource(curr)) + for i := 0; i < 2; i++ { + kt[i] = keyTable[r.Int()%0x32] + 50 + } + kt[2] = kt[1] + 20 + kt[3] = kt[2] + 20 + key3 := kt[4 : 4+10] + k3calc := key3[2:10] + copy(k3calc, key1[:4]) + for i := 0; i < 4; i++ { + k3calc[4+i] = key2[i] ^ kt[i] + } + key3[0], key3[1] = k3calc[6], k3calc[7] + key3 = key3[:8] + k3calc[6], k3calc[7] = 0, 0 + rc4Cipher, _ := rc4.NewCipher(key3) + rc4Cipher.XORKeyStream(key3, key3) + var crcData [0x15]byte + copy(crcData[4:4+8], (*[8]byte)(unsafe.Pointer(&magic))[:]) + tencentEncryptionA(input, kt[4:4+32], crcData[4:4+8]) + result := md5.Sum(input) + crcData[2] = 1 + crcData[4] = 1 + copy(crcData[5:9], kt[:4]) + crcData[9] = byte(curr >> 24) + crcData[10] = byte(curr >> 16) + crcData[11] = byte(curr >> 8) + crcData[12] = byte(curr) + copy(crcData[13:], result[:8]) + calcCrc := tencentCrc32(&crc32Table, crcData[2:]) + copy(kt[4+32:4+32+4], (*[4]byte)(unsafe.Pointer(&calcCrc))[:]) + crcData[0] = kt[4+32] + crcData[1] = kt[4+32+3] + nonce := uint32(r.Int() ^ r.Int() ^ r.Int()) + on := kt[:16] + binary.BigEndian.PutUint32(on[:4], nonce) + copy(on[4:8], on[:4]) + copy(on[8:16], on[:8]) + ts.transformEncode(&crcData) + encryptedData := crypto.tencentEncryptionB(on, crcData[:]) + ts.transformDecode(&encryptedData) + output := kt[:39] + output[0] = 0x0C + output[1] = 0x05 + binary.BigEndian.PutUint32(output[2:6], nonce) + copy(output[6:27], encryptedData[:]) + binary.LittleEndian.PutUint32(output[27:31], 0) + output[31] = table2[r.Int()%0x32] + output[32] = table2[r.Int()%0x32] + addition := r.Int() % 9 + for addition&1 == 0 { + addition = r.Int() % 9 + } + output[33] = output[31] + byte(addition) + output[34] = output[32] + byte(9-addition) + 1 + binary.LittleEndian.PutUint32(output[35:39], 0) + return output +} diff --git a/cmd/gocq/encryption/t544/t544_stub.go b/cmd/gocq/encryption/t544/t544_stub.go new file mode 100644 index 000000000..361f7b4b1 --- /dev/null +++ b/cmd/gocq/encryption/t544/t544_stub.go @@ -0,0 +1,7 @@ +//go:build !amd64 + +package t544 + +func init() { + +} diff --git a/cmd/gocq/encryption/t544/transform.go b/cmd/gocq/encryption/t544/transform.go new file mode 100644 index 000000000..f8bf1b080 --- /dev/null +++ b/cmd/gocq/encryption/t544/transform.go @@ -0,0 +1,21 @@ +//go:build amd64 + +package t544 + +type transformer struct { + encode [32][16]byte + decode [32][16]byte +} + +func (ts *transformer) transformEncode(bArr *[0x15]byte) { + transformInner(bArr, &ts.encode) +} + +func (ts *transformer) transformDecode(bArr *[0x15]byte) { + transformInner(bArr, &ts.decode) +} + +var ts = transformer{ + encode: readData[[32][16]byte]("encode.bin"), + decode: readData[[32][16]byte]("decode.bin"), +} diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index f5ab95bc5..97a96ef24 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -15,6 +15,8 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/wrapper" + "github.com/Mrs4s/go-cqhttp/cmd/gocq/encryption" + _ "github.com/Mrs4s/go-cqhttp/cmd/gocq/encryption/t544" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -267,7 +269,13 @@ func fetchCaptcha(id string) string { } func energy(uin uint64, id string, appVersion string, salt []byte) ([]byte, error) { - // temporary solution + if localSigner, ok := encryption.T544Signer[appVersion]; ok { + log.Debugf("use local T544Signer v%s", appVersion) + result := localSigner(time.Now().UnixMicro(), salt) + log.Debugf("t544 sign result: %x", result) + return result, nil + } + log.Debugf("fallback to remote T544Signer v%s", appVersion) signServer := "https://captcha.go-cqhttp.org/sdk/dandelion/energy" if base.SignServerOverwrite != "" { signServer = base.SignServerOverwrite @@ -283,9 +291,10 @@ func energy(uin uint64, id string, appVersion string, salt []byte) ([]byte, erro return nil, err } sign, err := hex.DecodeString(gjson.GetBytes(response, "result").String()) - if err != nil { + if err != nil || len(sign) == 0 { log.Errorf("获取T544时出现问题: %v", err) return nil, err } + log.Debugf("t544 sign result: %x", sign) return sign, nil } From c486c254d8cc23046d1bd40288dad3c5818570b6 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 1 Apr 2023 22:29:20 +0800 Subject: [PATCH 588/612] rf: move gocq/encryption -> internal/encryption --- cmd/gocq/login.go | 4 ++-- {cmd/gocq => internal}/encryption/config.go | 0 {cmd/gocq => internal}/encryption/t544/cpuid_amd64.go | 0 {cmd/gocq => internal}/encryption/t544/cpuid_amd64.s | 0 {cmd/gocq => internal}/encryption/t544/data.go | 0 {cmd/gocq => internal}/encryption/t544/encryption.go | 0 {cmd/gocq => internal}/encryption/t544/encryption_amd64.s | 0 {cmd/gocq => internal}/encryption/t544/encryption_generic.go | 0 {cmd/gocq => internal}/encryption/t544/t544.go | 2 +- {cmd/gocq => internal}/encryption/t544/t544_stub.go | 0 {cmd/gocq => internal}/encryption/t544/transform.go | 0 11 files changed, 3 insertions(+), 3 deletions(-) rename {cmd/gocq => internal}/encryption/config.go (100%) rename {cmd/gocq => internal}/encryption/t544/cpuid_amd64.go (100%) rename {cmd/gocq => internal}/encryption/t544/cpuid_amd64.s (100%) rename {cmd/gocq => internal}/encryption/t544/data.go (100%) rename {cmd/gocq => internal}/encryption/t544/encryption.go (100%) rename {cmd/gocq => internal}/encryption/t544/encryption_amd64.s (100%) rename {cmd/gocq => internal}/encryption/t544/encryption_generic.go (100%) rename {cmd/gocq => internal}/encryption/t544/t544.go (98%) rename {cmd/gocq => internal}/encryption/t544/t544_stub.go (100%) rename {cmd/gocq => internal}/encryption/t544/transform.go (100%) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 97a96ef24..9c0b826b4 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -15,8 +15,8 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/wrapper" - "github.com/Mrs4s/go-cqhttp/cmd/gocq/encryption" - _ "github.com/Mrs4s/go-cqhttp/cmd/gocq/encryption/t544" + "github.com/Mrs4s/go-cqhttp/internal/encryption" + _ "github.com/Mrs4s/go-cqhttp/internal/encryption/t544" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" diff --git a/cmd/gocq/encryption/config.go b/internal/encryption/config.go similarity index 100% rename from cmd/gocq/encryption/config.go rename to internal/encryption/config.go diff --git a/cmd/gocq/encryption/t544/cpuid_amd64.go b/internal/encryption/t544/cpuid_amd64.go similarity index 100% rename from cmd/gocq/encryption/t544/cpuid_amd64.go rename to internal/encryption/t544/cpuid_amd64.go diff --git a/cmd/gocq/encryption/t544/cpuid_amd64.s b/internal/encryption/t544/cpuid_amd64.s similarity index 100% rename from cmd/gocq/encryption/t544/cpuid_amd64.s rename to internal/encryption/t544/cpuid_amd64.s diff --git a/cmd/gocq/encryption/t544/data.go b/internal/encryption/t544/data.go similarity index 100% rename from cmd/gocq/encryption/t544/data.go rename to internal/encryption/t544/data.go diff --git a/cmd/gocq/encryption/t544/encryption.go b/internal/encryption/t544/encryption.go similarity index 100% rename from cmd/gocq/encryption/t544/encryption.go rename to internal/encryption/t544/encryption.go diff --git a/cmd/gocq/encryption/t544/encryption_amd64.s b/internal/encryption/t544/encryption_amd64.s similarity index 100% rename from cmd/gocq/encryption/t544/encryption_amd64.s rename to internal/encryption/t544/encryption_amd64.s diff --git a/cmd/gocq/encryption/t544/encryption_generic.go b/internal/encryption/t544/encryption_generic.go similarity index 100% rename from cmd/gocq/encryption/t544/encryption_generic.go rename to internal/encryption/t544/encryption_generic.go diff --git a/cmd/gocq/encryption/t544/t544.go b/internal/encryption/t544/t544.go similarity index 98% rename from cmd/gocq/encryption/t544/t544.go rename to internal/encryption/t544/t544.go index 53f732e60..0e04c3dd7 100644 --- a/cmd/gocq/encryption/t544/t544.go +++ b/internal/encryption/t544/t544.go @@ -9,7 +9,7 @@ import ( "math/rand" "unsafe" - "github.com/Mrs4s/go-cqhttp/cmd/gocq/encryption" + "github.com/Mrs4s/go-cqhttp/internal/encryption" ) const ( diff --git a/cmd/gocq/encryption/t544/t544_stub.go b/internal/encryption/t544/t544_stub.go similarity index 100% rename from cmd/gocq/encryption/t544/t544_stub.go rename to internal/encryption/t544/t544_stub.go diff --git a/cmd/gocq/encryption/t544/transform.go b/internal/encryption/t544/transform.go similarity index 100% rename from cmd/gocq/encryption/t544/transform.go rename to internal/encryption/t544/transform.go From 268ac07271c3be608b1187828ff4c67dc8a5c990 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 1 Apr 2023 22:31:57 +0800 Subject: [PATCH 589/612] ci: make lint happy --- .golangci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.golangci.yml b/.golangci.yml index 5f654d69f..d4374df26 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -56,6 +56,7 @@ run: skip-dirs: - db - cmd/api-generator + - internal/encryption tests: true # output configuration options From 1ab1cba84c4307c178c80b81c8b57d3a1d06ea9b Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 2 Apr 2023 18:04:13 +0800 Subject: [PATCH 590/612] rf: change protocol auto-update to manual update --- cmd/gocq/main.go | 37 ++++++++++++++++++------------- internal/base/flag.go | 2 ++ modules/config/config.go | 15 ++++++------- modules/config/default_config.yml | 2 -- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index b0e2e678a..efd6479f0 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -18,6 +18,7 @@ import ( rotatelogs "github.com/lestrrat-go/file-rotatelogs" "github.com/pkg/errors" log "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" "golang.org/x/crypto/pbkdf2" "golang.org/x/term" @@ -281,24 +282,30 @@ func LoginInteract() { cli.Uin = base.Account.Uin cli.PasswordMd5 = base.PasswordHash } - if !isTokenLogin { - if !base.Account.DisableProtocolUpdate { - log.Infof("正在检查协议更新...") - oldVersionName := device.Protocol.Version().String() - remoteVersion, err := getRemoteLatestProtocolVersion(int(device.Protocol.Version().Protocol)) - if err == nil { - if err = device.Protocol.Version().UpdateFromJson(remoteVersion); err == nil { - if device.Protocol.Version().String() != oldVersionName { - log.Infof("已自动更新协议版本: %s -> %s", oldVersionName, device.Protocol.Version().String()) - } else { - log.Infof("协议已经是最新版本") - } - _ = os.WriteFile(versionFile, remoteVersion, 0o644) + if !base.FastStart { + log.Infof("正在检查协议更新...") + currentVersionName := device.Protocol.Version().SortVersionName + remoteVersion, err := getRemoteLatestProtocolVersion(int(device.Protocol.Version().Protocol)) + if err == nil { + remoteVersionName := gjson.GetBytes(remoteVersion, "sort_version_name").String() + if remoteVersionName != currentVersionName { + switch { + case !base.UpdateProtocol: + log.Infof("检测到协议更新: %s -> %s", currentVersionName, remoteVersionName) + log.Infof("如果登录时出现版本过低错误, 可尝试使用 -update-protocol 参数启动") + case !isTokenLogin: + _ = device.Protocol.Version().UpdateFromJson(remoteVersion) + log.Infof("协议版本已更新: %s -> %s", currentVersionName, remoteVersionName) + default: + log.Infof("检测到协议更新: %s -> %s", currentVersionName, remoteVersionName) + log.Infof("由于使用了会话缓存, 无法自动更新协议, 请删除缓存后重试") } - } else if err.Error() != "remote version unavailable" { - log.Warnf("检查协议更新失败: %v", err) } + } else if err.Error() != "remote version unavailable" { + log.Warnf("检查协议更新失败: %v", err) } + } + if !isTokenLogin { if !isQRCodeLogin { if err := commonLogin(); err != nil { log.Fatalf("登录时发生致命错误: %v", err) diff --git a/internal/base/flag.go b/internal/base/flag.go index 5e546b5f8..44e018cb9 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -37,6 +37,7 @@ var ( LogColorful bool // 是否启用日志颜色 FastStart bool // 是否为快速启动 AllowTempSession bool // 是否允许发送临时会话信息 + UpdateProtocol bool // 是否更新协议 SignServerOverwrite string // 使用特定的服务器进行签名 PostFormat string // 上报格式 string or array @@ -61,6 +62,7 @@ func Parse() { flag.StringVar(&LittleWD, "w", "", "cover the working directory") d := flag.Bool("D", false, "debug mode") flag.BoolVar(&FastStart, "faststart", false, "skip waiting 5 seconds") + flag.BoolVar(&UpdateProtocol, "update-protocol", false, "update protocol") flag.StringVar(&SignServerOverwrite, "sign-server", "", "use special server to sign tlv") flag.Parse() diff --git a/modules/config/config.go b/modules/config/config.go index 53e2abf29..22d9879dc 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -28,14 +28,13 @@ type Reconnect struct { // Account 账号配置 type Account struct { - Uin int64 `yaml:"uin"` - Password string `yaml:"password"` - Encrypt bool `yaml:"encrypt"` - Status int `yaml:"status"` - ReLogin *Reconnect `yaml:"relogin"` - UseSSOAddress bool `yaml:"use-sso-address"` - AllowTempSession bool `yaml:"allow-temp-session"` - DisableProtocolUpdate bool `yaml:"disable-protocol-update"` + Uin int64 `yaml:"uin"` + Password string `yaml:"password"` + Encrypt bool `yaml:"encrypt"` + Status int `yaml:"status"` + ReLogin *Reconnect `yaml:"relogin"` + UseSSOAddress bool `yaml:"use-sso-address"` + AllowTempSession bool `yaml:"allow-temp-session"` } // Config 总配置文件 diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 56d51f7bf..9e4acd8ea 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -15,8 +15,6 @@ account: # 账号相关 use-sso-address: true # 是否允许发送临时会话消息 allow-temp-session: false - # 是否禁用协议更新 - disable-protocol-update: false heartbeat: # 心跳频率, 单位秒 From 233e276d6a1ed9d9cad666b5b6bb94347db50f84 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 2 Apr 2023 18:06:25 +0800 Subject: [PATCH 591/612] feat: t544 support 8.9.38.10545 --- internal/encryption/t544/t544.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/encryption/t544/t544.go b/internal/encryption/t544/t544.go index 0e04c3dd7..dd025cee5 100644 --- a/internal/encryption/t544/t544.go +++ b/internal/encryption/t544/t544.go @@ -26,6 +26,7 @@ var ( func init() { if canusesse2 { encryption.T544Signer["8.9.35.10440"] = sign + encryption.T544Signer["8.9.38.10545"] = sign } } From 0f0e71111145350717105048ce38b3fccbc11892 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 2 Apr 2023 18:09:37 +0800 Subject: [PATCH 592/612] fix comment --- internal/encryption/t544/t544.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/encryption/t544/t544.go b/internal/encryption/t544/t544.go index dd025cee5..9cb99f1b9 100644 --- a/internal/encryption/t544/t544.go +++ b/internal/encryption/t544/t544.go @@ -30,7 +30,7 @@ func init() { } } -// sign t544 v8.9.35.10440 +// sign t544 algorithm // special thanks to the anonymous contributor who provided the algorithm func sign(curr int64, input []byte) []byte { curr %= 1000000 @@ -60,10 +60,7 @@ func sign(curr int64, input []byte) []byte { crcData[2] = 1 crcData[4] = 1 copy(crcData[5:9], kt[:4]) - crcData[9] = byte(curr >> 24) - crcData[10] = byte(curr >> 16) - crcData[11] = byte(curr >> 8) - crcData[12] = byte(curr) + binary.BigEndian.PutUint32(crcData[9:13], uint32(curr)) copy(crcData[13:], result[:8]) calcCrc := tencentCrc32(&crc32Table, crcData[2:]) copy(kt[4+32:4+32+4], (*[4]byte)(unsafe.Pointer(&calcCrc))[:]) From 749cde2a6dc2ebed6d4f2bb5517763752e48f44c Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sun, 2 Apr 2023 18:32:49 +0800 Subject: [PATCH 593/612] fix #1782 --- coolq/event.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/coolq/event.go b/coolq/event.go index e54a40e45..5fa4b8567 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -475,6 +475,9 @@ func (bot *CQBot) offlineFileEvent(c *client.QQClient, e *client.OfflineFileEven } func (bot *CQBot) joinGroupEvent(c *client.QQClient, group *client.GroupInfo) { + if group == nil { + return + } log.Infof("Bot进入了群 %v.", formatGroupName(group)) bot.dispatch(bot.groupIncrease(group.Code, 0, c.Uin)) } From 1e42b2c45072bffef14b30aa6919b45d42d8c55e Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 3 Apr 2023 20:26:43 +0800 Subject: [PATCH 594/612] feat: login error message --- cmd/gocq/login.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 9c0b826b4..9e904ce7b 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -218,6 +218,15 @@ func loginResponseProcessor(res *client.LoginResponse) error { case client.OtherLoginError, client.UnknownLoginError, client.TooManySMSRequestError: msg := res.ErrorMessage log.Warnf("登录失败: %v Code: %v", msg, res.Code) + switch res.Code { + case 235: + log.Warnf("设备信息被封禁, 请删除 device.json 后重试.") + case 237: + log.Warnf("登录过于频繁, 请在手机QQ登录并根据提示完成认证后等一段时间重试") + case 45: // 在提供 t544 后还是出现45错误是需要强行升级到最新客户端或被限制非常用设备 + log.Warnf("你的账号涉嫌违规被限制在非常用设备登录, 请在手机QQ登录并根据提示完成认证") + log.Warnf("或使用 -update-protocol 升级到最新协议后重试") + } if res.Code == 235 { log.Warnf("请删除 device.json 后重试.") } From 637d46f282f8ba44d07e07f5d1968dfc3c9a37d4 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Mon, 3 Apr 2023 20:28:25 +0800 Subject: [PATCH 595/612] rf: remove useless code --- cmd/gocq/login.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 9e904ce7b..a8c801ba9 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -227,9 +227,6 @@ func loginResponseProcessor(res *client.LoginResponse) error { log.Warnf("你的账号涉嫌违规被限制在非常用设备登录, 请在手机QQ登录并根据提示完成认证") log.Warnf("或使用 -update-protocol 升级到最新协议后重试") } - if res.Code == 235 { - log.Warnf("请删除 device.json 后重试.") - } log.Infof("按 Enter 继续....") readLine() os.Exit(0) From 7b2d1fd573ead7e88545d4e2e1681f36617d4fc6 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Wed, 5 Apr 2023 01:35:05 +0800 Subject: [PATCH 596/612] rf: remove sse2 check --- internal/encryption/t544/cpuid_amd64.go | 10 ---------- internal/encryption/t544/cpuid_amd64.s | 15 --------------- internal/encryption/t544/t544.go | 6 ++---- 3 files changed, 2 insertions(+), 29 deletions(-) delete mode 100644 internal/encryption/t544/cpuid_amd64.go delete mode 100644 internal/encryption/t544/cpuid_amd64.s diff --git a/internal/encryption/t544/cpuid_amd64.go b/internal/encryption/t544/cpuid_amd64.go deleted file mode 100644 index a8e21a9cb..000000000 --- a/internal/encryption/t544/cpuid_amd64.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build amd64 - -package t544 - -func cpuid(op uint32) (eax, ebx, ecx, edx uint32) - -var canusesse2 = func() bool { - _, _, _, d := cpuid(1) - return d&(1<<26) > 0 -}() diff --git a/internal/encryption/t544/cpuid_amd64.s b/internal/encryption/t544/cpuid_amd64.s deleted file mode 100644 index 98f50a23b..000000000 --- a/internal/encryption/t544/cpuid_amd64.s +++ /dev/null @@ -1,15 +0,0 @@ -//go:build amd64 -// +build amd64 - -// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. - -// func cpuid(op uint32) (eax, ebx, ecx, edx uint32) -TEXT ·cpuid(SB), 7, $0 - XORQ CX, CX - MOVL op+0(FP), AX - CPUID - MOVL AX, eax+8(FP) - MOVL BX, ebx+12(FP) - MOVL CX, ecx+16(FP) - MOVL DX, edx+20(FP) - RET diff --git a/internal/encryption/t544/t544.go b/internal/encryption/t544/t544.go index 9cb99f1b9..321fcb1b9 100644 --- a/internal/encryption/t544/t544.go +++ b/internal/encryption/t544/t544.go @@ -24,10 +24,8 @@ var ( ) func init() { - if canusesse2 { - encryption.T544Signer["8.9.35.10440"] = sign - encryption.T544Signer["8.9.38.10545"] = sign - } + encryption.T544Signer["8.9.35.10440"] = sign + encryption.T544Signer["8.9.38.10545"] = sign } // sign t544 algorithm From 13325634c04f06745589c3d552ea1077b5219f37 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 8 Apr 2023 11:34:19 +0800 Subject: [PATCH 597/612] make lint happy --- cmd/gocq/login.go | 7 ++-- cmd/gocq/main.go | 78 ++++++++++++++++++-------------------- coolq/api_v12.go | 2 +- coolq/event.go | 16 ++++---- internal/msg/parse_test.go | 2 +- 5 files changed, 50 insertions(+), 55 deletions(-) diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index a8c801ba9..404cf895a 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -15,18 +15,17 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/wrapper" - "github.com/Mrs4s/go-cqhttp/internal/encryption" - _ "github.com/Mrs4s/go-cqhttp/internal/encryption/t544" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "gopkg.ilharper.com/x/isatty" - "github.com/Mrs4s/go-cqhttp/internal/base" - "github.com/Mrs4s/go-cqhttp/global" + "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/download" + "github.com/Mrs4s/go-cqhttp/internal/encryption" + _ "github.com/Mrs4s/go-cqhttp/internal/encryption/t544" // side effect ) var console = bufio.NewReader(os.Stdin) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index efd6479f0..4463fbfdd 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -22,14 +22,13 @@ import ( "golang.org/x/crypto/pbkdf2" "golang.org/x/term" - "github.com/Mrs4s/go-cqhttp/internal/download" - "github.com/Mrs4s/go-cqhttp/coolq" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/global/terminal" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" + "github.com/Mrs4s/go-cqhttp/internal/download" "github.com/Mrs4s/go-cqhttp/internal/selfdiagnosis" "github.com/Mrs4s/go-cqhttp/internal/selfupdate" "github.com/Mrs4s/go-cqhttp/modules/servers" @@ -168,51 +167,48 @@ func LoginInteract() { if !global.PathExists("password.encrypt") { if base.Account.Password == "" { log.Error("无法进行加密,请在配置文件中的添加密码后重新启动.") - readLine() - os.Exit(0) + } else { + log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)") + byteKey, _ = term.ReadPassword(int(os.Stdin.Fd())) + base.PasswordHash = md5.Sum([]byte(base.Account.Password)) + _ = os.WriteFile("password.encrypt", []byte(PasswordHashEncrypt(base.PasswordHash[:], byteKey)), 0o644) + log.Info("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.") } - log.Infof("密码加密已启用, 请输入Key对密码进行加密: (Enter 提交)") - byteKey, _ = term.ReadPassword(int(os.Stdin.Fd())) - base.PasswordHash = md5.Sum([]byte(base.Account.Password)) - _ = os.WriteFile("password.encrypt", []byte(PasswordHashEncrypt(base.PasswordHash[:], byteKey)), 0o644) - log.Info("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.") readLine() os.Exit(0) + } + if base.Account.Password != "" { + log.Error("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.") + readLine() + os.Exit(0) + } + if len(byteKey) == 0 { + log.Infof("密码加密已启用, 请输入Key对密码进行解密以继续: (Enter 提交)") + cancel := make(chan struct{}, 1) + state, _ := term.GetState(int(os.Stdin.Fd())) + go func() { + select { + case <-cancel: + return + case <-time.After(time.Second * 45): + log.Infof("解密key输入超时") + time.Sleep(3 * time.Second) + _ = term.Restore(int(os.Stdin.Fd()), state) + os.Exit(0) + } + }() + byteKey, _ = term.ReadPassword(int(os.Stdin.Fd())) + cancel <- struct{}{} } else { - if base.Account.Password != "" { - log.Error("密码已加密,为了您的账号安全,请删除配置文件中的密码后重新启动.") - readLine() - os.Exit(0) - } - - if len(byteKey) == 0 { - log.Infof("密码加密已启用, 请输入Key对密码进行解密以继续: (Enter 提交)") - cancel := make(chan struct{}, 1) - state, _ := term.GetState(int(os.Stdin.Fd())) - go func() { - select { - case <-cancel: - return - case <-time.After(time.Second * 45): - log.Infof("解密key输入超时") - time.Sleep(3 * time.Second) - _ = term.Restore(int(os.Stdin.Fd()), state) - os.Exit(0) - } - }() - byteKey, _ = term.ReadPassword(int(os.Stdin.Fd())) - cancel <- struct{}{} - } else { - log.Infof("密码加密已启用, 使用运行时传递的参数进行解密,按 Ctrl+C 取消.") - } + log.Infof("密码加密已启用, 使用运行时传递的参数进行解密,按 Ctrl+C 取消.") + } - encrypt, _ := os.ReadFile("password.encrypt") - ph, err := PasswordHashDecrypt(string(encrypt), byteKey) - if err != nil { - log.Fatalf("加密存储的密码损坏,请尝试重新配置密码") - } - copy(base.PasswordHash[:], ph) + encrypt, _ := os.ReadFile("password.encrypt") + ph, err := PasswordHashDecrypt(string(encrypt), byteKey) + if err != nil { + log.Fatalf("加密存储的密码损坏,请尝试重新配置密码") } + copy(base.PasswordHash[:], ph) } else if len(base.Account.Password) > 0 { base.PasswordHash = md5.Sum([]byte(base.Account.Password)) } diff --git a/coolq/api_v12.go b/coolq/api_v12.go index 21c8835e9..cbda8ed51 100644 --- a/coolq/api_v12.go +++ b/coolq/api_v12.go @@ -28,7 +28,7 @@ func (bot *CQBot) CQGetVersion() global.MSG { // // @route12(send_message) // @rename(m->message) -func (bot *CQBot) CQSendMessageV12(groupID, userID, detailType string, m gjson.Result) global.MSG { +func (bot *CQBot) CQSendMessageV12(groupID, userID, detailType string, m gjson.Result) global.MSG { // nolint // TODO: implement return OK(nil) } diff --git a/coolq/event.go b/coolq/event.go index 5fa4b8567..605ea3ec8 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -136,7 +136,7 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) bot.dispatch(gm) } -func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) { +func (bot *CQBot) tempMessageEvent(_ *client.QQClient, e *client.TempMessageEvent) { m := e.Message bot.checkMedia(m.Elements, m.Sender.Uin) source := message.Source{ @@ -491,7 +491,7 @@ func (bot *CQBot) leaveGroupEvent(c *client.QQClient, e *client.GroupLeaveEvent) bot.dispatch(bot.groupDecrease(e.Group.Code, c.Uin, e.Operator)) } -func (bot *CQBot) memberPermissionChangedEvent(c *client.QQClient, e *client.MemberPermissionChangedEvent) { +func (bot *CQBot) memberPermissionChangedEvent(_ *client.QQClient, e *client.MemberPermissionChangedEvent) { st := "unset" if e.NewPermission == client.Administrator { st = "set" @@ -502,7 +502,7 @@ func (bot *CQBot) memberPermissionChangedEvent(c *client.QQClient, e *client.Mem }) } -func (bot *CQBot) memberCardUpdatedEvent(c *client.QQClient, e *client.MemberCardUpdatedEvent) { +func (bot *CQBot) memberCardUpdatedEvent(_ *client.QQClient, e *client.MemberCardUpdatedEvent) { log.Infof("群 %v 的 %v 更新了名片 %v -> %v", formatGroupName(e.Group), formatMemberName(e.Member), e.OldCard, e.Member.CardName) bot.dispatchEvent("notice/group_card", global.MSG{ "group_id": e.Group.Code, @@ -526,7 +526,7 @@ func (bot *CQBot) memberLeaveEvent(_ *client.QQClient, e *client.MemberLeaveGrou bot.dispatch(bot.groupDecrease(e.Group.Code, e.Member.Uin, e.Operator)) } -func (bot *CQBot) friendRequestEvent(c *client.QQClient, e *client.NewFriendRequest) { +func (bot *CQBot) friendRequestEvent(_ *client.QQClient, e *client.NewFriendRequest) { log.Infof("收到来自 %v(%v) 的好友请求: %v", e.RequesterNick, e.RequesterUin, e.Message) flag := strconv.FormatInt(e.RequestId, 10) bot.friendReqCache.Store(flag, e) @@ -537,7 +537,7 @@ func (bot *CQBot) friendRequestEvent(c *client.QQClient, e *client.NewFriendRequ }) } -func (bot *CQBot) friendAddedEvent(c *client.QQClient, e *client.NewFriendEvent) { +func (bot *CQBot) friendAddedEvent(_ *client.QQClient, e *client.NewFriendEvent) { log.Infof("添加了新好友: %v(%v)", e.Friend.Nickname, e.Friend.Uin) bot.tempSessionCache.Delete(e.Friend.Uin) bot.dispatchEvent("notice/friend_add", global.MSG{ @@ -545,7 +545,7 @@ func (bot *CQBot) friendAddedEvent(c *client.QQClient, e *client.NewFriendEvent) }) } -func (bot *CQBot) groupInvitedEvent(c *client.QQClient, e *client.GroupInvitedRequest) { +func (bot *CQBot) groupInvitedEvent(_ *client.QQClient, e *client.GroupInvitedRequest) { log.Infof("收到来自群 %v(%v) 内用户 %v(%v) 的加群邀请.", e.GroupName, e.GroupCode, e.InvitorNick, e.InvitorUin) flag := strconv.FormatInt(e.RequestId, 10) bot.dispatchEvent("request/group/invite", global.MSG{ @@ -557,7 +557,7 @@ func (bot *CQBot) groupInvitedEvent(c *client.QQClient, e *client.GroupInvitedRe }) } -func (bot *CQBot) groupJoinReqEvent(c *client.QQClient, e *client.UserJoinGroupRequest) { +func (bot *CQBot) groupJoinReqEvent(_ *client.QQClient, e *client.UserJoinGroupRequest) { log.Infof("群 %v(%v) 收到来自用户 %v(%v) 的加群请求.", e.GroupName, e.GroupCode, e.RequesterNick, e.RequesterUin) flag := strconv.FormatInt(e.RequestId, 10) bot.dispatchEvent("request/group/add", global.MSG{ @@ -569,7 +569,7 @@ func (bot *CQBot) groupJoinReqEvent(c *client.QQClient, e *client.UserJoinGroupR }) } -func (bot *CQBot) otherClientStatusChangedEvent(c *client.QQClient, e *client.OtherClientStatusChangedEvent) { +func (bot *CQBot) otherClientStatusChangedEvent(_ *client.QQClient, e *client.OtherClientStatusChangedEvent) { if e.Online { log.Infof("Bot 账号在客户端 %v (%v) 登录.", e.Client.DeviceName, e.Client.DeviceKind) } else { diff --git a/internal/msg/parse_test.go b/internal/msg/parse_test.go index 21998c7d1..d11c3b7dd 100644 --- a/internal/msg/parse_test.go +++ b/internal/msg/parse_test.go @@ -10,7 +10,7 @@ import ( "github.com/tidwall/gjson" ) -func TestParseString(t *testing.T) { +func TestParseString(_ *testing.T) { // TODO: add more text for _, v := range ParseString(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`) { fmt.Println(v) From 8acc9f39c2374862539623d3c646e5bf4f5de3d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sat, 8 Apr 2023 12:05:45 +0800 Subject: [PATCH 598/612] =?UTF-8?q?fix:=20=E5=B0=9D=E8=AF=95=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20#2070=20(#2071)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/encryption/t544/encryption_amd64.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/encryption/t544/encryption_amd64.s b/internal/encryption/t544/encryption_amd64.s index 6460c97db..5ad7b982d 100644 --- a/internal/encryption/t544/encryption_amd64.s +++ b/internal/encryption/t544/encryption_amd64.s @@ -244,11 +244,11 @@ TEXT ·sub_d(SB), NOSPLIT, $16-32 MOVQ SI, DI ADDQ $15, DI MOVB $16, CX - STD lop: LEAQ -1(CX), AX XLAT MOVBLZX in-16(SP)(AX*1), AX + STD STOSB LOOP lop RET From 54995fc1018b1659863a445c78db8dcb40961220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sat, 8 Apr 2023 12:05:45 +0800 Subject: [PATCH 599/612] =?UTF-8?q?fix:=20=E5=B0=9D=E8=AF=95=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=20#2070=20(#2071)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/encryption/t544/encryption_amd64.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/encryption/t544/encryption_amd64.s b/internal/encryption/t544/encryption_amd64.s index 6460c97db..5ad7b982d 100644 --- a/internal/encryption/t544/encryption_amd64.s +++ b/internal/encryption/t544/encryption_amd64.s @@ -244,11 +244,11 @@ TEXT ·sub_d(SB), NOSPLIT, $16-32 MOVQ SI, DI ADDQ $15, DI MOVB $16, CX - STD lop: LEAQ -1(CX), AX XLAT MOVBLZX in-16(SP)(AX*1), AX + STD STOSB LOOP lop RET From 55cb80dccce05885f1242fd4379a8c9cd35eeb7a Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 8 Apr 2023 12:09:58 +0800 Subject: [PATCH 600/612] onebot: pick Attr, Value from log/slog [wip] --- pkg/onebot/attr.go | 78 +++++++++ pkg/onebot/kind_string.go | 31 ++++ pkg/onebot/value.go | 354 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 463 insertions(+) create mode 100644 pkg/onebot/attr.go create mode 100644 pkg/onebot/kind_string.go create mode 100644 pkg/onebot/value.go diff --git a/pkg/onebot/attr.go b/pkg/onebot/attr.go new file mode 100644 index 000000000..485fa2fb8 --- /dev/null +++ b/pkg/onebot/attr.go @@ -0,0 +1,78 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package onebot + +import ( + "time" +) + +// An Attr is a key-value pair. +type Attr struct { + Key string + Value Value +} + +// String returns an Attr for a string value. +func String(key, value string) Attr { + return Attr{key, StringValue(value)} +} + +// Int64 returns an Attr for an int64. +func Int64(key string, value int64) Attr { + return Attr{key, Int64Value(value)} +} + +// Int converts an int to an int64 and returns +// an Attr with that value. +func Int(key string, value int) Attr { + return Int64(key, int64(value)) +} + +// Uint64 returns an Attr for a uint64. +func Uint64(key string, v uint64) Attr { + return Attr{key, Uint64Value(v)} +} + +// Float64 returns an Attr for a floating-point number. +func Float64(key string, v float64) Attr { + return Attr{key, Float64Value(v)} +} + +// Bool returns an Attr for a bool. +func Bool(key string, v bool) Attr { + return Attr{key, BoolValue(v)} +} + +// Time returns an Attr for a time.Time. +// It discards the monotonic portion. +func Time(key string, v time.Time) Attr { + return Attr{key, TimeValue(v)} +} + +// Duration returns an Attr for a time.Duration. +func Duration(key string, v time.Duration) Attr { + return Attr{key, DurationValue(v)} +} + +// Group returns an Attr for a Group Value. +// The caller must not subsequently mutate the +// argument slice. +// +// Use Group to collect several Attrs under a single +// key on a log line, or as the result of LogValue +// in order to log a single value as multiple Attrs. +func Group(key string, as ...Attr) Attr { + return Attr{key, GroupValue(as...)} +} + +// Any returns an Attr for the supplied value. +// See [Value.AnyValue] for how values are treated. +func Any(key string, value any) Attr { + return Attr{key, AnyValue(value)} +} + +func (a Attr) String() string { + return a.Key + "=" + a.Value.String() +} diff --git a/pkg/onebot/kind_string.go b/pkg/onebot/kind_string.go new file mode 100644 index 000000000..7e04ad41b --- /dev/null +++ b/pkg/onebot/kind_string.go @@ -0,0 +1,31 @@ +// Code generated by "stringer -type=Kind -trimprefix=Kind"; DO NOT EDIT. + +package onebot + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[KindAny-0] + _ = x[KindBool-1] + _ = x[KindDuration-2] + _ = x[KindFloat64-3] + _ = x[KindInt64-4] + _ = x[KindString-5] + _ = x[KindTime-6] + _ = x[KindUint64-7] + _ = x[KindGroup-8] +} + +const _Kind_name = "AnyBoolDurationFloat64Int64StringTimeUint64Group" + +var _Kind_index = [...]uint8{0, 3, 7, 15, 22, 27, 33, 37, 43, 48} + +func (i Kind) String() string { + if i < 0 || i >= Kind(len(_Kind_index)-1) { + return "Kind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Kind_name[_Kind_index[i]:_Kind_index[i+1]] +} diff --git a/pkg/onebot/value.go b/pkg/onebot/value.go new file mode 100644 index 000000000..6192ef881 --- /dev/null +++ b/pkg/onebot/value.go @@ -0,0 +1,354 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package onebot + +import ( + "fmt" + "math" + "strconv" + "time" + "unsafe" +) + +// A Value can represent any Go value, but unlike type any, +// it can represent most small values without an allocation. +// The zero Value corresponds to nil. +type Value struct { + _ [0]func() // disallow == + num uint64 // hold number value + any any // hold Kind or other value +} + +type ( + stringptr *byte // used in Value.any when the Value is a string + groupptr *Attr // used in Value.any when the Value is a []Attr +) + +//go:generate stringer -type=Kind -trimprefix=Kind + +// Kind is the kind of Value. +type Kind int + +const ( + KindAny Kind = iota + KindBool + KindDuration + KindFloat64 + KindInt64 + KindString + KindTime + KindUint64 + KindGroup +) + +// Unexported version of Kind, just so we can store Kinds in Values. +// (No user-provided value has this type.) +type kind Kind + +// Kind returns v's Kind. +func (v Value) Kind() Kind { + switch x := v.any.(type) { + case Kind: + return x + case stringptr: + return KindString + case timeLocation: + return KindTime + case groupptr: + return KindGroup + case kind: // a kind is just a wrapper for a Kind + return KindAny + default: + return KindAny + } +} + +//////////////// Constructors + +// StringValue returns a new Value for a string. +func StringValue(value string) Value { + return Value{num: uint64(len(value)), any: stringptr(unsafe.StringData(value))} +} + +// IntValue returns a Value for an int. +func IntValue(v int) Value { + return Int64Value(int64(v)) +} + +// Int64Value returns a Value for an int64. +func Int64Value(v int64) Value { + return Value{num: uint64(v), any: KindInt64} +} + +// Uint64Value returns a Value for a uint64. +func Uint64Value(v uint64) Value { + return Value{num: v, any: KindUint64} +} + +// Float64Value returns a Value for a floating-point number. +func Float64Value(v float64) Value { + return Value{num: math.Float64bits(v), any: KindFloat64} +} + +// BoolValue returns a Value for a bool. +func BoolValue(v bool) Value { + u := uint64(0) + if v { + u = 1 + } + return Value{num: u, any: KindBool} +} + +// Unexported version of *time.Location, just so we can store *time.Locations in +// Values. (No user-provided value has this type.) +type timeLocation *time.Location + +// TimeValue returns a Value for a time.Time. +// It discards the monotonic portion. +func TimeValue(v time.Time) Value { + if v.IsZero() { + // UnixNano on the zero time is undefined, so represent the zero time + // with a nil *time.Location instead. time.Time.Location method never + // returns nil, so a Value with any == timeLocation(nil) cannot be + // mistaken for any other Value, time.Time or otherwise. + return Value{any: timeLocation(nil)} + } + return Value{num: uint64(v.UnixNano()), any: timeLocation(v.Location())} +} + +// DurationValue returns a Value for a time.Duration. +func DurationValue(v time.Duration) Value { + return Value{num: uint64(v.Nanoseconds()), any: KindDuration} +} + +// GroupValue returns a new Value for a list of Attrs. +// The caller must not subsequently mutate the argument slice. +func GroupValue(as ...Attr) Value { + return Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))} +} + +// AnyValue returns a Value for the supplied value. +// +// If the supplied value is of type Value, it is returned +// unmodified. +// +// Given a value of one of Go's predeclared string, bool, or +// (non-complex) numeric types, AnyValue returns a Value of kind +// String, Bool, Uint64, Int64, or Float64. The width of the +// original numeric type is not preserved. +// +// Given a time.Time or time.Duration value, AnyValue returns a Value of kind +// KindTime or KindDuration. The monotonic time is not preserved. +// +// For nil, or values of all other types, including named types whose +// underlying type is numeric, AnyValue returns a value of kind KindAny. +func AnyValue(v any) Value { + switch v := v.(type) { + case string: + return StringValue(v) + case int: + return Int64Value(int64(v)) + case uint: + return Uint64Value(uint64(v)) + case int64: + return Int64Value(v) + case uint64: + return Uint64Value(v) + case bool: + return BoolValue(v) + case time.Duration: + return DurationValue(v) + case time.Time: + return TimeValue(v) + case uint8: + return Uint64Value(uint64(v)) + case uint16: + return Uint64Value(uint64(v)) + case uint32: + return Uint64Value(uint64(v)) + case uintptr: + return Uint64Value(uint64(v)) + case int8: + return Int64Value(int64(v)) + case int16: + return Int64Value(int64(v)) + case int32: + return Int64Value(int64(v)) + case float64: + return Float64Value(v) + case float32: + return Float64Value(float64(v)) + case []Attr: + return GroupValue(v...) + case Kind: + return Value{any: kind(v)} + case Value: + return v + default: + return Value{any: v} + } +} + +//////////////// Accessors + +// Any returns v's value as an any. +func (v Value) Any() any { + switch v.Kind() { + case KindAny: + if k, ok := v.any.(kind); ok { + return Kind(k) + } + return v.any + case KindGroup: + return v.group() + case KindInt64: + return int64(v.num) + case KindUint64: + return v.num + case KindFloat64: + return v.float() + case KindString: + return v.str() + case KindBool: + return v.bool() + case KindDuration: + return v.duration() + case KindTime: + return v.time() + default: + panic(fmt.Sprintf("bad kind: %s", v.Kind())) + } +} + +// String returns Value's value as a string, formatted like fmt.Sprint. Unlike +// the methods Int64, Float64, and so on, which panic if v is of the +// wrong kind, String never panics. +func (v Value) String() string { + if sp, ok := v.any.(stringptr); ok { + return unsafe.String(sp, v.num) + } + var buf []byte + return string(v.append(buf)) +} + +func (v Value) str() string { + return unsafe.String(v.any.(stringptr), v.num) +} + +// Int64 returns v's value as an int64. It panics +// if v is not a signed integer. +func (v Value) Int64() int64 { + if g, w := v.Kind(), KindInt64; g != w { + panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) + } + return int64(v.num) +} + +// Uint64 returns v's value as a uint64. It panics +// if v is not an unsigned integer. +func (v Value) Uint64() uint64 { + if g, w := v.Kind(), KindUint64; g != w { + panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) + } + return v.num +} + +// Bool returns v's value as a bool. It panics +// if v is not a bool. +func (v Value) Bool() bool { + if g, w := v.Kind(), KindBool; g != w { + panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) + } + return v.bool() +} + +func (v Value) bool() bool { + return v.num == 1 +} + +// Duration returns v's value as a time.Duration. It panics +// if v is not a time.Duration. +func (v Value) Duration() time.Duration { + if g, w := v.Kind(), KindDuration; g != w { + panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) + } + + return v.duration() +} + +func (v Value) duration() time.Duration { + return time.Duration(int64(v.num)) +} + +// Float64 returns v's value as a float64. It panics +// if v is not a float64. +func (v Value) Float64() float64 { + if g, w := v.Kind(), KindFloat64; g != w { + panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) + } + + return v.float() +} + +func (v Value) float() float64 { + return math.Float64frombits(v.num) +} + +// Time returns v's value as a time.Time. It panics +// if v is not a time.Time. +func (v Value) Time() time.Time { + if g, w := v.Kind(), KindTime; g != w { + panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) + } + return v.time() +} + +func (v Value) time() time.Time { + loc := v.any.(timeLocation) + if loc == nil { + return time.Time{} + } + return time.Unix(0, int64(v.num)).In(loc) +} + +// Group returns v's value as a []Attr. +// It panics if v's Kind is not KindGroup. +func (v Value) Group() []Attr { + if sp, ok := v.any.(groupptr); ok { + return unsafe.Slice(sp, v.num) + } + panic("Group: bad kind") +} + +func (v Value) group() []Attr { + return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num) +} + +// append appends a text representation of v to dst. +// v is formatted as with fmt.Sprint. +func (v Value) append(dst []byte) []byte { + switch v.Kind() { + case KindString: + return append(dst, v.str()...) + case KindInt64: + return strconv.AppendInt(dst, int64(v.num), 10) + case KindUint64: + return strconv.AppendUint(dst, v.num, 10) + case KindFloat64: + return strconv.AppendFloat(dst, v.float(), 'g', -1, 64) + case KindBool: + return strconv.AppendBool(dst, v.bool()) + case KindDuration: + return append(dst, v.duration().String()...) + case KindTime: + return append(dst, v.time().String()...) + case KindGroup: + return fmt.Append(dst, v.group()) + case KindAny: + return fmt.Append(dst, v.any) + default: + panic(fmt.Sprintf("bad kind: %s", v.Kind())) + } +} From 0b90074a48b4de2f447fe5025e9c4fb18e64df52 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Sat, 8 Apr 2023 17:08:53 +0800 Subject: [PATCH 601/612] feat: http timeout setting --- cmd/gocq/main.go | 2 +- internal/base/flag.go | 2 ++ internal/download/download.go | 9 ++++++++- modules/config/config.go | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 4463fbfdd..2e6c85a67 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -364,6 +364,7 @@ func LoginInteract() { }) saveToken() cli.AllowSlider = true + download.SetGlobalTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 在登录完成后设置, 防止在堵塞协议更新 log.Infof("登录成功 欢迎使用: %v", cli.Nickname) log.Info("开始加载好友列表...") global.Check(cli.ReloadFriendList(), true) @@ -375,7 +376,6 @@ func LoginInteract() { base.Account.Status = 0 } cli.SetOnlineStatus(allowStatus[base.Account.Status]) - servers.Run(coolq.NewQQBot(cli)) log.Info("资源初始化完成, 开始处理信息.") log.Info("アトリは、高性能ですから!") diff --git a/internal/base/flag.go b/internal/base/flag.go index 44e018cb9..b5b6ce53e 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -39,6 +39,7 @@ var ( AllowTempSession bool // 是否允许发送临时会话信息 UpdateProtocol bool // 是否更新协议 SignServerOverwrite string // 使用特定的服务器进行签名 + HTTPTimeout int PostFormat string // 上报格式 string or array Proxy string // 存储 proxy_rewrite,用于设置代理 @@ -88,6 +89,7 @@ func Init() { ReportSelfMessage = conf.Message.ReportSelfMessage UseSSOAddress = conf.Account.UseSSOAddress AllowTempSession = conf.Account.AllowTempSession + HTTPTimeout = conf.Message.HTTPTimeout } { // others Proxy = conf.Message.ProxyRewrite diff --git a/internal/download/download.go b/internal/download/download.go index a7b7e06f3..7622e4c4e 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -33,7 +33,7 @@ var client = &http.Client{ MaxIdleConns: 0, MaxIdleConnsPerHost: 999, }, - Timeout: time.Second * 15, + Timeout: time.Second * 5, } // ErrOverSize 响应主体过大时返回此错误 @@ -42,6 +42,13 @@ var ErrOverSize = errors.New("oversize") // UserAgent HTTP请求时使用的UA const UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" +func SetGlobalTimeout(t time.Duration) { + if t == 0 { + t = time.Second * 10 + } + client.Timeout = t +} + // Request is a file download request type Request struct { Method string diff --git a/modules/config/config.go b/modules/config/config.go index 22d9879dc..512915c0c 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -56,6 +56,7 @@ type Config struct { ExtraReplyData bool `yaml:"extra-reply-data"` SkipMimeScan bool `yaml:"skip-mime-scan"` ConvertWebpImage bool `yaml:"convert-webp-image"` + HTTPTimeout int `yaml:"http-timeout"` } `yaml:"message"` Output struct { From 91b4394d9bffe0a4c7104c9120cbe5e8d11b6bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Sun, 9 Apr 2023 17:25:57 +0800 Subject: [PATCH 602/612] optimize(t544): drop unsafe (#2076) Updates #2075 #2072 #2051 --- internal/encryption/t544/encryption.go | 2 +- internal/encryption/t544/encryption_amd64.s | 118 +++++++++++------- .../encryption/t544/encryption_generic.go | 7 +- internal/encryption/t544/t544.go | 10 +- internal/encryption/t544/t544_test.go | 22 ++++ 5 files changed, 104 insertions(+), 55 deletions(-) create mode 100755 internal/encryption/t544/t544_test.go diff --git a/internal/encryption/t544/encryption.go b/internal/encryption/t544/encryption.go index 75c91d2cb..89b09c0b8 100644 --- a/internal/encryption/t544/encryption.go +++ b/internal/encryption/t544/encryption.go @@ -50,4 +50,4 @@ func (c *state) init(key []byte, data []byte, counter uint64, nr uint8) { initState(c, key, data, counter) } -func sub_ad([]uint32) +func refreshState(c *state) diff --git a/internal/encryption/t544/encryption_amd64.s b/internal/encryption/t544/encryption_amd64.s index 5ad7b982d..246014583 100644 --- a/internal/encryption/t544/encryption_amd64.s +++ b/internal/encryption/t544/encryption_amd64.s @@ -10,8 +10,8 @@ DATA LC0<>+12(SB)/4, $1797285236 GLOBL LC0<>(SB), NOPTR, $16 TEXT ·sub_a(SB), NOSPLIT, $0-48 - MOVQ ·a+0(FP), DI - MOVQ ·b+24(FP), CX + MOVQ a+0(FP), DI + MOVQ b+24(FP), CX MOVQ CX, DX MOVBLZX 3(CX), CX XORB CX, (DI) @@ -48,8 +48,8 @@ TEXT ·sub_a(SB), NOSPLIT, $0-48 RET TEXT ·sub_b(SB), NOSPLIT, $0-48 - MOVQ ·a+0(FP), DI - MOVQ ·b+24(FP), CX + MOVQ a+0(FP), DI + MOVQ b+24(FP), CX MOVQ CX, DX MOVBLZX 3(CX), CX XORB CX, (DI) @@ -87,8 +87,8 @@ TEXT ·sub_b(SB), NOSPLIT, $0-48 TEXT ·sub_c(SB), NOSPLIT, $0-32 - MOVQ ·a+0(FP), DI - MOVQ ·b+8(FP), SI + MOVQ a+0(FP), DI + MOVQ b+8(FP), SI MOVQ SI, AX MOVBLZX (SI), SI MOVL SI, CX @@ -236,26 +236,27 @@ TEXT ·sub_c(SB), NOSPLIT, $0-32 MOVB CX, 15(AX) RET -TEXT ·sub_d(SB), NOSPLIT, $16-32 - MOVQ ·t+0(FP), BX - MOVQ ·s+8(FP), SI - MOVOU (SI), X0 +TEXT ·sub_d(SB), NOSPLIT, $24-32 + MOVQ t+0(FP), BX + MOVQ s+8(FP), DI + MOVOU (DI), X0 MOVOU X0, in-16(SP) - MOVQ SI, DI + MOVQ $16, CX ADDQ $15, DI - MOVB $16, CX + PUSHFQ + STD lop: LEAQ -1(CX), AX XLAT - MOVBLZX in-16(SP)(AX*1), AX - STD - STOSB + LEAQ in-16(SP)(AX*1), SI + MOVSB LOOP lop + POPFQ RET TEXT ·sub_e(SB), NOSPLIT, $0-32 - MOVQ ·a+0(FP), DI - MOVQ ·n+8(FP), SI + MOVQ a+0(FP), DI + MOVQ n+8(FP), SI MOVQ $4, AX lop: MOVBQZX -4(SI)(AX*4), DX @@ -295,9 +296,9 @@ lop: JNZ lop RET -TEXT sub_ab(SB), NOSPLIT, $0-24 - MOVQ ·s+0(FP), DI - MOVQ ·w+8(FP), SI +TEXT sub_ab<>(SB), NOSPLIT, $0-24 + MOVQ s+0(FP), DI + MOVQ w+8(FP), SI MOVL SI, AX MOVL SI, CX MOVL SI, DX @@ -329,14 +330,14 @@ TEXT sub_ab(SB), NOSPLIT, $0-24 MOVBLZX (DI)(DX*1), DX SALL $16, DX ORL DX, AX - MOVQ AX, ·retval+16(FP) + MOVQ AX, retval+16(FP) RET TEXT ·sub_f(SB), NOSPLIT, $24-68 - MOVQ ·k+0(FP), DI - MOVQ ·r+8(FP), SI - MOVQ ·s+16(FP), DX - MOVQ $·w+24(FP), CX + MOVQ k+0(FP), DI + MOVQ r+8(FP), SI + MOVQ s+16(FP), DX + MOVQ $w+24(FP), CX MOVQ CX, R10 MOVQ SI, R9 MOVQ DX, R8 @@ -367,7 +368,7 @@ inner: ROLL $8, AX MOVQ R8, 0(SP) MOVL AX, 8(SP) - CALL sub_ab(SB) + CALL sub_ab<>(SB) MOVQ 16(SP), AX LEAL -1(BX), DX SARL $2, DX @@ -378,10 +379,10 @@ end: RET TEXT ·sub_aa(SB), NOSPLIT, $0-56 - MOVQ ·i+0(FP), DI - MOVQ ·t+8(FP), SI - MOVQ ·b+16(FP), DX - MOVQ ·m+24(FP), CX + MOVQ i+0(FP), DI + MOVQ t+8(FP), SI + MOVQ b+16(FP), DX + MOVQ m+24(FP), CX MOVL DI, AX MOVLQSX DI, DI MOVQ SI, R8 @@ -407,13 +408,13 @@ TEXT ·sub_aa(SB), NOSPLIT, $0-56 MOVBLZX (AX)(DI*1), AX SALL $4, AX ORB 256(SI)(DX*1), AX - MOVQ AX, ·retval+48(FP) + MOVQ AX, retval+48(FP) RET // func transformInner(x *[0x15]byte, tab *[32][16]byte) TEXT ·transformInner(SB), NOSPLIT, $0-16 - MOVQ ·x+0(FP), DI - MOVQ ·tab+8(FP), SI + MOVQ x+0(FP), DI + MOVQ tab+8(FP), SI MOVQ DI, AX MOVL $1, CX MOVQ SI, DI @@ -446,10 +447,10 @@ lop: RET TEXT ·initState(SB), NOSPLIT, $0-64 - MOVQ ·c+0(FP), DI - MOVQ ·key+8(FP), SI - MOVQ ·data+32(FP), R8 - MOVQ ·counter+56(FP), AX + MOVQ c+0(FP), DI + MOVQ key+8(FP), SI + MOVQ data+32(FP), R8 + MOVQ counter+56(FP), AX MOVOA LC0<>(SB), X0 MOVUPS X0, (DI) MOVOU (SI), X1 @@ -467,8 +468,8 @@ TEXT ·initState(SB), NOSPLIT, $0-64 MOVUPS X6,112(DI) RET -TEXT ·sub_ad(SB), NOSPLIT, $8-24 - MOVQ ·a+0(FP), DI +TEXT sub_ad<>(SB), NOSPLIT, $8-8 + MOVQ a+0(FP), DI MOVQ DI, AX MOVL 40(DI), R10 MOVL 12(DI), R12 @@ -610,11 +611,42 @@ TEXT ·sub_ad(SB), NOSPLIT, $8-24 MOVUPS X0, 32(AX) RET +TEXT ·refreshState(SB), NOSPLIT, $16-8 + MOVQ i+0(FP), BX + MOVB 128(BX), CX + JE ad + SHRQ $1, CX +fr: + MOVQ BX, 0(SP) + MOVQ CX, c-8(SP) + CALL sub_ad<>(SB) + MOVQ c-8(SP), CX + MOVQ i+0(FP), BX + LOOP fr +ad: + MOVOU (BX), X0 + MOVOU 64(BX), X1 + MOVOU 80(BX), X2 + MOVOU 96(BX), X3 + PADDD X1, X0 + MOVOU 48(BX), X4 + MOVUPS X0, (BX) + MOVOU 16(BX), X0 + PADDD X2, X0 + MOVUPS X0, 16(BX) + MOVOU 32(BX), X0 + PADDD X3, X0 + MOVUPS X0, 32(BX) + MOVOU 112(BX), X0 + PADDD X4, X0 + MOVUPS X0, 48(BX) + RET + // func tencentCrc32(tab *crc32.Table, b []byte) uint32 TEXT ·tencentCrc32(SB), NOSPLIT, $0-40 - MOVQ ·tab+0(FP), DI - MOVQ ·bptr+8(FP), SI - MOVQ ·bngas+16(FP), DX + MOVQ tab+0(FP), DI + MOVQ bptr+8(FP), SI + MOVQ bngas+16(FP), DX TESTQ DX, DX JE quickend ADDQ SI, DX @@ -629,7 +661,7 @@ lop: CMPQ SI, DX JNE lop NOTL AX - MOVQ AX, ·bngas+32(FP) + MOVQ AX, bngas+32(FP) RET quickend: XORL AX, AX diff --git a/internal/encryption/t544/encryption_generic.go b/internal/encryption/t544/encryption_generic.go index e74c351e7..cd3b4f0ce 100644 --- a/internal/encryption/t544/encryption_generic.go +++ b/internal/encryption/t544/encryption_generic.go @@ -91,12 +91,7 @@ func (c *state) encrypt(data []byte) { dataLen := uint32(len(data)) for dataLen > 0 { if c.p == 0 { - for i := uint8(0); i < c.nr; i += 2 { - sub_ad(c.state[:]) - } - for i := 0; i < 16; i++ { - c.state[i] += c.orgstate[i] - } + refreshState(c) } var sb [16 * 4]byte for i, v := range c.state { diff --git a/internal/encryption/t544/t544.go b/internal/encryption/t544/t544.go index 321fcb1b9..34ab5b7b9 100644 --- a/internal/encryption/t544/t544.go +++ b/internal/encryption/t544/t544.go @@ -7,7 +7,6 @@ import ( "crypto/rc4" "encoding/binary" "math/rand" - "unsafe" "github.com/Mrs4s/go-cqhttp/internal/encryption" ) @@ -31,8 +30,10 @@ func init() { // sign t544 algorithm // special thanks to the anonymous contributor who provided the algorithm func sign(curr int64, input []byte) []byte { + var crcData [0x15]byte curr %= 1000000 - input = append(input, []byte{byte(curr >> 24), byte(curr >> 16), byte(curr >> 8), byte(curr)}...) + binary.BigEndian.PutUint32(crcData[:4], uint32(curr)) + input = append(input, crcData[:4]...) var kt [4 + 32 + 4]byte r := rand.New(rand.NewSource(curr)) for i := 0; i < 2; i++ { @@ -51,8 +52,7 @@ func sign(curr int64, input []byte) []byte { k3calc[6], k3calc[7] = 0, 0 rc4Cipher, _ := rc4.NewCipher(key3) rc4Cipher.XORKeyStream(key3, key3) - var crcData [0x15]byte - copy(crcData[4:4+8], (*[8]byte)(unsafe.Pointer(&magic))[:]) + binary.LittleEndian.PutUint64(crcData[4:4+8], magic) tencentEncryptionA(input, kt[4:4+32], crcData[4:4+8]) result := md5.Sum(input) crcData[2] = 1 @@ -61,7 +61,7 @@ func sign(curr int64, input []byte) []byte { binary.BigEndian.PutUint32(crcData[9:13], uint32(curr)) copy(crcData[13:], result[:8]) calcCrc := tencentCrc32(&crc32Table, crcData[2:]) - copy(kt[4+32:4+32+4], (*[4]byte)(unsafe.Pointer(&calcCrc))[:]) + binary.LittleEndian.PutUint32(kt[4+32:4+32+4], calcCrc) crcData[0] = kt[4+32] crcData[1] = kt[4+32+3] nonce := uint32(r.Int() ^ r.Int() ^ r.Int()) diff --git a/internal/encryption/t544/t544_test.go b/internal/encryption/t544/t544_test.go new file mode 100755 index 000000000..1f0f18460 --- /dev/null +++ b/internal/encryption/t544/t544_test.go @@ -0,0 +1,22 @@ +package t544 + +import ( + "crypto/rand" + "encoding/hex" + "testing" +) + +func TestT544(t *testing.T) { + r := hex.EncodeToString(sign(0, []byte{})) + if r != "0c05d28b405bce1595c70ffa694ff163d4b600f229482e07de32c8000000003525382c00000000" { + t.Fatal(r) + } +} + +func TestCrash(t *testing.T) { + brand := make([]byte, 4096) + for i := 1; i <= 1024; i++ { + rand.Reader.Read(brand) + sign(123, brand) + } +} From 1ed675d5bf0cf7f56b22fcc79fba76011dda0b42 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 9 Apr 2023 17:37:12 +0800 Subject: [PATCH 603/612] internal/t544: add `//go:noescape` --- internal/encryption/t544/encryption.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/internal/encryption/t544/encryption.go b/internal/encryption/t544/encryption.go index 89b09c0b8..1c2f3e4ee 100644 --- a/internal/encryption/t544/encryption.go +++ b/internal/encryption/t544/encryption.go @@ -23,25 +23,35 @@ var crc32Table = func() (tab crc32.Table) { return }() +//go:noescape func tencentCrc32(tab *crc32.Table, b []byte) uint32 +//go:noescape func sub_a([]byte, []uint32) +//go:noescape func sub_b([]byte, []uint32) +//go:noescape func sub_c(*[16][16]byte, []byte) +//go:noescape func sub_d(*[16]byte, []byte) +//go:noescape func sub_e(*[256][6]byte, []byte) +//go:noescape func sub_f(*[16]byte, *[15]uint32, *[16][16]byte) (w [44]uint32) func sub_aa(int, *[16][2][16][16]byte, *[16]byte, []byte) byte // transformInner see com/tencent/mobileqq/dt/model/FEBound +// +//go:noescape func transformInner(*[0x15]byte, *[32][16]byte) +//go:noescape func initState(*state, []byte, []byte, uint64) func (c *state) init(key []byte, data []byte, counter uint64, nr uint8) { @@ -50,4 +60,5 @@ func (c *state) init(key []byte, data []byte, counter uint64, nr uint8) { initState(c, key, data, counter) } +//go:noescape func refreshState(c *state) From 42606a825d0499ec7e2291cabbf34804545fc0aa Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sun, 9 Apr 2023 17:51:32 +0800 Subject: [PATCH 604/612] internal/download: disable http when not visiting go-cqhttp.org --- cmd/gocq/main.go | 2 +- internal/download/download.go | 35 +++++++++++++++++++++----- internal/encryption/t544/encryption.go | 1 + pkg/onebot/value.go | 1 + 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 2e6c85a67..852eb01e5 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -364,7 +364,7 @@ func LoginInteract() { }) saveToken() cli.AllowSlider = true - download.SetGlobalTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 在登录完成后设置, 防止在堵塞协议更新 + download.SetTimeout(time.Duration(base.HTTPTimeout) * time.Second) // 在登录完成后设置, 防止在堵塞协议更新 log.Infof("登录成功 欢迎使用: %v", cli.Nickname) log.Info("开始加载好友列表...") global.Check(cli.ReloadFriendList(), true) diff --git a/internal/download/download.go b/internal/download/download.go index 7622e4c4e..a82af5ad5 100644 --- a/internal/download/download.go +++ b/internal/download/download.go @@ -4,6 +4,7 @@ package download import ( "bufio" "compress/gzip" + "crypto/tls" "fmt" "io" "net/http" @@ -22,15 +23,28 @@ import ( var client = &http.Client{ Transport: &http.Transport{ - Proxy: func(request *http.Request) (u *url.URL, e error) { + Proxy: func(request *http.Request) (*url.URL, error) { if base.Proxy == "" { return http.ProxyFromEnvironment(request) } return url.Parse(base.Proxy) }, - ForceAttemptHTTP2: false, - MaxConnsPerHost: 0, - MaxIdleConns: 0, + // Disable http2 + TLSNextProto: map[string]func(authority string, c *tls.Conn) http.RoundTripper{}, + MaxIdleConnsPerHost: 999, + }, + Timeout: time.Second * 5, +} + +var clienth2 = &http.Client{ + Transport: &http.Transport{ + Proxy: func(request *http.Request) (*url.URL, error) { + if base.Proxy == "" { + return http.ProxyFromEnvironment(request) + } + return url.Parse(base.Proxy) + }, + ForceAttemptHTTP2: true, MaxIdleConnsPerHost: 999, }, Timeout: time.Second * 5, @@ -42,11 +56,13 @@ var ErrOverSize = errors.New("oversize") // UserAgent HTTP请求时使用的UA const UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" -func SetGlobalTimeout(t time.Duration) { +// SetTimeout set internal/download client timeout +func SetTimeout(t time.Duration) { if t == 0 { t = time.Second * 10 } client.Timeout = t + clienth2.Timeout = t } // Request is a file download request @@ -58,6 +74,13 @@ type Request struct { Body io.Reader } +func (r Request) client() *http.Client { + if strings.Contains(r.URL, "go-cqhttp.org") { + return clienth2 + } + return client +} + func (r Request) do() (*http.Response, error) { if r.Method == "" { r.Method = http.MethodGet @@ -72,7 +95,7 @@ func (r Request) do() (*http.Response, error) { req.Header.Set(k, v) } - return client.Do(req) + return r.client().Do(req) } func (r Request) body() (io.ReadCloser, error) { diff --git a/internal/encryption/t544/encryption.go b/internal/encryption/t544/encryption.go index 1c2f3e4ee..a2c6bade3 100644 --- a/internal/encryption/t544/encryption.go +++ b/internal/encryption/t544/encryption.go @@ -44,6 +44,7 @@ func sub_e(*[256][6]byte, []byte) //go:noescape func sub_f(*[16]byte, *[15]uint32, *[16][16]byte) (w [44]uint32) +//go:noescape func sub_aa(int, *[16][2][16][16]byte, *[16]byte, []byte) byte // transformInner see com/tencent/mobileqq/dt/model/FEBound diff --git a/pkg/onebot/value.go b/pkg/onebot/value.go index 6192ef881..eefa73d94 100644 --- a/pkg/onebot/value.go +++ b/pkg/onebot/value.go @@ -31,6 +31,7 @@ type ( // Kind is the kind of Value. type Kind int +// Kind const ( KindAny Kind = iota KindBool From bad3c86912d071efffb801847c11489093ccaf82 Mon Sep 17 00:00:00 2001 From: Azusa <61765965+Azusa-Yuan@users.noreply.github.com> Date: Tue, 30 May 2023 17:26:22 +0800 Subject: [PATCH 605/612] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BD=91=E6=98=93?= =?UTF-8?q?=E4=BA=91=E9=9F=B3=E4=B9=90url=E6=A0=BC=E5=BC=8F=20(#2146)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修改网易云音乐url格式 --- coolq/cqcode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/cqcode.go b/coolq/cqcode.go index c7751b524..a08a27538 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -713,7 +713,7 @@ func (bot *CQBot) ConvertElement(spec *onebot.Spec, elem msg.Element, sourceType MusicType: message.CloudMusic, Title: info.Get("name").String(), Summary: artistName, - Url: "https://y.music.163.com/m/song/" + id, + Url: "https://music.163.com/song/?id=" + id, PictureUrl: info.Get("album.picUrl").String(), MusicUrl: "https://music.163.com/song/media/outer/url?id=" + id, }, nil From 5bf64ee743f060b53f075810c270f3dded91135d Mon Sep 17 00:00:00 2001 From: Janet-Baker <36568027+Janet-Baker@users.noreply.github.com> Date: Sun, 4 Jun 2023 09:56:40 +0800 Subject: [PATCH 606/612] Fix #2119 (#2186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 尝试修复 #2070 (#2071) * Close file after uploading it to private chat fix #2119 上传文件到私聊后,释放文件。 --- coolq/api.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coolq/api.go b/coolq/api.go index f54ac67db..a1e6fc0f9 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -617,6 +617,7 @@ func (bot *CQBot) CQUploadPrivateFile(userID int64, file, name string) global.MS log.Warnf("上传私聊文件 %v 失败: %+v", file, err) return Failed(100, "OPEN_FILE_ERROR", "打开文件失败") } + defer func() { _ = fileBody.Close() }() localFile := &client.LocalFile{ FileName: name, Body: fileBody, From c8e480d12fdfc21e526166d1dbd90554b5b94c96 Mon Sep 17 00:00:00 2001 From: yuanyan3060 <58114805+yuanyan3060@users.noreply.github.com> Date: Sun, 4 Jun 2023 09:59:46 +0800 Subject: [PATCH 607/612] Update default_config.yml (#2151) --- modules/config/default_config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 9e4acd8ea..02efd138f 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -45,6 +45,8 @@ message: skip-mime-scan: false # 是否自动转换 WebP 图片 convert-webp-image: false + # http超时时间 + http-timeout: 0 output: # 日志等级 trace,debug,info,warn,error From 9b9ecd6a418fe0dfc0766882c00157c5acae94e6 Mon Sep 17 00:00:00 2001 From: felinae98 <731499577@qq.com> Date: Sun, 4 Jun 2023 10:23:45 +0800 Subject: [PATCH 608/612] chore(docker): adjust dockerfile and entrypoint (#2194) * chore(docker): adjust dockerfile and entrypoint --- Dockerfile | 1 + docker-entrypoint.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 869edff65..51330edc8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,3 +42,4 @@ WORKDIR /data VOLUME [ "/data" ] ENTRYPOINT [ "/docker-entrypoint.sh" ] +CMD [ "/app/cqhttp" ] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 3419c707f..34c5e72ed 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -17,4 +17,4 @@ chown -R ${UID}:${GID} /app /data chmod +x /app/cqhttp echo "Starting..." -su-exec ${USER} /app/cqhttp +su-exec ${USER} "$@" From 8e6e79f73432702e8c7b2ef7067bd610e6ab9963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=95=96=E5=BE=8B=E9=A3=8E?= <67987607+Mr-Ao-Dragon@users.noreply.github.com> Date: Thu, 8 Jun 2023 15:17:12 +0800 Subject: [PATCH 609/612] =?UTF-8?q?bootstrap=E6=94=B9=E8=BF=9B=20(#2192)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 保证云函数能直接调用启动脚本 * 添加末尾空格 以免某些shell无法正确执行。 * 还原 .gitignore 还原因为粗心提交上去的本地文件忽略 --------- Signed-off-by: BuildTools --- go.sum | 7 +++++++ scripts/bootstrap | 14 ++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/go.sum b/go.sum index 5312818df..d1ade111d 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,7 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -43,6 +44,7 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -52,6 +54,7 @@ github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -93,6 +96,7 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -120,6 +124,7 @@ github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCO github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE= go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= @@ -196,11 +201,13 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= gopkg.ilharper.com/x/isatty v1.1.1 h1:RAg32Pxq/nIK4AVtdm9RBqxsxZZX1uRKRSS21E5SHMk= gopkg.ilharper.com/x/isatty v1.1.1/go.mod h1:ofpv77Td5qQO6R1dmDd3oNt8TZdRo+l5gYAMxopRyS0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/scripts/bootstrap b/scripts/bootstrap index 9868a9eeb..89ef64f28 100644 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -1,6 +1,8 @@ -#!/bin/sh -echo "Start GOCQHTTP~~~" - -cp -f config.yml /tmp/config.yml -cp -f device.json /tmp/device.json -./go-cqhttp -w="/tmp/" faststart +#!/usr/bin/env bash +function index.main_handler() { + echo "Start GOCQHTTP~~~" + cp -f config.yml /tmp/config.yml + cp -f device.json /tmp/device.json + ./go-cqhttp -w="/tmp/" faststart +} +index.main_handler From 19906eba36cfa380dc02760ef6737f1010f6e44e Mon Sep 17 00:00:00 2001 From: xiangxiangxiong9 <1296976552@qq.com> Date: Tue, 20 Jun 2023 21:54:07 +0800 Subject: [PATCH 610/612] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BE=A4=E5=8C=BF?= =?UTF-8?q?=E5=90=8D=E6=B6=88=E6=81=AF=E4=BA=8B=E4=BB=B6=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E7=9A=84sub=5Ftype=20#2216=20(#2219)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coolq/converter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coolq/converter.go b/coolq/converter.go index 30720e2a8..591056004 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -92,7 +92,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) *event { "name": m.Sender.AnonymousInfo.AnonymousNick, } gm["sender"].(global.MSG)["nickname"] = "匿名消息" - gm["sub_type"] = "anonymous" + typ = "message/group/anonymous" } else { group := bot.Client.FindGroup(m.GroupCode) mem := group.FindMember(m.Sender.Uin) From 655e14977cf392147843cfa3f20144c9b09bf2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E4=B9=8B=E5=87=8C=E6=AE=87?= Date: Mon, 26 Jun 2023 22:03:57 +0800 Subject: [PATCH 611/612] =?UTF-8?q?feat:=20=E4=B8=80=E9=94=AE=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E8=84=9A=E6=9C=AC=E5=A2=9E=E5=8A=A0=E5=A4=8D=E5=88=B6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E4=BB=93=E5=BA=93=E7=9A=84?= =?UTF-8?q?session=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qqrobot/_build_latest_version_and_run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qqrobot/_build_latest_version_and_run.sh b/qqrobot/_build_latest_version_and_run.sh index 3f1062a2d..1890a2e82 100755 --- a/qqrobot/_build_latest_version_and_run.sh +++ b/qqrobot/_build_latest_version_and_run.sh @@ -8,7 +8,7 @@ go build -v -o qq_robot . # 拉取私有仓库子模块的最新版本,并将最新配置复制到项目根目录 git submodule update --recursive --remote -cp qqrobot/setting/{config.toml,config.yml,device.json} . +cp qqrobot/setting/{config.toml,config.yml,device.json,session.token} . # 运行 ./qq_robot faststart From 9c1390c75c97bbed5b153abffaa45b2ed3a1c360 Mon Sep 17 00:00:00 2001 From: Mrs4s Date: Tue, 27 Jun 2023 18:11:06 +0800 Subject: [PATCH 612/612] feat: support sign server --- cmd/gocq/login.go | 69 +- cmd/gocq/main.go | 9 + go.mod | 2 +- go.sum | 11 +- internal/base/flag.go | 4 +- internal/encryption/config.go | 3 - internal/encryption/t544/data.go | 30 - internal/encryption/t544/encryption.go | 65 -- internal/encryption/t544/encryption_amd64.s | 669 ------------------ .../encryption/t544/encryption_generic.go | 112 --- internal/encryption/t544/t544.go | 91 --- internal/encryption/t544/t544_stub.go | 7 - internal/encryption/t544/t544_test.go | 22 - internal/encryption/t544/transform.go | 21 - modules/config/config.go | 1 + modules/config/default_config.yml | 9 + 16 files changed, 62 insertions(+), 1063 deletions(-) delete mode 100644 internal/encryption/config.go delete mode 100644 internal/encryption/t544/data.go delete mode 100644 internal/encryption/t544/encryption.go delete mode 100644 internal/encryption/t544/encryption_amd64.s delete mode 100644 internal/encryption/t544/encryption_generic.go delete mode 100644 internal/encryption/t544/t544.go delete mode 100644 internal/encryption/t544/t544_stub.go delete mode 100755 internal/encryption/t544/t544_test.go delete mode 100644 internal/encryption/t544/transform.go diff --git a/cmd/gocq/login.go b/cmd/gocq/login.go index 404cf895a..5909af0d1 100644 --- a/cmd/gocq/login.go +++ b/cmd/gocq/login.go @@ -14,7 +14,7 @@ import ( "github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/utils" - "github.com/Mrs4s/MiraiGo/wrapper" + "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/mattn/go-colorable" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -22,18 +22,11 @@ import ( "gopkg.ilharper.com/x/isatty" "github.com/Mrs4s/go-cqhttp/global" - "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/download" - "github.com/Mrs4s/go-cqhttp/internal/encryption" - _ "github.com/Mrs4s/go-cqhttp/internal/encryption/t544" // side effect ) var console = bufio.NewReader(os.Stdin) -func init() { - wrapper.DandelionEnergy = energy -} - func readLine() (str string) { str, _ = console.ReadString('\n') str = strings.TrimSpace(str) @@ -222,9 +215,8 @@ func loginResponseProcessor(res *client.LoginResponse) error { log.Warnf("设备信息被封禁, 请删除 device.json 后重试.") case 237: log.Warnf("登录过于频繁, 请在手机QQ登录并根据提示完成认证后等一段时间重试") - case 45: // 在提供 t544 后还是出现45错误是需要强行升级到最新客户端或被限制非常用设备 - log.Warnf("你的账号涉嫌违规被限制在非常用设备登录, 请在手机QQ登录并根据提示完成认证") - log.Warnf("或使用 -update-protocol 升级到最新协议后重试") + case 45: + log.Warnf("你的账号被限制登录, 请配置 SignServer 后重试") } log.Infof("按 Enter 继续....") readLine() @@ -274,32 +266,47 @@ func fetchCaptcha(id string) string { } func energy(uin uint64, id string, appVersion string, salt []byte) ([]byte, error) { - if localSigner, ok := encryption.T544Signer[appVersion]; ok { - log.Debugf("use local T544Signer v%s", appVersion) - result := localSigner(time.Now().UnixMicro(), salt) - log.Debugf("t544 sign result: %x", result) - return result, nil - } - log.Debugf("fallback to remote T544Signer v%s", appVersion) - signServer := "https://captcha.go-cqhttp.org/sdk/dandelion/energy" - if base.SignServerOverwrite != "" { - signServer = base.SignServerOverwrite + signServer := base.SignServer + if !strings.HasSuffix(signServer, "/") { + signServer += "/" } response, err := download.Request{ - Method: http.MethodPost, - URL: signServer, - Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, - Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&id=%s&salt=%s&version=%s", uin, id, hex.EncodeToString(salt), appVersion))), + Method: http.MethodGet, + URL: signServer + "custom_energy" + fmt.Sprintf("?data=%v&salt=%v", id, hex.EncodeToString(salt)), }.Bytes() if err != nil { - log.Errorf("获取T544时出现问题: %v", err) + log.Warnf("获取T544 sign时出现错误: %v server: %v", err, signServer) return nil, err } - sign, err := hex.DecodeString(gjson.GetBytes(response, "result").String()) - if err != nil || len(sign) == 0 { - log.Errorf("获取T544时出现问题: %v", err) + data, err := hex.DecodeString(gjson.GetBytes(response, "data").String()) + if err != nil { + log.Warnf("获取T544 sign时出现错误: %v", err) return nil, err } - log.Debugf("t544 sign result: %x", sign) - return sign, nil + if len(data) == 0 { + log.Warnf("获取T544 sign时出现错误: %v", "data is empty") + return nil, errors.New("data is empty") + } + return data, nil +} + +func sign(seq uint64, uin string, cmd string, qua string, buff []byte) (sign []byte, extra []byte, token []byte, err error) { + signServer := base.SignServer + if !strings.HasSuffix(signServer, "/") { + signServer += "/" + } + response, err := download.Request{ + Method: http.MethodPost, + URL: signServer + "sign", + Header: map[string]string{"Content-Type": "application/x-www-form-urlencoded"}, + Body: bytes.NewReader([]byte(fmt.Sprintf("uin=%v&qua=%s&cmd=%s&seq=%v&buffer=%v", uin, qua, cmd, seq, hex.EncodeToString(buff)))), + }.Bytes() + if err != nil { + log.Warnf("获取sso sign时出现错误: %v server: %v", err, signServer) + return nil, nil, nil, err + } + sign, _ = hex.DecodeString(gjson.GetBytes(response, "data.sign").String()) + extra, _ = hex.DecodeString(gjson.GetBytes(response, "data.extra").String()) + token, _ = hex.DecodeString(gjson.GetBytes(response, "data.token").String()) + return sign, extra, token, nil } diff --git a/cmd/gocq/main.go b/cmd/gocq/main.go index 852eb01e5..10ab254f2 100644 --- a/cmd/gocq/main.go +++ b/cmd/gocq/main.go @@ -14,6 +14,7 @@ import ( "github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/client" + "github.com/Mrs4s/MiraiGo/wrapper" para "github.com/fumiama/go-hide-param" rotatelogs "github.com/lestrrat-go/file-rotatelogs" "github.com/pkg/errors" @@ -163,6 +164,14 @@ func LoginInteract() { } } + if base.SignServer != "-" && base.SignServer != "" { + log.Infof("使用服务器 %s 进行数据包签名", base.SignServer) + wrapper.DandelionEnergy = energy + wrapper.FekitGetSign = sign + } else { + log.Warnf("警告: 未配置签名服务器, 这可能会导致登录 45 错误码或发送消息被风控") + } + if base.Account.Encrypt { if !global.PathExists("password.encrypt") { if base.Account.Password == "" { diff --git a/go.mod b/go.mod index 4262ef2db..ffe42a157 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/FloatTech/sqlite v1.5.7 github.com/Microsoft/go-winio v0.6.0 - github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5 + github.com/Mrs4s/MiraiGo v0.0.0-20230627090859-19e3d172596e github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc github.com/fumiama/go-base16384 v1.6.1 diff --git a/go.sum b/go.sum index d1ade111d..d251a116c 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5 h1:E4fIQ0l/LNZK44NjdViRb/hx4cIeHXyQFPzzkx7cjVE= -github.com/Mrs4s/MiraiGo v0.0.0-20230401072048-f8d9841755b5/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= +github.com/Mrs4s/MiraiGo v0.0.0-20230627090859-19e3d172596e h1:99itMjI//+KaFF0+0QCBg/uHhGMJ99jG2lP6z/UnOsU= +github.com/Mrs4s/MiraiGo v0.0.0-20230627090859-19e3d172596e/go.mod h1:mU3fBFU+7eO0kaGes7YRKtzIDtwIU84nSSwTV7NK2b0= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d h1:/Xuj3fIiMY2ls1TwvPKmaqQrtJsPY+c9s+0lOScVHd8= github.com/RomiChan/protobuf v0.1.1-0.20230204044148-2ed269a2e54d/go.mod h1:2Ie+hdBFQpQFDHfeklgxoFmQRCE7O+KwFpISeXq7OwA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= @@ -31,7 +31,6 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -44,7 +43,6 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -54,7 +52,6 @@ github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -96,7 +93,6 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -124,7 +120,6 @@ github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCO github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE= go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= @@ -201,13 +196,11 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= gopkg.ilharper.com/x/isatty v1.1.1 h1:RAg32Pxq/nIK4AVtdm9RBqxsxZZX1uRKRSS21E5SHMk= gopkg.ilharper.com/x/isatty v1.1.1/go.mod h1:ofpv77Td5qQO6R1dmDd3oNt8TZdRo+l5gYAMxopRyS0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/internal/base/flag.go b/internal/base/flag.go index b5b6ce53e..e622ecaea 100644 --- a/internal/base/flag.go +++ b/internal/base/flag.go @@ -38,7 +38,7 @@ var ( FastStart bool // 是否为快速启动 AllowTempSession bool // 是否允许发送临时会话信息 UpdateProtocol bool // 是否更新协议 - SignServerOverwrite string // 使用特定的服务器进行签名 + SignServer string // 使用特定的服务器进行签名 HTTPTimeout int PostFormat string // 上报格式 string or array @@ -64,7 +64,6 @@ func Parse() { d := flag.Bool("D", false, "debug mode") flag.BoolVar(&FastStart, "faststart", false, "skip waiting 5 seconds") flag.BoolVar(&UpdateProtocol, "update-protocol", false, "update protocol") - flag.StringVar(&SignServerOverwrite, "sign-server", "", "use special server to sign tlv") flag.Parse() if *d { @@ -89,6 +88,7 @@ func Init() { ReportSelfMessage = conf.Message.ReportSelfMessage UseSSOAddress = conf.Account.UseSSOAddress AllowTempSession = conf.Account.AllowTempSession + SignServer = conf.Account.SignServer HTTPTimeout = conf.Message.HTTPTimeout } { // others diff --git a/internal/encryption/config.go b/internal/encryption/config.go deleted file mode 100644 index 9bba4276f..000000000 --- a/internal/encryption/config.go +++ /dev/null @@ -1,3 +0,0 @@ -package encryption - -var T544Signer = map[string]func(int64, []byte) []byte{} diff --git a/internal/encryption/t544/data.go b/internal/encryption/t544/data.go deleted file mode 100644 index 2c0da7bd2..000000000 --- a/internal/encryption/t544/data.go +++ /dev/null @@ -1,30 +0,0 @@ -//go:build amd64 - -package t544 - -import ( - "archive/zip" - "bytes" - "io" - "unsafe" - - base14 "github.com/fumiama/go-base16384" -) - -var cryptoData = base14.DecodeFromString( - `戒縰幐一丂一一一娠煳樨刀一乀一圀一吷垌脲妘璖蘀丄跾縀一丆橶縐硍笂儸祠婔瓛伄荰且涵箁澜徴唭嵛庋儉犛埫渀皇淛僎侻昰豪楃愨煬尴豀仵矫栐攂縑千瞷劕瞆繍嵜久劒蕨捅臛佤峺蔗毑襱濄葬呟歍癩覥乆萦厰橤刭翖燈伞榠计賎卯嘭测庆蜖予秶浤熦盭偊光紥礌宒梩伂埧禀嚢曉葊杂箼碒播傸类本煻縢萄宴妡藆調矿筌峸螸翣椶儮扴儳贶擣懪皵孽犠嫛縻萍詌奠蛂竀膜蝆茺耈哣絇爃崵蝝滷楷榏篐棔赦僷舆俨剁椦夤垫睩廜胐弑荜藑斛螥呿臬牴竚聈慑蒔猆珟悑譀弭氐桓历哉孔滁勥始浏崜幕仨仞才簦潈纓竔肕浹戯垁捒崒腖坭撅橅婨搅臛孤槟佖濚襹椄梬菀噭癝冾毥蘢嘅旵曫侃懿棘毱蕵烙椁琼傡祤羶椆攨褆枳劭莟蚳蕚巬嘙矲嘬咯彅祫襥幦膯狝楹繦箻潷稛蛿復纉潬攘掜啲滅篇屫悱欧堶矖幁蔝督丝薢聁薊惣橹砖璧碸禱泭賽翲羞俟槗胅費党痼箝玀腅秨唇興溱虝小汁綡它侖叔芴虭墌嚇螖溈熀秓灻媖搫碸綢贷腎嫵兂囟疾貎跧瑘豄殿爥蓆恆榎芤矟訦憄峙坈圪趕熪亙栣柒珯倍塢衑偦楫峾械纙垺爘垜秝謩蒎捤疩汋豽蛢储嗩嵆潲橙蠧市誣楱嬚微茊睙荻肙漦柩荝讞癓禯墕謰墪摏欚笃螼礑皆沵塪艫狁稪索琴夂憤妱堂觛嚹衦竍觠糐翏舭懒莚萻支楰貖许偵呫爔蓐粀艰糬梃枯俏壶謐刂觡枇庿祛妬催歡繩跽洊澎儰覄瞇劾峮穛奤嫆笪孼氁志耡瀀脦壂粲盋宇妕毃嫉娛旍痹吜衦掺臽罦元罧糴淘户惻謵笾囂觔蜐損斢燖莻笠蕟栫其寅祛某簘宭椦觛臃歭脥訍蕱柫絻凯昷翮畗渘球簑忪蛢劂萾娯崎蜃槭斔作曹湲蓣申茮岃撕燩屑譞盼萎裉紦嶰溃簻惆刻簪刵泴恌巰庸筏匸乢誒唛痍徵茽卼帿荛垮浥荝烷桫琯澿涹豘臐跩獌茾藈烧詺豘緗景拾樚滿訄一跿茄稌刔一一渀一一利绰琚乀一丈一傀一侑獣槙呒螉睮匳繑唁丌丬儧脦跛蕇偀掤概蜣裴檮柬欖篠曣葛矊幜晎妌繀胵綔畍偰蟞挚堧賨粚嗩璘匷崍惹幡簱偫泍呸竕尡硱渔豶厾篁擈痘絨茤乯蕞滱簇勃茶絀塐蟖瞅繭唕解誤笉惖肑暙廬罼豚狆咎揊訉妖品沕疗稉狏猊痮觢瑟窇愨椡摓谴祰蒄扫噃盃蜭裣挃崺肖息揁莽焼溒崓荸晩美炏曣聱褎幘珘矐肖滗羶懶帔萉藲璐媹譈哶仜悻衴盈旋穽蠮焛獜粏腨咈猉惼癹竻蘗澃茀氭懺愰灿岷外犅言一跿茄稌刔一一渀一一奚壾脚乀一丈一傀一侕籣槙呒螉睮匴奱攁丈縔冇她赛巭貯恿丞坓玺珏窴萙椓蘈嵌艎悧訇楬砒缎甍菲稻撟摤蘎仛汈奉社忢楉盒曇水碱蜖偧姪繵栤讞恋琗尔沌臔纱懛窃奢筇窴冄榭情憞妡縍磱嬯苜螜惃斓庭繤蕬蝂竨猞腥胒壠纎嵭癞盢抬拱袒笓懚囔幭紒崋詘窄豜爒祽桾礬旬伩觟渺湩穓蒧漞睱亻俸糋华賸帬招狣吼藭圎塘俖佰斯眎緔欇璘撈哷焿涬唖椥暌劧裤蚸担砬氎卼牛禤芗怰條娯啿蒇舆蛂槊睝晽滩譙夌牜一跿茄稌刔一一渀一一塺絏懡偀一亀一僀一俑潢椙揶効灩榓坶氨焱向何盧毿箲贿侁贴劽捝如翕浱杤毴膔謻攴毹菵厽蕴豢矄莕藗瑐苃籷藧斐摗緙藺琒圆奇疃吏儌円祇熁玎籾粆粸说孡羑羙绌婵蟯桚痎喽粦葔歧吽榾涕扖裘橧蓼衒涙虡荟眭矼解窨脸脸堌戌笞蚼簠裏莲薒榘觮杣妌膌膂幅垖摫堫構疜糆琺蟶窩膞椙衟撨旊崾慷擪晁祿宧襼櫗蝾窡眵汳毘覹襟呡譼圫絖蠺章嵻柾瞬崌沌瑎汪祏啜泪嬰氰朜簞尘瑞沬蚼筌葮賒羫粸敳熉埽罾癪覴謳斶夿澭粑肵賫臎姭椬沫濵洈焳焳滨盫覃羙纘繰庴賳縴翗喽瀮唽同蕟禡禎崝浯晳獾槧枅屾澶穷蕴簹疅葴翪蓪壳稝蕬緭乻催贕沩覰俘腤嶩燗讯攦孺唽吞脃夦球敵岒媻崜櫻泓田翗唜賞苧聾試葛惻怍矮皬设抝蛂襹襻恺婻脫裥惫矷焌秳烛籧甍尋弛泌呺膉瓠慁溫脤湋旜請搜蝺裨胵盳廢癫膞姉峫裷控姱琚峬乶娽擋糟柛朌棟脜桋侎泟浩楫寪矷祷狭溏嬯枎唍咂压例澫澣溢艵翇藓捇旞讕婽词婵蔷猹襻熝艪犦苼蕯籸碻灳翽汗蝍篡紌珌文乕僱狎蚣綄掴賟獭罏揼彥嚷熼簹糯洭滌蜺璸蛱菲茦撰諡绑砙当请欯臌珨端戔蓆袘柵政杹烾罓消奟葟柎攼歸紭冊伖燖槫膻眵襴尧澣罀烁脳焳溨苑蟓覫艑矣觓舞毨徑覻堫敾蓊粜苳烾纲愸螾淐森葓薸码焏掋糶豔掇喙见筗詄摰螝趷荧篢禳俘滪昘簙稺爓幷絣奜泺咸裻瓨盫豿佳瓚曧絟噝凟祣厽寫臄爯药碹睜娙甎偻蟖珐诋蓓奟尟続苍憽盭寝咃匃姟浍挝涴氨婜婻萲翗俯枃屘罻搒误曋唜耳汷掹主掝寮矎腽桢许殑呇呧冒瞃仅绝碿穑蟒跆傼撺磎击苴苮受薫專羓僱滈蝱摱櫳奚泷菑菷矬孲譋慛汫澸涏至糷巑粏棹筄矔挗曃襦塸篕纹貘菻翀僓觇跽姷煏卺肔獳脑洿緽垵蘩拡諑盱縲翇嵗眣懧榟棂藧瀸蟫圎毟袮桝浜蕟梾毁厯曅捇僡磡盀琲槒氹廋螰矅螻袚嫸禛絓井篹板慅敜喇疎否薾螰剐孜宫簎痞羧緼徢覸榾抙掯蓛繳碎竧峿桕櫅虨瘘粚咸忲製賓壼膒祹设搝汹撸婤橦樳儑樹樴栽欝橱臩帥跻糆士盫臞羔嵻橅纮葛袥孑臲篷菴婮慹敬倘嫎寪洃玩苹袲簵蜺毬罬磴玴藏泼聅擽琰搐朝檞簏夬溒姌藗棸怘咭簏唗痚纗蟼参屽舃巢况棹嵮豼犊浞萞觥耱苃葠楽徣櫿嗡瞤詶磗紸藕蓣澥牗橮咎傊心儃瞖慎伮讬坳矷择盓濷絫楳氋疷簻詮琗臖藞罢恩蔮毸螕簾儖嘿吞褹攋诧确盱詔撀犴永塘賻烲舨婐岼槲滵扽窖筨熮搌膃乕矣膧薱獏臸榟慅薄觷嶗睴覵唲蕴柡樝拗椛庰籯薿稣潽簥禦煡滌虰想僞荗覛诟吜倆夆奌膌膂盓忧懮桡大粴朚觚芷簏唾莆簽蒍訬狭萚献糮胢攑蒭谼珇甓羵荢蒠蠏泌呢冖篺筌崨畭捜旜豃倏腻坸痓趼嚇愬粔九罹讬硻薲睎嗞训筱褌綇瓮腽誧礕官峄嗣蟽柖贘氮旪懕裺癆慪蟌淯愷虿姦蟮蛢誾伫貇嗹畽趶唽褰滀荵诏媵濯籭煏泺蠚蛫狳竁甬暪檿艑螎紝蕳磮湋赃妺奧菥玽蛖泻肖崴競蚊諬啺机涹徂懗菗痶覢櫎仂元垶敊倬賺炲褺泷茵志蔹焅漦濕旇芏璷懒费椻歡浾葏蛍愍睷螕碢评詫椵讘咇瑇卯卹偍襳睷豟凁织盁牺湸蚓慡蔢璃嵻塘賻磴穥臞薐蟌筡膞榻薝喸瞋浗婋苏緆斎毼瀩嬮磰珥譧淞堃會泌呦熛紅夃秆磬膕獳赿栠蚅膇竁璮蚹讈圶聑苑菇喽焮泶掩蛳薵笯糯俄覉茵讌粵燦殰苿搢糷尵莪夿愠奨岈洱棋溢幾诛愍蝹礖斾号諏志脆柃滢灍羖憅嬆薙媅宜嵻硴話觚肇絻峓田羷蝼肩費菍畛籆刿哬紥疺疎楡婧毰秆瓽憛硘誩嶝簏兆燇之嗟欸蟞叹煖疦覿曃斞專抟汰蘨猜窪粏嫝涭焍泷猕荴衏巶榷肾沚詪蘒笷榑漏羓埣矅蜽脴槫燦僉硋熇兆渝嫝叏猩欵杮咎傈得埋盱牱瑰脌歱翏玩諗埣尝曟喹补狻沥翟煐杺撾媴賕譯瞖籷羒反珼擱斝滯儃姏崙視瓕綇環覍纹赖詗劂奌窰枌瓖攽罹枖岸賺蒲蟩褭臞讑溙签厼憫哽胙褉嫩能柗栝姥姫豓留孰瘘稙篟癣孶蠵痥豉慆燇下兖燏介啜嬚崜儬橳譴捱请宵濏綧瘻嵥槳缓孟尔見粺箶蚊燕秵旘孩綟甩愌婎赑箷汗蝂肬柱幹絿籣棹柔蕻娽琇冂灑胊狡桥犑硣觮楫兜絻咖诏管秎斝败奒瀛蝉攻岓臟囖哱挎篮橁蠱枫赦蝸綼磣罠乢艹蚉胴艵诧硪賾矗璝懖哈栤听啞讬硻滱觙勃觏儑楟櫱屚艶赴藫员慳裙裢腷厔嶣蝴豻綦奻畷甡炗偅仅蛓翕绠皔檲盹倭畻谶趴蕱惥臑滍觘胗姝憥儶啲塨嫻袊胬襾淠攀丿践伬億匀一丠一一丄嶝簠傀帀一伀丂縀丁艡暛呕讈籢桛渀万跾曟啷緉祯罌世檯賗磝窨夥诺摑贊蕒炯甩唬僟讓垍燿洰芥蝼攝滄挄翈許灘厚俄瘎妬疲歂昲繨楮撨匣絚脩蛋虅嵄仭嘿夕箫妾屒狅焿廯碾舴苎卅豀痵仲涨扨興豊欸譯奭的廿諴竐繏穟珑佼悝谽朗得宂住蔈炩倡哮簅寥蘯槠媎溤爘牜纴裆塆揤汹誃殶寕憪擅槓硥沫溋矠献唩祌榣歴嗒觘簩績筙犀巘屡孕覘椄殞虾垁厧枎猦翨涧展堷蛊吤宿螐璄呤笏稕妱昄一跿茄稌刔一一渀一一尊终簊侀一丘一僀一俑潢椙揶媹灩榀一槿蜀一一一倃偂绐蘐吒历激昔椝叄満纐稴専滲芜蜶儂煣皸爎偏脒媨帘怖咇么楁拙旡幹桎檛佡荍卿暆但翑橈咅著譅吜功琤材民毚抵皀纐稴専囎枻蕻灃栊瓊贉煘粷嗲堏奉膓昪巯松畘稺瞢緬庸螯湼誣亣曨簤儢絇熵疍僬秺炌宋樿撾倸圿婈肠蠧譳蘚曰烮芥祃蚇痙瓮爆仭覻蓐琳埂蓕弙炖蒇商杈呪脥噬嗕蛾嬩紖攠糔羨孁獿苦爑墘詔喇媏憫択薢煺獔涘慜瓝摽栏穋獡艗凹蜵矝氄袧揇圔柽燗甔繋纁昔墼箬襢拨稆曕牖籷攒殙掰莟傖潲巧旀蒥谛穒兛厩藅匟榖杏眠彰怸蜰嬭聋袦令揑峱潆捷朷蚎化斄愞贷櫞牼裐衽洐筚犵絾幷堢來什殽瓚萋兿赘堸愙狠毬晬掷祺緢嫅奩枕炳莽卒榌肐嫄尦峉恱苨汃显禁毙奬薥倴蝍蚲蘟繥篒擌惏琵侨囇玚菄蚓膮櫒倥傿瑻弁痉絺凃瑆絓栯嶽宰廩嵯彂畑瞽淕粩涨贰璨珖磿胘晇粳裐洤蒚觧杷疙褎炸法嵬粺橗昳笻秀焼澶襭帼嚾蛤炌簐擫疃屻亻膊昤賆槮呄緶台堄萷摶纨艈業蛄瞗螼羫咥掤娖愢穩癸独碽溪觐嫊矮藸抳碗壳荜舮九擸哒糃寫娣抾台衁祚謞耪彍烐濹炏箫檣肇娷夣嶾帢薬憮恴章爷懐桛炓茍溹熒擏壇嚮桗吠剡岉臤弲禺矿乨趠褦粰檢胮瑸滂擳纈獙擮槊妜榘貏怺啳款絨萹虹謾噋匥棞財覄坟廼溨菴儋貁碕勜繮槬诶爑瑣珼菓翹藻激敇柯涫孓勥認綹蜑猰欎寜斑蒳浆扖惓羾撰簎犯窌庘傶犿巃虛棦瘭兡移墔焃燠嘸昤侌灢嚰樞垒獧佐搪榞牆膺篱唊癎梇挰喯巫擥剂謽禙莡蓌膧搚臩狆牑昌蝳沅塽圡慚圍萩烊褳幽烑塬凑徿洯等溗焴箶姀畜媎贐嫕粒哰朸擹聻爈獌證榠窹俻擾譸羆桙谷癶聑淽罦疸杷賌牝皅巟膠捨猟懪祘礕贞哙嶾捼粋茊繋蛧譀疩埠玷繎弿玍纄赤薚勉喾刾賯蘽芐泋姌络忺梞蚟居袨屒礁觋忏夗矧噎寯啇贵挪磙痜蝰澦栜詶婂卂橱咡簡婷繜幩譹菂氃朥侯意朶度畀呯茁哽扵胃厛歡忧尘曷肴熴聡蔈爾仱痙屗絲螗栰捾瀉筮栁篻垕灌墺蚧菘湞篰畗瘑肃貓揯烘嵒彔築盭硝徬峦檉捊猰猱皴嵉柟產戍筋悒燑樄贖亍徲扌瀺兏讝沺怅嫿涹碨欧牣詮噔禵盼箮蒰磅甌籚主瞨傿楆塷艂譮剫唁裍甮畔腋厐疖斎祶勲奉堭搣搁刦磜舮爽誩樒谠埊絵房罖債葙謆哪寽瓅峑堗羿尶悩薗慌潘睞痝溱擛峨斠统朸砜帓畼讳椅炽苺衧哉欳氕搸凴慈罴茁菌杴予懕媧犲詷夣衈瑄洤滠塚抜誗褴涖諒槈赳捰胼船广繋婀捵翇塷羄瞏匪莄艶藬失肷孿爠蛁艶淰芏櫆胿廢祟姅諁罞榣緱伇峞文蔁俼崯巔倎賿跞積螒笩貑係枹硻罖簒粻襅琨貎菣嗗槨偿漜挭曙喜杏砦瘾扩咣珣帐七跿戒縰幐一丂一一一勗脫噘一丄一一夀一商厉穥旙僦垥籢昹氦涇牣螸呖妝潤蛁湀七跿戒縰幐一丂一一一幗堷幜一丄一一夀一商厉穥旙惦垥糲屝喜貨敁哏蓟礩糥趚纲一仿跔劰娐戀一亀一一丿罨貜簀一净一下一万待灬杗葢螉睮估儑刀帀丰屰缚襅催縩瘠澇傂皞婄庑爔欃僁忑穼唃叅谀一巿譁夁亅丁帀一倀一一丰溍腰瘐一丁一两一一一一一一一一一丘蔦嫌耮暚擥伬伂匀佀一丈一一丁傜崉澠刀一亀一瘀一一一一一一七刐一朙搶讑献暚擥伬伂匀佀一丈一一丂莢緬羠刀一亀一瘀一一一一一一万娔一杛琶讑献暚擥伬伂匀佀一丈一一丂疫苴谐爀一嘀一稀一一一一一一下战一欘搦羕浡妘璖蝁夁亅丁帀一倀一一乃蟛瀀瘄一一帀丬一一一一一一一僼帀丝吖垱獟暋琦玹幋乀潀乐一丂一一一蘫儢蘨吀一习一夀一一一一一一七耑一商厉穥旘胦垥籐惀帡帀戀一亀一一丄菳壈搀一伀一下一一一一一一一严吀万待灬杗葂螉睮戒縐噐且一一渀一一刕肍舗一丁一一僀一一一一一一一朆一俑潢椙揶抹灩榔劰刈戀匀一丠一一丏詚嶧妀一仰一丂縀一一一一一一丩侀丁艡暛呕讘籢桛猄稔吀一一爀圀赀帀凬昀一一㴃`, -) - -var cryptoZip, _ = zip.NewReader(bytes.NewReader(cryptoData), int64(len(cryptoData))) - -func readData[T any](name string) T { - f, err := cryptoZip.Open(name) - if err != nil { - panic(err) - } - data, err := io.ReadAll(f) - if err != nil { - panic(err) - } - return *(*T)(*(*unsafe.Pointer)(unsafe.Pointer(&data))) -} diff --git a/internal/encryption/t544/encryption.go b/internal/encryption/t544/encryption.go deleted file mode 100644 index a2c6bade3..000000000 --- a/internal/encryption/t544/encryption.go +++ /dev/null @@ -1,65 +0,0 @@ -//go:build amd64 - -package t544 - -import ( - "encoding/binary" - "hash/crc32" - "io" -) - -var crc32Table = func() (tab crc32.Table) { - f, err := cryptoZip.Open("crc32.bin") - if err != nil { - panic(err) - } - data, err := io.ReadAll(f) - if err != nil { - panic(err) - } - for i := range tab { - tab[i] = binary.LittleEndian.Uint32(data[i*4 : (i+1)*4]) - } - return -}() - -//go:noescape -func tencentCrc32(tab *crc32.Table, b []byte) uint32 - -//go:noescape -func sub_a([]byte, []uint32) - -//go:noescape -func sub_b([]byte, []uint32) - -//go:noescape -func sub_c(*[16][16]byte, []byte) - -//go:noescape -func sub_d(*[16]byte, []byte) - -//go:noescape -func sub_e(*[256][6]byte, []byte) - -//go:noescape -func sub_f(*[16]byte, *[15]uint32, *[16][16]byte) (w [44]uint32) - -//go:noescape -func sub_aa(int, *[16][2][16][16]byte, *[16]byte, []byte) byte - -// transformInner see com/tencent/mobileqq/dt/model/FEBound -// -//go:noescape -func transformInner(*[0x15]byte, *[32][16]byte) - -//go:noescape -func initState(*state, []byte, []byte, uint64) - -func (c *state) init(key []byte, data []byte, counter uint64, nr uint8) { - c.nr = nr - c.p = 0 - initState(c, key, data, counter) -} - -//go:noescape -func refreshState(c *state) diff --git a/internal/encryption/t544/encryption_amd64.s b/internal/encryption/t544/encryption_amd64.s deleted file mode 100644 index 246014583..000000000 --- a/internal/encryption/t544/encryption_amd64.s +++ /dev/null @@ -1,669 +0,0 @@ -//go:build amd64 -// +build amd64 - -#include "textflag.h" - -DATA LC0<>+0(SB)/4, $1634760805 -DATA LC0<>+4(SB)/4, $857760878 -DATA LC0<>+8(SB)/4, $2036477234 -DATA LC0<>+12(SB)/4, $1797285236 -GLOBL LC0<>(SB), NOPTR, $16 - -TEXT ·sub_a(SB), NOSPLIT, $0-48 - MOVQ a+0(FP), DI - MOVQ b+24(FP), CX - MOVQ CX, DX - MOVBLZX 3(CX), CX - XORB CX, (DI) - MOVBLZX 2(DX), CX - XORB CX, 1(DI) - MOVBLZX 1(DX), CX - XORB CX, 2(DI) - MOVBLZX (DX), CX - XORB CX, 3(DI) - MOVBLZX 7(DX), CX - XORB CX, 4(DI) - MOVBLZX 6(DX), CX - XORB CX, 5(DI) - MOVBLZX 5(DX), CX - XORB CX, 6(DI) - MOVBLZX 4(DX), CX - XORB CX, 7(DI) - MOVBLZX 11(DX),CX - XORB CX, 8(DI) - MOVBLZX 10(DX),CX - XORB CX, 9(DI) - MOVBLZX 9(DX), CX - XORB CX,10(DI) - MOVBLZX 8(DX), CX - XORB CX,11(DI) - MOVBLZX 15(DX),CX - XORB CX,12(DI) - MOVBLZX 14(DX),CX - XORB CX,13(DI) - MOVBLZX 13(DX),CX - XORB CX,14(DI) - MOVBLZX 12(DX),DX - XORB DL,15(DI) - RET - -TEXT ·sub_b(SB), NOSPLIT, $0-48 - MOVQ a+0(FP), DI - MOVQ b+24(FP), CX - MOVQ CX, DX - MOVBLZX 3(CX), CX - XORB CX, (DI) - MOVBLZX 6(DX), CX - XORB CX, 1(DI) - MOVBLZX 9(DX), CX - XORB CX, 2(DI) - MOVBLZX 12(DX),CX - XORB CX, 3(DI) - MOVBLZX 7(DX), CX - XORB CX, 4(DI) - MOVBLZX 10(DX),CX - XORB CX, 5(DI) - MOVBLZX 13(DX),CX - XORB CX, 6(DI) - MOVBLZX (DX), CX - XORB CX,7(DI) - MOVBLZX 11(DX),CX - XORB CX,8(DI) - MOVBLZX 14(DX),CX - XORB CX,9(DI) - MOVBLZX 1(DX), CX - XORB CX,10(DI) - MOVBLZX 4(DX), CX - XORB CX,11(DI) - MOVBLZX 15(DX),CX - XORB CX,12(DI) - MOVBLZX 2(DX), CX - XORB CX,13(DI) - MOVBLZX 5(DX), CX - XORB CX,14(DI) - MOVBLZX 8(DX), DX - XORB DL,15(DI) - RET - - -TEXT ·sub_c(SB), NOSPLIT, $0-32 - MOVQ a+0(FP), DI - MOVQ b+8(FP), SI - MOVQ SI, AX - MOVBLZX (SI), SI - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 1(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, (AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 2(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 1(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 3(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 2(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 4(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 3(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 5(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 4(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 6(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 5(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 7(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 6(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 8(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 7(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 9(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 8(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 10(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 9(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 11(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 10(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 12(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 11(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 13(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 12(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 14(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 13(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVBLZX 15(AX), SI - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 14(AX) - MOVL SI, CX - ANDL $15, SI - SHRL $4, CX - SHLL $4, CX - ADDL SI, CX - MOVLQSX CX, CX - MOVBLZX (DI)(CX*1), CX - MOVB CX, 15(AX) - RET - -TEXT ·sub_d(SB), NOSPLIT, $24-32 - MOVQ t+0(FP), BX - MOVQ s+8(FP), DI - MOVOU (DI), X0 - MOVOU X0, in-16(SP) - MOVQ $16, CX - ADDQ $15, DI - PUSHFQ - STD -lop: - LEAQ -1(CX), AX - XLAT - LEAQ in-16(SP)(AX*1), SI - MOVSB - LOOP lop - POPFQ - RET - -TEXT ·sub_e(SB), NOSPLIT, $0-32 - MOVQ a+0(FP), DI - MOVQ n+8(FP), SI - MOVQ $4, AX -lop: - MOVBQZX -4(SI)(AX*4), DX - MOVBQZX -3(SI)(AX*4), CX - MOVBQZX -2(SI)(AX*4), R10 - MOVBQZX -1(SI)(AX*4), R8 - LEAQ (DX)(DX*2), R9 - LEAQ (R9*2), R9 - LEAQ (CX)(CX*2), R11 - LEAQ (R11*2), R11 - LEAQ (R10)(R10*2), BX - LEAQ (BX*2), BX - MOVB DX, R13 - XORB CX, DX - XORB R10, CX - MOVB (DI)(R9*1), R12 - XORB 1(DI)(R11*1), R12 - XORB R8, R10 - XORB R12, R10 - MOVB R10, -4(SI)(AX*4) - MOVB (DI)(R11*1), R10 - XORB 1(DI)(BX*1), R10 - XORB R8, R13 - XORB R10, R13 - MOVB R13, -3(SI)(AX*4) - MOVB (DI)(BX*1), R10 - LEAQ (R8)(R8*2), R8 - LEAQ (R8*2), R8 - XORB 1(DI)(R8*1), R10 - XORB R10, DX - MOVB DX, -2(SI)(AX*4) - MOVB 1(DI)(R9*1), DX - XORB (DI)(R8*1), DX - XORB DX, CX - MOVB CX, -1(SI)(AX*4) - DECB AX - JNZ lop - RET - -TEXT sub_ab<>(SB), NOSPLIT, $0-24 - MOVQ s+0(FP), DI - MOVQ w+8(FP), SI - MOVL SI, AX - MOVL SI, CX - MOVL SI, DX - SHRL $28, AX - SHRL $24, CX - ANDL $15, CX - SALL $4, AX - ADDL CX, AX - MOVBLZX SI, CX - MOVBLZX (DI)(AX*1), AX - MOVBLZX (DI)(CX*1), CX - SALL $24, AX - ORL CX, AX - MOVL SI, CX - SHRL $8, SI - SHRL $8, CX - ANDL $15, SI - ANDL $240, CX - ADDL SI, CX - MOVBLZX (DI)(CX*1), CX - SALL $8, CX - ORL CX, AX - MOVL DX, CX - SHRL $16, DX - SHRL $16, CX - ANDL $15, DX - ANDL $240, CX - ADDL CX, DX - MOVBLZX (DI)(DX*1), DX - SALL $16, DX - ORL DX, AX - MOVQ AX, retval+16(FP) - RET - -TEXT ·sub_f(SB), NOSPLIT, $24-68 - MOVQ k+0(FP), DI - MOVQ r+8(FP), SI - MOVQ s+16(FP), DX - MOVQ $w+24(FP), CX - MOVQ CX, R10 - MOVQ SI, R9 - MOVQ DX, R8 - MOVL $4, BX - MOVL (DI), AX - BSWAPL AX - MOVL AX, (CX) - MOVL 4(DI), AX - BSWAPL AX - MOVL AX, 4(CX) - MOVL 8(DI), AX - BSWAPL AX - MOVL AX, 8(CX) - MOVL 12(DI), AX - BSWAPL AX - MOVL AX, 12(CX) - JMP inner -for: - XORL -16(R10)(BX*4), AX - MOVL AX, (R10)(BX*4) - ADDQ $1, BX - CMPQ BX, $44 - JE end -inner: - MOVL -4(R10)(BX*4), AX - TESTB $3, BX - JNE for - ROLL $8, AX - MOVQ R8, 0(SP) - MOVL AX, 8(SP) - CALL sub_ab<>(SB) - MOVQ 16(SP), AX - LEAL -1(BX), DX - SARL $2, DX - MOVLQSX DX, DX - XORL (R9)(DX*4), AX - JMP for -end: - RET - -TEXT ·sub_aa(SB), NOSPLIT, $0-56 - MOVQ i+0(FP), DI - MOVQ t+8(FP), SI - MOVQ b+16(FP), DX - MOVQ m+24(FP), CX - MOVL DI, AX - MOVLQSX DI, DI - MOVQ SI, R8 - MOVQ DX, SI - MOVBLZX (CX)(DI*1), CX - ANDL $15, AX - MOVBLZX (SI)(AX*1), SI - MOVQ AX, DX - MOVL CX, AX - SALQ $9, DX - ANDL $15, CX - SHRB $4, AX - MOVL SI, DI - ADDQ R8, DX - SALQ $4, CX - ANDL $15, AX - SHRB $4, DI - ANDL $15, SI - SALQ $4, AX - ANDL $15, DI - ADDQ DX, AX - ADDQ CX, DX - MOVBLZX (AX)(DI*1), AX - SALL $4, AX - ORB 256(SI)(DX*1), AX - MOVQ AX, retval+48(FP) - RET - -// func transformInner(x *[0x15]byte, tab *[32][16]byte) -TEXT ·transformInner(SB), NOSPLIT, $0-16 - MOVQ x+0(FP), DI - MOVQ tab+8(FP), SI - MOVQ DI, AX - MOVL $1, CX - MOVQ SI, DI - MOVQ AX, SI -lop: - MOVBLZX (SI), R8 - LEAL -1(CX), AX - ADDQ $1, SI - ANDL $31, AX - MOVL R8, DX - SALL $4, AX - ANDL $15, R8 - SHRB $4, DX - MOVBLZX DX, DX - ADDL DX, AX - CDQE - MOVBLSX (DI)(AX*1), AX - SALL $4, AX - MOVL AX, DX - MOVL CX, AX - ADDL $2, CX - ANDL $31, AX - SALL $4, AX - ADDL R8, AX - CDQE - ORB (DI)(AX*1), DX - MOVB DX, -1(SI) - CMPL CX, $43 - JNE lop - RET - -TEXT ·initState(SB), NOSPLIT, $0-64 - MOVQ c+0(FP), DI - MOVQ key+8(FP), SI - MOVQ data+32(FP), R8 - MOVQ counter+56(FP), AX - MOVOA LC0<>(SB), X0 - MOVUPS X0, (DI) - MOVOU (SI), X1 - MOVOU (DI), X3 - MOVUPS X1, 16(DI) - MOVOU 16(SI), X2 - MOVQ AX, 48(DI) - MOVUPS X2, 32(DI) - MOVQ (R8), AX - MOVUPS X3, 64(DI) - MOVQ AX, 56(DI) - MOVQ 48(DI), AX - MOVUPS X1, 80(DI) - MOVUPS X2, 96(DI) - MOVUPS X6,112(DI) - RET - -TEXT sub_ad<>(SB), NOSPLIT, $8-8 - MOVQ a+0(FP), DI - MOVQ DI, AX - MOVL 40(DI), R10 - MOVL 12(DI), R12 - MOVL 44(DI), BP - MOVL 16(DI), DX - MOVL (DI), R15 - MOVL 48(DI), R9 - MOVL 20(DI), SI - MOVL 32(DI), R11 - ADDL DX, R15 - MOVL 4(DI), R14 - MOVL 52(DI), R8 - XORL R15, R9 - MOVL 24(DI), CX - MOVL 8(DI), R13 - ROLL $16, R9 - ADDL SI, R14 - MOVL 36(DI), BX - MOVL 56(DI), DI - ADDL R9, R11 - XORL R14, R8 - ADDL CX, R13 - XORL R11, DX - ROLL $16, R8 - XORL R13, DI - ROLL $12, DX - ADDL R8, BX - ROLL $16, DI - ADDL DX, R15 - XORL BX, SI - ADDL DI, R10 - XORL R15, R9 - ROLL $12, SI - XORL R10, CX - ROLL $8, R9 - ADDL SI, R14 - ROLL $12, CX - ADDL R9, R11 - XORL R14, R8 - ADDL CX, R13 - XORL R11, DX - ROLL $8, R8 - XORL R13, DI - ROLL $7, DX - LEAL (BX)(R8*1), BX - ROLL $8, DI - MOVL DX, tmp0-8(SP) - MOVL 28(AX), DX - XORL BX, SI - MOVL BX, tmp1-4(SP) - MOVL R10, BX - MOVL 60(AX), R10 - ROLL $7, SI - ADDL DI, BX - ADDL DX, R12 - ADDL SI, R15 - XORL R12, R10 - XORL BX, CX - ROLL $16, R10 - ROLL $7, CX - ADDL R10, BP - ADDL CX, R14 - XORL BP, DX - XORL R14, R9 - ROLL $12, DX - ROLL $16, R9 - ADDL DX, R12 - XORL R12, R10 - ROLL $8, R10 - ADDL R10, BP - XORL R15, R10 - ROLL $16, R10 - XORL BP, DX - ADDL R9, BP - ADDL R10, BX - ROLL $7, DX - XORL BP, CX - XORL BX, SI - ROLL $12, SI - ADDL SI, R15 - XORL R15, R10 - MOVL R15, (AX) - ROLL $8, R10 - ADDL R10, BX - MOVL R10, 60(AX) - XORL BX, SI - MOVD BX, X1 - ROLL $7, SI - ROLL $12, CX - ADDL DX, R13 - XORL R13, R8 - ADDL CX, R14 - MOVL SI, 20(AX) - ROLL $16, R8 - XORL R14, R9 - MOVL R14, 4(AX) - ADDL R8, R11 - ROLL $8, R9 - XORL R11, DX - ADDL R9, BP - MOVL R9, 48(AX) - ROLL $12, DX - XORL BP, CX - MOVD BP, X2 - ADDL DX, R13 - ROLL $7, CX - PUNPCKLLQ X2, X1 - XORL R13, R8 - MOVL CX, 24(AX) - ROLL $8, R8 - MOVL R13, 8(AX) - ADDL R8, R11 - XORL R11, DX - MOVD R11, X0 - ROLL $7, DX - MOVL DX, 28(AX) - MOVL R8, 52(AX) - MOVL tmp0-8(SP), SI - MOVL tmp1-4(SP), CX - ADDL SI, R12 - XORL R12, DI - ROLL $16, DI - ADDL DI, CX - XORL CX, SI - MOVL SI, DX - ROLL $12, DX - ADDL DX, R12 - XORL R12, DI - MOVL R12, 12(AX) - ROLL $8, DI - ADDL DI, CX - MOVL DI, 56(AX) - MOVD CX, X3 - XORL CX, DX - PUNPCKLLQ X3, X0 - ROLL $7, DX - PUNPCKLQDQ X1, X0 - MOVL DX, 16(AX) - MOVUPS X0, 32(AX) - RET - -TEXT ·refreshState(SB), NOSPLIT, $16-8 - MOVQ i+0(FP), BX - MOVB 128(BX), CX - JE ad - SHRQ $1, CX -fr: - MOVQ BX, 0(SP) - MOVQ CX, c-8(SP) - CALL sub_ad<>(SB) - MOVQ c-8(SP), CX - MOVQ i+0(FP), BX - LOOP fr -ad: - MOVOU (BX), X0 - MOVOU 64(BX), X1 - MOVOU 80(BX), X2 - MOVOU 96(BX), X3 - PADDD X1, X0 - MOVOU 48(BX), X4 - MOVUPS X0, (BX) - MOVOU 16(BX), X0 - PADDD X2, X0 - MOVUPS X0, 16(BX) - MOVOU 32(BX), X0 - PADDD X3, X0 - MOVUPS X0, 32(BX) - MOVOU 112(BX), X0 - PADDD X4, X0 - MOVUPS X0, 48(BX) - RET - -// func tencentCrc32(tab *crc32.Table, b []byte) uint32 -TEXT ·tencentCrc32(SB), NOSPLIT, $0-40 - MOVQ tab+0(FP), DI - MOVQ bptr+8(FP), SI - MOVQ bngas+16(FP), DX - TESTQ DX, DX - JE quickend - ADDQ SI, DX - MOVL $-1, AX -lop: - MOVBLZX (SI), CX - ADDQ $1, SI - XORL AX, CX - SHRL $8, AX - MOVBLZX CX, CX - XORL (DI)(CX*4), AX - CMPQ SI, DX - JNE lop - NOTL AX - MOVQ AX, bngas+32(FP) - RET -quickend: - XORL AX, AX - RET - diff --git a/internal/encryption/t544/encryption_generic.go b/internal/encryption/t544/encryption_generic.go deleted file mode 100644 index cd3b4f0ce..000000000 --- a/internal/encryption/t544/encryption_generic.go +++ /dev/null @@ -1,112 +0,0 @@ -//go:build amd64 - -package t544 - -import ( - "encoding/binary" - "io" -) - -type encryptionData struct { - tableA [16][2][16][16]byte - tableB [16][16]byte - tableC [256][6]byte - tableD [16]byte - tableE [16]byte - tableF [15]uint32 -} - -type state struct { - state [16]uint32 // 16 - orgstate [16]uint32 // 16 - nr uint8 - p uint8 -} - -var crypto = encryptionData{ - tableA: readData[[16][2][16][16]byte]("table_a.bin"), - tableB: readData[[16][16]byte]("table_b.bin"), - tableC: readData[[256][6]byte]("table_c.bin"), - tableD: readData[[16]byte]("table_d.bin"), - tableE: readData[[16]byte]("table_e.bin"), - tableF: func() (tab [15]uint32) { - f, err := cryptoZip.Open("table_f.bin") - if err != nil { - panic(err) - } - data, err := io.ReadAll(f) - if err != nil { - panic(err) - } - for i := range tab { - tab[i] = binary.LittleEndian.Uint32(data[i*4 : (i+1)*4]) - } - return - }(), -} - -func (e *encryptionData) tencentEncryptB(p1 []byte, p2 []uint32) { - const c = 10 - for r := 0; r < 9; r++ { - sub_d(&e.tableD, p1) - sub_b(p1, p2[r*4:(r+1)*4]) - sub_c(&e.tableB, p1) - sub_e(&e.tableC, p1) - } - sub_d(&e.tableD, p1) - sub_b(p1, p2[(c-1)*4:c*4]) - sub_c(&e.tableB, p1) - sub_a(p1, p2[c*4:(c+1)*4]) -} - -func (e *encryptionData) tencentEncryptionB(c []byte, m []byte) (out [0x15]byte) { - var buf [16]byte - w := sub_f(&e.tableE, &e.tableF, &e.tableB) - - for i := range out { - if (i & 0xf) == 0 { - copy(buf[:], c) - e.tencentEncryptB(buf[:], w[:]) - for j := 15; j >= 0; j-- { - c[j]++ - if c[j] != 0 { - break - } - } - } - out[i] = sub_aa(i, &e.tableA, &buf, m) - } - - return -} - -func tencentEncryptionA(input, key, data []byte) { - var s state - s.init(key, data, 0, 20) - s.encrypt(input) -} - -func (c *state) encrypt(data []byte) { - bp := 0 - dataLen := uint32(len(data)) - for dataLen > 0 { - if c.p == 0 { - refreshState(c) - } - var sb [16 * 4]byte - for i, v := range c.state { - binary.LittleEndian.PutUint32(sb[i*4:(i+1)*4], v) - } - for c.p != 64 && dataLen != 0 { - data[bp] ^= sb[c.p] - c.p++ - bp++ - dataLen-- - } - if c.p >= 64 { - c.p = 0 - c.orgstate[12]++ - c.state = c.orgstate - } - } -} diff --git a/internal/encryption/t544/t544.go b/internal/encryption/t544/t544.go deleted file mode 100644 index 34ab5b7b9..000000000 --- a/internal/encryption/t544/t544.go +++ /dev/null @@ -1,91 +0,0 @@ -//go:build amd64 - -package t544 - -import ( - "crypto/md5" - "crypto/rc4" - "encoding/binary" - "math/rand" - - "github.com/Mrs4s/go-cqhttp/internal/encryption" -) - -const ( - keyTable = "$%&()+,-456789:?ABCDEEFGHIJabcdefghijkopqrstuvwxyz" - table2 = "!#$%&)+.0123456789:=>?@ABCDEFGKMNabcdefghijkopqrst" -) - -var ( - magic = uint64(0x6EEDCF0DC4675540) - key1 = [8]byte{'a', '$', '(', 'e', 'T', '7', '*', '@'} - key2 = [8]byte{'&', 'O', '9', '!', '>', '6', 'X', ')'} -) - -func init() { - encryption.T544Signer["8.9.35.10440"] = sign - encryption.T544Signer["8.9.38.10545"] = sign -} - -// sign t544 algorithm -// special thanks to the anonymous contributor who provided the algorithm -func sign(curr int64, input []byte) []byte { - var crcData [0x15]byte - curr %= 1000000 - binary.BigEndian.PutUint32(crcData[:4], uint32(curr)) - input = append(input, crcData[:4]...) - var kt [4 + 32 + 4]byte - r := rand.New(rand.NewSource(curr)) - for i := 0; i < 2; i++ { - kt[i] = keyTable[r.Int()%0x32] + 50 - } - kt[2] = kt[1] + 20 - kt[3] = kt[2] + 20 - key3 := kt[4 : 4+10] - k3calc := key3[2:10] - copy(k3calc, key1[:4]) - for i := 0; i < 4; i++ { - k3calc[4+i] = key2[i] ^ kt[i] - } - key3[0], key3[1] = k3calc[6], k3calc[7] - key3 = key3[:8] - k3calc[6], k3calc[7] = 0, 0 - rc4Cipher, _ := rc4.NewCipher(key3) - rc4Cipher.XORKeyStream(key3, key3) - binary.LittleEndian.PutUint64(crcData[4:4+8], magic) - tencentEncryptionA(input, kt[4:4+32], crcData[4:4+8]) - result := md5.Sum(input) - crcData[2] = 1 - crcData[4] = 1 - copy(crcData[5:9], kt[:4]) - binary.BigEndian.PutUint32(crcData[9:13], uint32(curr)) - copy(crcData[13:], result[:8]) - calcCrc := tencentCrc32(&crc32Table, crcData[2:]) - binary.LittleEndian.PutUint32(kt[4+32:4+32+4], calcCrc) - crcData[0] = kt[4+32] - crcData[1] = kt[4+32+3] - nonce := uint32(r.Int() ^ r.Int() ^ r.Int()) - on := kt[:16] - binary.BigEndian.PutUint32(on[:4], nonce) - copy(on[4:8], on[:4]) - copy(on[8:16], on[:8]) - ts.transformEncode(&crcData) - encryptedData := crypto.tencentEncryptionB(on, crcData[:]) - ts.transformDecode(&encryptedData) - output := kt[:39] - output[0] = 0x0C - output[1] = 0x05 - binary.BigEndian.PutUint32(output[2:6], nonce) - copy(output[6:27], encryptedData[:]) - binary.LittleEndian.PutUint32(output[27:31], 0) - output[31] = table2[r.Int()%0x32] - output[32] = table2[r.Int()%0x32] - addition := r.Int() % 9 - for addition&1 == 0 { - addition = r.Int() % 9 - } - output[33] = output[31] + byte(addition) - output[34] = output[32] + byte(9-addition) + 1 - binary.LittleEndian.PutUint32(output[35:39], 0) - return output -} diff --git a/internal/encryption/t544/t544_stub.go b/internal/encryption/t544/t544_stub.go deleted file mode 100644 index 361f7b4b1..000000000 --- a/internal/encryption/t544/t544_stub.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !amd64 - -package t544 - -func init() { - -} diff --git a/internal/encryption/t544/t544_test.go b/internal/encryption/t544/t544_test.go deleted file mode 100755 index 1f0f18460..000000000 --- a/internal/encryption/t544/t544_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package t544 - -import ( - "crypto/rand" - "encoding/hex" - "testing" -) - -func TestT544(t *testing.T) { - r := hex.EncodeToString(sign(0, []byte{})) - if r != "0c05d28b405bce1595c70ffa694ff163d4b600f229482e07de32c8000000003525382c00000000" { - t.Fatal(r) - } -} - -func TestCrash(t *testing.T) { - brand := make([]byte, 4096) - for i := 1; i <= 1024; i++ { - rand.Reader.Read(brand) - sign(123, brand) - } -} diff --git a/internal/encryption/t544/transform.go b/internal/encryption/t544/transform.go deleted file mode 100644 index f8bf1b080..000000000 --- a/internal/encryption/t544/transform.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build amd64 - -package t544 - -type transformer struct { - encode [32][16]byte - decode [32][16]byte -} - -func (ts *transformer) transformEncode(bArr *[0x15]byte) { - transformInner(bArr, &ts.encode) -} - -func (ts *transformer) transformDecode(bArr *[0x15]byte) { - transformInner(bArr, &ts.decode) -} - -var ts = transformer{ - encode: readData[[32][16]byte]("encode.bin"), - decode: readData[[32][16]byte]("decode.bin"), -} diff --git a/modules/config/config.go b/modules/config/config.go index 512915c0c..b438359a5 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -35,6 +35,7 @@ type Account struct { ReLogin *Reconnect `yaml:"relogin"` UseSSOAddress bool `yaml:"use-sso-address"` AllowTempSession bool `yaml:"allow-temp-session"` + SignServer string `yaml:"sign-server"` } // Config 总配置文件 diff --git a/modules/config/default_config.yml b/modules/config/default_config.yml index 02efd138f..6873052f8 100644 --- a/modules/config/default_config.yml +++ b/modules/config/default_config.yml @@ -16,6 +16,15 @@ account: # 账号相关 # 是否允许发送临时会话消息 allow-temp-session: false + # 数据包的签名服务器 + # 兼容 https://github.com/fuqiuluo/unidbg-fetch-qsign + # 如果遇到 登录 45 错误, 或者发送信息风控的话需要填入一个服务器 + # 示例: + # sign-server: 'http://127.0.0.1:8080' # 本地签名服务器 + # sign-server: 'https://signserver.example.com' # 线上签名服务器 + # 服务器可使用docker在本地搭建或者使用他人开放的服务 + sign-server: '-' + heartbeat: # 心跳频率, 单位秒 # -1 为关闭心跳