1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-07-08 15:18:32 +00:00

246 Commits

Author SHA1 Message Date
54bdd873e3 Fix typo: waring->warning (#345)
* Fix typo

* Fix typo
2024-02-26 21:46:53 +09:00
c025459da0 fix: add missing qua (app version) (#342) 2023-10-10 10:26:00 +09:00
ee8edcfe32 fix: failed get nt qq img (#343) 2023-10-10 10:25:41 +09:00
a8213e127b fix: get group honor info (#340)
* fix: get group honor info

* update: use newDecoder instead manual parse
2023-08-23 13:05:31 +08:00
e4b6dc62fd fix: security alerts 2023-08-03 23:23:00 +08:00
LXY
b4cd7e8f21 Security ci (#200)
* Create codeql-analysis.yml

* 修复:security#1

* ci: revert LBot to master
2023-08-01 10:34:08 +08:00
LXY
42148c0fbd fix: T119 (#221)
* fix: 奇怪的永真式引起的报错

* Update tlv_decoders.go

* fix: 我是憨憨

* fix: 我是憨憨

---------

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2023-08-01 10:33:41 +08:00
b49cd23484 feat: batch kick (#279)
* kick batch

* Update group_info.go

* Update client.go

* Update builders.go

---------

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2023-08-01 10:29:04 +08:00
1d0fdf6983 typo: consistent error style & grammar shark (#320) 2023-08-01 09:39:00 +08:00
65a3c56340 feat: 添加IMesssage接口并实现 (#335)
* -- 添加接口并实现:
 - 添加消息类型接口:IMessage,设定方法:获取消息元素列表,获取聊天id,获取消息文本;以privateMessage,groupMessage,TempMessage实现该接口
-- 目的:
 - 对群聊/私聊/临时消息做出回应有相同的处理逻辑时不用根据消息类型不同定义不同方法

* -- add
 - sync upstream change

* -- ci
 - sync upstream change

* Delete sync.yml

---------

Co-authored-by: GH Action - Upstream Sync <action@github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2023-08-01 09:33:55 +08:00
d344e0f318 Merge pull request #338 from 1umine/master
提供外部可调用的函数 'SendSsoPacket' 供提交回调给签名服务器
2023-07-30 21:39:47 +08:00
06bd1ed7b1 Update: add func 'SendSsoPacket' for sign's 'requestCallback' 2023-07-30 15:39:53 +08:00
19e3d17259 update transport 2023-06-27 17:08:59 +08:00
dab2c35f17 Merge pull request #337 from 6DDUU6/master
fix: nil pointer error
2023-06-10 16:27:41 +08:00
6352f22009 calc image size 2023-06-10 16:22:26 +08:00
7c68459666 fix private image size 2023-06-10 16:15:57 +08:00
190c061e69 fix: nil pointer error 2023-06-09 20:07:46 +08:00
758c7b8efa Merge pull request #333 from 6DDUU6/master
增加头部SsoSecureInfo代码实现
2023-06-05 20:52:24 +08:00
425f3e4d0c feat: pack sec head 2023-05-21 18:50:36 +08:00
8cfc2c0450 feat: SsoSecure pb field 2023-05-21 18:41:09 +08:00
22ff004663 rf: remove fixed seq 2023-04-05 20:20:28 +08:00
f8d9841755 update default protocol version & pass through app version for T544 signer 2023-04-01 15:20:48 +08:00
62d60f1322 Merge pull request #316 from tom-snow/master
完善群文件系统移动与重命名文件API
2023-03-19 17:22:03 +08:00
fdfb1e2816 Merge branch 'Mrs4s:master' into master 2023-03-19 17:18:05 +08:00
08979eb832 update android watch protocol 2023-03-19 16:50:56 +08:00
b8bef924a0 fix RequestQImei panic 2023-03-19 15:02:33 +08:00
fd83d24f67 improve t544 implementation 2023-03-18 00:28:54 +08:00
3a3355e00a fix: transport miss qimei 2023-03-17 23:23:09 +08:00
b461f356a3 ci(chore): Fix stylings 2023-03-15 23:15:50 +00:00
1c6552c19f Merge pull request #322 from lunzhiPenxil/patch-1
修正GenIMEI
2023-03-16 07:15:27 +08:00
4b7a854307 Merge pull request #324 from icarus-ai/patch-1
fix: uptimes, 距离当前时间戳最多14400秒
2023-03-16 07:14:57 +08:00
a075c15b69 fix: uptimes, 距离当前时间戳最多14400秒
86400也会导致q16 fde9508748b00283b2
2023-03-15 23:54:41 +08:00
1045201c7e Merge pull request #323 from icarus-ai/master
fix: uptimes
2023-03-15 23:25:37 +08:00
7cecf01ed2 fix: uptimes
go time januay=1
时间过久可能返回一个固定的q16 fde9508748b00283b2
2023-03-15 23:20:34 +08:00
5e7ade149f improve AppVersion display 2023-03-15 02:15:50 +08:00
933005a6aa Merge branch 'Mrs4s:master' into master 2023-03-13 19:15:59 +08:00
a43d46ba57 修正GenIMEI 2023-03-13 16:01:42 +08:00
71e86b9b3d rename: AppVersion.ReadJson -> AppVersion.UpdateFromJson 2023-03-13 01:23:04 +08:00
653b853ca5 Merge pull request #321 from zhaodice/patch-1
使逻辑更加科学(x)
2023-03-12 22:12:22 +08:00
a277c77cb5 Merge branch 'Mrs4s:master' into master 2023-03-12 21:22:37 +08:00
7d2150c2d7 使逻辑更加科学(x) 2023-03-12 13:16:30 +08:00
80b6b28436 fix t545 2023-03-11 01:08:00 +08:00
365235008b improve t544 2023-03-11 01:07:26 +08:00
cdc207867e implements qimei & fix t545 2023-03-11 00:37:31 +08:00
692fc0f6ea upload file 2023-03-10 21:59:35 +08:00
30c24fa5b2 fix typo 2023-03-10 21:57:02 +08:00
85129aa06c support manual update AppVersion 2023-03-10 21:54:44 +08:00
1e21944e12 fix typo 2023-03-10 17:21:22 +08:00
74757b6b7b fix typo 2023-03-10 17:20:39 +08:00
9760f01de1 fix login 2023-03-10 03:56:17 +08:00
adae194961 fix version 2023-03-10 02:17:06 +08:00
59abbee92a Merge pull request #319 from icarus-ai/master
添加事件: 群解散, 删除好友
2023-03-10 01:18:54 +08:00
8028aaedd2 添加事件
add: 群解散事件
add: 被删除好友事件
2023-03-08 23:29:40 +08:00
fee6c23736 fix build time 2023-03-08 01:29:29 +08:00
a42218b851 fix oicq subcmd 7 tlv 2023-03-05 18:22:25 +08:00
b8f6dadd53 update protocol version 2023-03-05 17:59:02 +08:00
7c54342c9f feat: tlv544 2023-03-05 17:58:28 +08:00
b75252bbaa fix code2d trans header 2023-03-05 00:34:56 +08:00
0cca66b23b 完善群文件系统移动与重命名文件API 2023-02-24 16:26:34 +08:00
Lin
5a89d8a9bf fix query qrcode (#315) 2023-02-23 17:35:28 +08:00
dfd10f4405 client: fix fetch qrcode 2023-02-23 16:29:15 +08:00
af032dec96 client/internal/highway: auto switch multi-thread upload 2023-02-20 15:05:19 +08:00
e7a34fdd90 client/internal/highway: use pool for multithread upload 2023-02-19 23:06:15 +08:00
3b97ce341b client/internal/highway: shorten lock time 2023-02-19 19:59:54 +08:00
4716f69c1a client/internal/highway: fix typo 2023-02-19 19:42:26 +08:00
da29660b3e client/internal/highway: use connection pool 2023-02-19 19:39:19 +08:00
109b1640b8 client/internal/auth: desugar MainSigMap 2023-02-19 15:38:01 +08:00
068fa5b271 client: avoid alloc in Protocol.Version() 2023-02-19 13:08:32 +08:00
fd37850f8c client: default use android pad protocol 2023-02-19 12:27:22 +08:00
1ec3bbf188 client: use TCP method to upload group file 2023-02-17 18:01:43 +08:00
f630205782 client: use UploadBDH for uploading forward message 2023-02-16 21:31:04 +08:00
ed15fbca26 client: move device&version into c.transport 2023-02-16 18:40:56 +08:00
cfd7d92d1f client: remove SystemDeviceInfo [break] 2023-02-14 23:23:09 +08:00
3ff1fee1b6 client: remove some unused pb field
remove 64KiB from go-cqhttp.
2023-02-13 21:26:55 +08:00
993ec128a0 all: fix some issue reported by golangci-lint 2023-02-13 15:33:55 +08:00
2156430a24 message: update new face 2023-02-13 00:17:21 +08:00
96d46f63d2 client: remove payload parameter in decoder function
replace with (*network.Packet).Payload
2023-02-11 20:49:33 +08:00
481c830f56 client: fix electron qq url 2023-02-11 20:14:09 +08:00
841fef387a internal/proto: re-use map[uint64]any
use slice will cause some panic.
2023-02-11 18:01:19 +08:00
733944693c client/internal/network: unify IncomingPacket&IncomingPacketInfo 2023-02-11 17:03:35 +08:00
1a7fcd76cf internal/crypto: move to client/internal/oicq 2023-02-11 16:21:29 +08:00
94b761717e internal/packets: remove
move to other packages.
2023-02-11 15:35:58 +08:00
7f557c197c client: remove (*QQClient).UseHighwayMessage 2023-02-10 23:39:12 +08:00
aa5a8c45fc client/internal/highway: allow retry other server 2023-02-10 23:18:46 +08:00
3bfc20fd2e client: remove deprecated image upload api 2023-02-10 22:14:17 +08:00
a8d1e40a27 client: try fix panic in decodeMultiApplyDownResponse 2023-02-10 21:36:12 +08:00
e9f4d33002 client: don't use math/rand.Read
This function has been deprecated.
2023-02-10 21:03:21 +08:00
f56bcbe326 client: implement query personal sign 2023-02-09 22:28:32 +08:00
727686e453 client: fix DynamicMessage usage 2023-02-08 21:23:16 +08:00
bdb083558b internal/proto: ensure dynamic message key incremental 2023-02-08 14:42:14 +08:00
d9cc29c678 修复越界 (#314)
* 修复utils.B2S越界
2023-02-08 11:35:29 +08:00
91f9576e48 .workflow: fix go version 2023-02-06 20:29:51 +08:00
f33123fcf6 all: bump go 1.20 2023-02-06 20:28:03 +08:00
4b6eff7c64 client: fix incorrect proto unmarshal usage 2023-02-04 12:46:33 +08:00
15069b74b3 support SetGroupAnonymous (#312) 2023-01-31 17:05:43 +08:00
8e70b22d1c Merge pull request #309 from Yukari316/master
feat: 发送群公告返回公告id
2023-01-03 21:01:53 +08:00
4e339ff6b3 feat: 发送群公告返回公告id 2023-01-03 20:15:50 +08:00
f199516611 Merge pull request #297 from littlecxm/master
chore: misc
2022-12-27 13:50:49 +08:00
592ca2358f Merge pull request #305 from super1207/master
修复群聊语音接收
2022-12-11 00:25:01 +08:00
5fca4cecf1 尝试修复群聊语音接收 2022-12-09 15:56:41 +08:00
cf66e28647 fix protocol 2022-12-07 03:31:01 +08:00
4658474c60 internal/crypto: fix build with go1.20 2022-12-02 14:07:17 +08:00
4deeac15f6 fix stringer 2022-12-01 18:17:36 +08:00
23f637d295 fix: guild event flow decode error 2022-12-01 17:07:48 +08:00
a30750849d fix: update protocol version & add aPad protocol 2022-12-01 17:07:00 +08:00
482a60ad84 send message error 46 2022-12-01 03:03:21 +08:00
64722799b7 client/entities: stringfy const type LoginError (#300) 2022-11-02 15:19:37 +08:00
b291cf2896 Merge pull request #299 from weilinfox/master
试图修复 IPad 协议登录
2022-10-29 21:11:51 +08:00
18cc29b241 Fix IPad Protocol login error 2022-10-28 15:44:29 +08:00
d3b8c5f700 internal/proto: add SInt[32|64]
also simplify some code.
2022-09-25 16:26:53 +08:00
8aa1683866 internal/crypto: use crypto/ecdh in go1.20 2022-09-25 00:10:26 +08:00
CXM
b053819391 chore: misc 2022-09-08 16:08:39 +08:00
CXM
b66dd94317 chore: readme 2022-09-08 16:08:29 +08:00
e657427abd message: add String for SourceType 2022-08-31 21:59:48 +08:00
a3c348100d client/internal/oicq: clean up 2022-08-28 17:01:50 +08:00
63cd911a25 Merge pull request #295 from dmlgzs/master
修复 MacOS 协议
2022-08-27 23:43:14 +08:00
8999883d92 修复 MacOS 协议
新 SubAppId 来自 https://github.com/takayama-lily/oicq/pull/418
2022-08-27 23:39:56 +08:00
08ca3b023a deprecate CAS in favor of CompareAndSwap (#291)
* deprecate CAS in favor of CompareAndSwap

* ci: update to go1.19

* 为 Statistics 实现 MarshalJSON 接口 (See 2d79fcc)
2022-08-24 14:22:36 +08:00
f4a9a63c8b fix: group file folders nil pointer (#290) 2022-08-21 23:04:04 +08:00
5c0e2c5773 fix: 修复读取合并转发私聊内容报错 (#287) 2022-07-20 20:40:26 +08:00
a39b3fdd96 Merge pull request #284 from Akegarasu/feat-del-group-notice
feat: 删除公告
2022-07-01 00:01:33 +08:00
af1358465c feat: 删除公告
feat: 获取公告现在会返回notice_id(fid) 用来删除公告
fix: “发送给新成员”的公告无法正常获取
2022-06-30 17:22:46 +08:00
e26832b72d fix panic on upload unknown target image 2022-06-24 20:14:27 +08:00
5d277ab45b dep: update protobuf 2022-06-24 11:05:19 +08:00
24ee0103e7 client: add VipLevel in SummaryCard 2022-06-22 09:57:46 +08:00
ae8c187aa5 fix t193 encode error 2022-06-21 16:30:50 +08:00
3fb9f40095 client: fix submit ticket 2022-06-21 15:36:58 +08:00
d1a66c42b6 client: fix upload private file 2022-06-21 13:53:18 +08:00
1336167b9c client: refactor pack tlv 2022-06-21 13:49:11 +08:00
6f4f491002 client: fix Android version 2022-06-21 11:52:16 +08:00
0ce160e357 client: update Android version 2022-06-20 22:28:42 +08:00
d09215e943 client: UploadFile support private message 2022-06-20 11:24:56 +08:00
6dc99526ee client: change use _UploadFile to upload group file 2022-06-20 10:16:19 +08:00
2a1fd324ca client: add FileResource 2022-06-19 22:59:20 +08:00
c92096e7ae fix sign packet error (#278)
* fix sign packet error

* Update sign.go
2022-06-19 15:18:41 +08:00
9b6c4ddf2f client: hoist highway input in uploading forward message 2022-06-18 23:53:35 +08:00
6e1792c6b1 client: don't return typed nil in UploadImage 2022-06-18 23:12:10 +08:00
3f5174dda1 highway: fix upload exciting 2022-06-15 20:28:56 +08:00
87ff2f9591 fix panic while uploading existing guild image 2022-06-14 17:37:22 +08:00
b56c61f5b4 Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2022-06-06 22:25:26 +08:00
c8397f2e94 temporary disable OnlinePush.SidTicketExpired packet handler 2022-06-06 22:24:46 +08:00
831b36ef76 feat: add a debug mode for internal/proto.Marshal 2022-06-05 17:32:11 +08:00
ae33763fe1 dep: update RomiChan/protobuf 2022-06-05 16:53:05 +08:00
007f228e10 ci(chore): Fix stylings 2022-05-24 05:31:30 +00:00
1288c30775 添加群打卡接口 (看起来是这个包 (#273)
* group sign

* group sign

* rename pb file 0xeb7

* new proto impl
2022-05-24 13:31:07 +08:00
52148a8ddf fix wrong out path 2022-05-24 11:31:15 +08:00
6054d53318 update protobuf/Makefile 2022-05-24 11:18:10 +08:00
5b616d65f7 all: adapt new proto2 api 2022-05-24 09:59:58 +08:00
59db392118 ci(chore): Fix stylings 2022-05-24 01:55:56 +00:00
ae36149502 add group_id in forward msg node (#275) 2022-05-24 09:55:33 +08:00
76c0d90577 client: delete Memo in GroupInfo&support string intern 2022-05-23 12:18:22 +08:00
a9a08dbb3a client: delete SpecialTitleExpireTime field in GroupMemberInfo 2022-05-23 12:01:38 +08:00
b28ec81f54 fix: fix go vet error 2022-05-23 11:06:51 +08:00
cb56240978 client: add option to highway message 2022-05-23 11:02:25 +08:00
2b2be6693d client,binary: minor changes 2022-05-23 10:28:50 +08:00
026b01c440 Merge pull request #274 from icarus-ai/patch-3
genLongTemplate 非ASCII字符串长度计数错误
2022-05-17 16:48:22 +08:00
fc9a403abf genLongTemplate 非ASCII字符串长度计数错误
一个字切成半个啦😭
2022-05-17 16:08:17 +08:00
62b5678c94 Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2022-05-14 23:59:57 +08:00
802f679783 fix: unidirectional friend unmarshal error 2022-05-14 23:59:35 +08:00
fba6bc90e2 Merge pull request #272 from fumiama/delay
fix #245: tcping error handling
2022-05-10 19:35:36 +08:00
80f4bbae3f fix #245: tcping error handling 2022-05-10 19:28:39 +08:00
4164d656d8 fix: temporary fix qidian protocol panic 2022-05-10 18:20:23 +08:00
27c591e3cb fix: update qidian protocol version 2022-05-10 04:03:08 +08:00
0f43de570f Merge pull request #253 from lz1998/t106
fix t106
2022-05-09 22:25:00 +08:00
4ff7c080d8 Merge pull request #247 from lz1998/fix-group-code2uin
fix ToGroupUin, ToGroupCode
2022-05-09 22:24:33 +08:00
8b3c2a9b71 Merge pull request #255 from buhuang28/buhuang-dev
fix t1 time format
2022-05-09 22:24:09 +08:00
2ccf935230 Merge pull request #267 from MingxuanGame/master
fix: receiver is 0 at the private poke event by MacOS protocol
2022-05-09 22:23:50 +08:00
c0be2d9698 typo: rename TCPListener to TCPClient 2022-05-09 22:06:55 +08:00
d2fe00e2bc feat: subscribable event handler 2022-05-09 22:03:27 +08:00
a4cf95f793 Merge pull request #256 from fumiama/master
fix: send message to self error
2022-04-28 23:55:02 +08:00
1b7e3d8580 binary: better bce for NewTeaCipher 2022-04-14 21:37:04 +08:00
75be9b870a Merge pull request #1 from MingxuanGame/dev
Merge dev into master
2022-04-10 20:51:55 +08:00
dc9e4254ec fix: receiver is 0 at MacOS protocol 2022-04-10 20:47:53 +08:00
7057431309 Merge branch 'Mrs4s:master' into master 2022-04-06 11:19:25 +08:00
6e7053381e Merge pull request #266 from Sora233/SendGroupMessage_fallthrough
send group message fallthrough in case of error
2022-04-05 22:45:58 +08:00
9cb9e80d99 client: fix forward 2022-04-05 21:47:34 +08:00
02db17e660 send group message fallthrough in case of error 2022-04-05 01:52:30 +08:00
03f3d34e3e ci: add static check 2022-04-04 21:37:50 +08:00
0fd0b02e41 Merge branch 'Mrs4s:master' into master 2022-03-29 17:08:48 +08:00
e2a42e5425 client: seek start 0 before upload 2022-03-28 15:27:59 +08:00
4f05838b6c fix nil in multi reader 2022-03-28 13:35:58 +08:00
33590e4b32 highway: rename BdhInput and Input to Transaction 2022-03-28 13:13:06 +08:00
9422ce4751 all: optimize
detected by go-perfguard
2022-03-27 22:42:48 +08:00
868828f3da 注释client251行println (#264)
谁忘了注释这个println(≖_≖ )
2022-03-23 12:48:57 +08:00
665c6acf02 client: use net/netip
netip包中的数据结构更省内存,而且不需要堆分配
2022-03-22 22:44:37 +08:00
4314fdcb39 binary: avoid alloc in reading integer 2022-03-22 21:34:05 +08:00
aa657c0f09 dep: update syncx 2022-03-21 21:39:15 +08:00
8b86fe4d9c client: refactor forward message api 2022-03-21 21:37:12 +08:00
38990f6e1c client: use generic version sync.Map 2022-03-20 15:07:54 +08:00
3dc7dc4fdd client: use buffered channel
this channel use receive with timeout, so we need use buffered to avoid memory leak.
2022-03-20 13:55:08 +08:00
714961d68f client: use %x placeholder 2022-03-20 13:48:02 +08:00
436482c7db ci(chore): Fix stylings 2022-03-19 17:06:33 +00:00
79f34a47b8 简化URL处理逻辑 (#263)
* update image url

* %X
2022-03-20 01:06:05 +08:00
5903226f25 client: remove some err check
`(*bytes.Buffer).Write` always return nil error, so we don't need error check
2022-03-19 10:03:37 +08:00
2dfc8f0cbc delete useless code (#262) 2022-03-18 23:22:11 +08:00
35f774b86a client: fix wrong event dispatch 2022-03-18 22:51:14 +08:00
87e53dd6e2 client: update ShutUpTimestamp when receive mute event 2022-03-18 21:32:22 +08:00
bc4e9f3c95 client: delete GetVipInfo
This api is broken.
2022-03-18 21:14:45 +08:00
8fa49fedb9 feat: support get group announcement (#258)
* feat: support get group announcement

* refactor: avoid importing new dependent package

* refactor: prettify
2022-03-18 20:36:13 +08:00
f5950d72fa dep: update protobuf 2022-03-18 19:34:35 +08:00
d185826bee utils: avoid importing encoding/xml package 2022-03-18 17:35:44 +08:00
f7ced299d9 Update goimports.yml 2022-03-17 16:59:41 +08:00
6d84141b8d Merge pull request #260 from Mrs4s/typeparam
all: update to go1.18 with generic
2022-03-17 16:57:21 +08:00
4a007cfcf9 all: rewrite interface{} to any
generated by `gofmt -w -r 'interface{} -> any' .`
2022-03-17 10:52:03 +08:00
ea0237538a ci: update to go1.18 2022-03-16 14:03:51 +08:00
dc9ecd65ea refactor: delete tag 14 in jce of getSSOAddress (#257)
synchronize with source code
2022-03-12 11:55:31 +08:00
764a5a0c77 fix: 合并转发 (#259)
似在commit e287cbfabd0438955a7415a430f3ebfb3762cfca的时候复制粘贴错了 xml格式不符导致合并转发发送失败?
228行 群聊的聊天记录 改成聊天记录能发了
2022-03-12 11:55:14 +08:00
348e317a34 client: shrink event handler slice 2022-03-02 21:41:46 +08:00
7699c32258 client: only log when logger is non-nil 2022-03-02 18:03:08 +08:00
abb3709a18 client: replace LogEvent with Logger 2022-03-01 22:52:22 +08:00
a7098f6000 client: use packOIDBPackageProto 2022-03-01 22:03:08 +08:00
ef65fd67e6 client: use unpackOIDBPackage 2022-03-01 16:45:01 +08:00
d9f803837f client: use generic event handler 2022-03-01 16:20:18 +08:00
ab2ee60132 ci: use unstable toolchain 2022-03-01 15:05:23 +08:00
d900412d41 ci: adjust go toolchain version 2022-03-01 15:04:12 +08:00
81fe9e85d0 ci: enable ci check 2022-03-01 15:02:17 +08:00
6bc03d6b8c utils: port ttl cache to generic 2022-03-01 15:00:33 +08:00
15a746802b network,highway: move HeadBodyFrame to highway
This code only used by highway
2022-03-01 13:39:27 +08:00
9884d9b0de dep: update protobuf 2022-02-27 21:55:20 +08:00
76539e425d client: avoid ReadAll in image ocr 2022-02-27 16:52:57 +08:00
ca4580dad5 highway: refactor multi thread uploading 2022-02-27 16:46:11 +08:00
e6ad62569d Revert "highway: use io.ReadSeeker and disable multi-thread upload"
This reverts commit a2d65a2bb6.

This commit break video uploading.
2022-02-27 14:14:18 +08:00
a2d65a2bb6 highway: use io.ReadSeeker and disable multi-thread upload 2022-02-25 22:18:52 +08:00
3555d23136 network: remove unnecessary sync.Pool 2022-02-25 21:21:21 +08:00
df63c994e4 fix: send message to self error 2022-02-24 20:45:15 +08:00
c515024783 highway: reduce memcpy, memalloc by using net.Buffers 2022-02-23 22:19:01 +08:00
e287cbfabd client: clean genForwardTemplate argument 2022-02-23 21:09:08 +08:00
fb2d3d28f0 client: remove similar code 2022-02-23 19:28:02 +08:00
c9784ffce8 fix t1 time format 2022-02-22 20:25:34 +08:00
421755d938 client: add json tag to statistics 2022-02-22 13:25:16 +08:00
6b5e7d35f0 client: new API UploadImage, UploadVoice and deprecate old API 2022-02-21 23:35:44 +08:00
1e32793eef client: combine upload group video and guild video 2022-02-21 17:31:05 +08:00
9ef6c3cf3a feat: set profile detail (#254)
* feat: profile detail

* struct: ProfileDetailUpdate

* ProfileDetailUpdate

* update profile
2022-02-21 00:22:45 +08:00
5e8a512698 client: allow multi-thread upload without cache file
Delete `UploadGroupImageByFile` since `UploadGroupImage` can multi-thread upload
2022-02-20 23:26:07 +08:00
c17b9b73c6 fix t106 2022-02-20 19:15:54 +08:00
32ef91dd32 feat: set qq nickname (#252)
* feat: set qq nickname

* fix: pretty
2022-02-20 18:57:08 +08:00
bd6f8947ef client: minor update 2022-02-18 18:13:55 +08:00
0578942d86 Revert "fix reply seq"
This reverts commit 6692c4acb8.
2022-02-18 14:57:47 +08:00
0e68a1e7b7 client: merge fragmented private message 2022-02-14 16:27:17 +08:00
d53bf8503e client/pb: re-generate proto files 2022-02-14 00:51:36 +08:00
8a518ee96c dep: drop github.com/klauspost/compress 2022-02-13 17:08:11 +08:00
6692c4acb8 fix reply seq 2022-02-12 16:16:37 +08:00
28637c41be internal/proto: move binary.DynamicProtoMessage to proto.DynamicMessage 2022-02-10 21:56:40 +08:00
5d071b034c fix event flow panic 2022-02-09 17:25:29 +08:00
8258176fba Add MarketFaceElement case to message.ToString() (#241) 2022-02-07 19:51:33 +08:00
a8788e826f fix ToGroupUin, ToGroupCode 2022-01-22 01:29:55 +08:00
232 changed files with 10710 additions and 41004 deletions

70
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '27 14 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -2,9 +2,9 @@ name: Go
on: on:
push: push:
branches: [ master ] branches: [ master, typeparam ]
pull_request: pull_request:
branches: [ master ] branches: [ master, typeparam ]
jobs: jobs:
@ -16,7 +16,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: ^1.13 go-version: '1.20'
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@ -14,7 +14,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: ^1.13 go-version: 1.19
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v2 uses: actions/checkout@v2

52
Makefile Normal file
View File

@ -0,0 +1,52 @@
.PHONY: protoc-gen-golite-version clean install-protoc-plugin proto
.DEFAULT_GOAL := proto
PROTO_DIR=client/pb
PROTO_OUTPUT_PATH=client
PROTO_IMPORT_PATH=client
PROTO_FILES := \
$(PROTO_DIR)/*.proto \
$(PROTO_DIR)/channel/*.proto \
$(PROTO_DIR)/cmd0x3f6/*.proto \
$(PROTO_DIR)/cmd0x6ff/*.proto \
$(PROTO_DIR)/cmd0x346/*.proto \
$(PROTO_DIR)/cmd0x352/*.proto \
$(PROTO_DIR)/cmd0x388/*.proto \
$(PROTO_DIR)/exciting/*.proto \
$(PROTO_DIR)/faceroam/*.proto \
$(PROTO_DIR)/highway/*.proto \
$(PROTO_DIR)/longmsg/*.proto \
$(PROTO_DIR)/msf/*.proto \
$(PROTO_DIR)/msg/*.proto \
$(PROTO_DIR)/msgtype0x210/*.proto \
$(PROTO_DIR)/multimsg/*.proto \
$(PROTO_DIR)/notify/*.proto \
$(PROTO_DIR)/oidb/*.proto \
$(PROTO_DIR)/profilecard/*.proto \
$(PROTO_DIR)/pttcenter/*.proto \
$(PROTO_DIR)/qweb/*.proto \
$(PROTO_DIR)/richmedia/*.proto \
$(PROTO_DIR)/structmsg/*.proto \
$(PROTO_DIR)/web/*.proto
PROTOC_GEN_GOLITE_VERSION := \
$(shell grep "github.com/RomiChan/protobuf" go.mod | awk -F v '{print "v"$$2}')
protoc-gen-golite-version:
@echo "Use protoc-gen-golite version: $(PROTOC_GEN_GOLITE_VERSION)"
clean:
find . -name "*.pb.go" | xargs rm -f
install-protoc-plugin: protoc-gen-golite-version
go install github.com/RomiChan/protobuf/cmd/protoc-gen-golite@$(PROTOC_GEN_GOLITE_VERSION)
proto: install-protoc-plugin
protoc --golite_out=$(PROTO_OUTPUT_PATH) --golite_opt=paths=source_relative -I=$(PROTO_IMPORT_PATH) $(PROTO_FILES)
fmt:
go vet -stdmethods=false ./...
.EXPORT_ALL_VARIABLES:
GO111MODULE = on

View File

@ -1,22 +1,34 @@
# MiraiGo # MiraiGo
qq-android协议的golang实现 移植于mirai qq-android 协议的golang实现 移植于 [mirai](https://github.com/mamoe/mirai)
# 警告 ## 使用前声明
本项目为协议实现,api非常原始不推荐使用。 本项目为协议实现,不推荐直接使用。
建议基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 使用框架开发。 CQHTTP 用户建议使用基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 框架开发。
同时提供不基于 CQHTTP 的原生框架 [MiraiGo-Template](https://github.com/Logiase/MiraiGo-Template) 进行开发。 同时提供原生框架 [MiraiGo-Template](https://github.com/Logiase/MiraiGo-Template) 进行开发。
# 已完成功能/开发计划 ## 使用方法
#### 登录
```bash
go get -u github.com/Mrs4s/MiraiGo
```
## 支持的功能
## 协议支持
<details>
<summary>已完成功能/开发计划列表</summary>
**登录**
- [x] 账号密码登录 - [x] 账号密码登录
- [x] 二维码登录 - [x] 二维码登录
- [x] 验证码提交 - [x] 验证码提交
- [x] 设备锁验证 - [x] 设备锁验证
- [x] 错误信息解析 - [x] 错误信息解析
#### 消息类型 **消息类型**
- [x] 文本 - [x] 文本
- [x] 图片 - [x] 图片
- [x] 语音 - [x] 语音
@ -30,14 +42,14 @@ qq-android协议的golang实现 移植于mirai
- [x] 合并转发 - [x] 合并转发
- [x] 群文件(上传与接收信息) - [x] 群文件(上传与接收信息)
#### 事件 **事件**
- [x] 好友消息 - [x] 好友消息
- [x] 群消息 - [x] 群消息
- [x] 临时会话消息 - [x] 临时会话消息
- [x] 登录号加群 - [x] 登录号加群
- [x] 登录号退群(包含T出) - [x] 登录号退群(包含T出)
- [x] 新成员进群/退群 - [x] 新成员进群/退群
- [x] 群/好友消息撤回 - [x] 群/好友消息撤回
- [x] 群禁言 - [x] 群禁言
- [x] 群成员权限变更 - [x] 群成员权限变更
- [x] 收到邀请进群通知 - [x] 收到邀请进群通知
@ -45,10 +57,11 @@ qq-android协议的golang实现 移植于mirai
- [x] 新好友 - [x] 新好友
- [x] 新好友请求 - [x] 新好友请求
- [x] 客户端离线 - [x] 客户端离线
- [x] 群提示 (戳一戳/运气王等) - [x] 群提示 (戳一戳/运气王等)
#### 主动操作 **主动操作**
> 为防止滥用,将不支持主动邀请新成员进群
_为防止滥用不支持主动邀请新成员进群_
- [x] 发送群消息 - [x] 发送群消息
- [x] 发送好友消息 - [x] 发送好友消息
@ -72,11 +85,14 @@ qq-android协议的golang实现 移植于mirai
- [x] 戳一戳群友 - [x] 戳一戳群友
- [x] 获取陌生人信息 - [x] 获取陌生人信息
#### 敏感操作 </details>
> 由于[QQ钱包支付用户服务协议](https://www.tenpay.com/v2/html5/basic/public/agreement/protocol_mqq_pay.shtml), 将不支持一切有关QQ钱包的协议
### 不支持的协议
**基于 [QQ钱包支付用户服务协议](https://www.tenpay.com/v2/html5/basic/public/agreement/protocol_mqq_pay.shtml) 不支持一切有关QQ钱包的协议**
>4.13 您不得利用本服务实施下列任一的行为: >4.13 您不得利用本服务实施下列任一的行为:
>\ >\
> 9 **侵害QQ钱包支付服务系統** > 9 **侵害QQ钱包支付服务系統**
- [ ] ~~QQ钱包协议(收款/付款等)~~ - [ ] ~~QQ钱包协议(收款/付款等)~~

View File

@ -21,10 +21,10 @@ func NewJceReader(data []byte) *JceReader {
return &JceReader{buf: data} return &JceReader{buf: data}
} }
func (r *JceReader) readHead() (hd HeadData, l int) { func (r *JceReader) readHead() HeadData {
hd, l = r.peakHead() hd, l := r.peakHead()
r.off += l r.off += l
return return hd
} }
func (r *JceReader) peakHead() (hd HeadData, l int) { func (r *JceReader) peakHead() (hd HeadData, l int) {
@ -49,7 +49,7 @@ func (r *JceReader) skipHead() {
} }
func (r *JceReader) skip(l int) { func (r *JceReader) skip(l int) {
r.skipBytes(l) r.off += l
} }
func (r *JceReader) skipField(t byte) { func (r *JceReader) skipField(t byte) {
@ -86,7 +86,7 @@ func (r *JceReader) skipField(t byte) {
} }
func (r *JceReader) skipNextField() { func (r *JceReader) skipNextField() {
hd, _ := r.readHead() hd := r.readHead()
r.skipField(hd.Type) r.skipField(hd.Type)
} }
@ -105,17 +105,6 @@ func (r *JceReader) readBytes(n int) []byte {
return b return b
} }
func (r *JceReader) skipBytes(n int) {
if r.off+n > len(r.buf) {
panic("skipBytes: EOF")
}
lremain := len(r.buf[r.off:])
if lremain < n {
n = lremain
}
r.off += n
}
func (r *JceReader) readByte() byte { func (r *JceReader) readByte() byte {
if r.off >= len(r.buf) { if r.off >= len(r.buf) {
panic("readByte: EOF") panic("readByte: EOF")
@ -126,15 +115,21 @@ func (r *JceReader) readByte() byte {
} }
func (r *JceReader) readUInt16() uint16 { func (r *JceReader) readUInt16() uint16 {
return goBinary.BigEndian.Uint16(r.readBytes(2)) b := make([]byte, 2)
r.off += copy(b, r.buf[r.off:])
return goBinary.BigEndian.Uint16(b)
} }
func (r *JceReader) readUInt32() uint32 { func (r *JceReader) readUInt32() uint32 {
return goBinary.BigEndian.Uint32(r.readBytes(4)) b := make([]byte, 4)
r.off += copy(b, r.buf[r.off:])
return goBinary.BigEndian.Uint32(b)
} }
func (r *JceReader) readUInt64() uint64 { func (r *JceReader) readUInt64() uint64 {
return goBinary.BigEndian.Uint64(r.readBytes(8)) b := make([]byte, 8)
r.off += copy(b, r.buf[r.off:])
return goBinary.BigEndian.Uint64(b)
} }
func (r *JceReader) readFloat32() float32 { func (r *JceReader) readFloat32() float32 {
@ -156,10 +151,10 @@ func (r *JceReader) skipToTag(tag int) bool {
} }
func (r *JceReader) skipToStructEnd() { func (r *JceReader) skipToStructEnd() {
hd, _ := r.readHead() hd := r.readHead()
for hd.Type != 11 { for hd.Type != 11 {
r.skipField(hd.Type) r.skipField(hd.Type)
hd, _ = r.readHead() hd = r.readHead()
} }
} }
@ -167,7 +162,7 @@ func (r *JceReader) ReadByte(tag int) byte {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return 0 return 0
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 12: case 12:
return 0 return 0
@ -186,7 +181,7 @@ func (r *JceReader) ReadInt16(tag int) int16 {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return 0 return 0
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 12: case 12:
return 0 return 0
@ -203,7 +198,7 @@ func (r *JceReader) ReadInt32(tag int) int32 {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return 0 return 0
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 12: case 12:
return 0 return 0
@ -222,7 +217,7 @@ func (r *JceReader) ReadInt64(tag int) int64 {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return 0 return 0
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 12: case 12:
return 0 return 0
@ -243,7 +238,7 @@ func (r *JceReader) ReadFloat32(tag int) float32 {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return 0 return 0
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 12: case 12:
return 0 return 0
@ -258,7 +253,7 @@ func (r *JceReader) ReadFloat64(tag int) float64 {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return 0 return 0
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 12: case 12:
return 0 return 0
@ -275,7 +270,7 @@ func (r *JceReader) ReadString(tag int) string {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return "" return ""
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 6: case 6:
return utils.B2S(r.readBytes(int(r.readByte()))) return utils.B2S(r.readBytes(int(r.readByte())))
@ -290,7 +285,7 @@ func (r *JceReader) ReadBytes(tag int) []byte {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -311,7 +306,7 @@ func (r *JceReader) ReadByteArrArr(tag int) (baa [][]byte) {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -378,7 +373,7 @@ func (r *JceReader) ReadJceStruct(obj IJceStruct, tag int) {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return return
} }
hd, _ := r.readHead() hd := r.readHead()
if hd.Type != 10 { if hd.Type != 10 {
return return
} }
@ -390,7 +385,7 @@ func (r *JceReader) ReadMapStrStr(tag int) map[string]string {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 8: case 8:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -408,7 +403,7 @@ func (r *JceReader) ReadMapStrByte(tag int) map[string][]byte {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 8: case 8:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -422,11 +417,37 @@ func (r *JceReader) ReadMapStrByte(tag int) map[string][]byte {
} }
} }
func (r *JceReader) ReadMapIntVipInfo(tag int) map[int]*VipInfo {
if !r.skipToTag(tag) {
return nil
}
r.skipHead()
hd := r.readHead()
switch hd.Type {
case 8:
s := r.ReadInt32(0)
m := make(map[int]*VipInfo, s)
for i := 0; i < int(s); i++ {
k := r.ReadInt64(0)
v := new(VipInfo)
r.readHead()
v.ReadFrom(r)
r.skipToStructEnd()
m[int(k)] = v
}
r.skipToStructEnd()
return m
default:
r.skipToStructEnd()
return nil
}
}
func (r *JceReader) ReadMapStrMapStrByte(tag int) map[string]map[string][]byte { func (r *JceReader) ReadMapStrMapStrByte(tag int) map[string]map[string][]byte {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 8: case 8:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -491,7 +512,7 @@ func (r *JceReader) ReadFileStorageServerInfos(tag int) []FileStorageServerInfo
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -511,7 +532,7 @@ func (r *JceReader) ReadBigDataIPLists(tag int) []BigDataIPList {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -531,7 +552,7 @@ func (r *JceReader) ReadBigDataIPInfos(tag int) []BigDataIPInfo {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -551,7 +572,7 @@ func (r *JceReader) ReadOnlineInfos(tag int) []OnlineInfo {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -571,7 +592,7 @@ func (r *JceReader) ReadInstanceInfos(tag int) []InstanceInfo {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -591,7 +612,7 @@ func (r *JceReader) ReadSsoServerInfos(tag int) []SsoServerInfo {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -611,7 +632,7 @@ func (r *JceReader) ReadFriendInfos(tag int) []FriendInfo {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -631,7 +652,7 @@ func (r *JceReader) ReadTroopNumbers(tag int) []TroopNumber {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -651,7 +672,7 @@ func (r *JceReader) ReadTroopMemberInfos(tag int) []TroopMemberInfo {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -671,7 +692,7 @@ func (r *JceReader) ReadPushMessageInfos(tag int) []PushMessageInfo {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -691,7 +712,7 @@ func (r *JceReader) ReadSvcDevLoginInfos(tag int) []SvcDevLoginInfo {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil return nil
} }
hd, _ := r.readHead() hd := r.readHead()
switch hd.Type { switch hd.Type {
case 9: case 9:
s := r.ReadInt32(0) s := r.ReadInt32(0)

View File

@ -1,7 +1,7 @@
package jce package jce
import ( import (
"math/rand" "crypto/rand"
"reflect" "reflect"
"strconv" "strconv"
"sync" "sync"
@ -99,7 +99,7 @@ func TestJceReader_ReadBytes(t *testing.T) {
assert.Equal(t, b, rb) assert.Equal(t, b, rb)
} }
func (w *JceWriter) WriteObject(i interface{}, tag byte) { func (w *JceWriter) WriteObject(i any, tag byte) {
t := reflect.TypeOf(i) t := reflect.TypeOf(i)
if t.Kind() == reflect.Map { if t.Kind() == reflect.Map {
w.WriteMap(i, tag) w.WriteMap(i, tag)
@ -192,7 +192,7 @@ type decoder struct {
var decoderCache = sync.Map{} var decoderCache = sync.Map{}
// WriteJceStructRaw 写入 Jce 结构体 // WriteJceStructRaw 写入 Jce 结构体
func (w *JceWriter) WriteJceStructRaw(s interface{}) { func (w *JceWriter) WriteJceStructRaw(s any) {
t := reflect.TypeOf(s) t := reflect.TypeOf(s)
if t.Kind() != reflect.Ptr { if t.Kind() != reflect.Ptr {
return return
@ -234,7 +234,7 @@ func (w *JceWriter) WriteJceStruct(s IJceStruct, tag byte) {
w.writeHead(11, 0) w.writeHead(11, 0)
} }
func (w *JceWriter) WriteSlice(i interface{}, tag byte) { func (w *JceWriter) WriteSlice(i any, tag byte) {
va := reflect.ValueOf(i) va := reflect.ValueOf(i)
if va.Kind() != reflect.Slice { if va.Kind() != reflect.Slice {
panic("JceWriter.WriteSlice: not a slice") panic("JceWriter.WriteSlice: not a slice")
@ -270,7 +270,7 @@ func (w *JceWriter) WriteJceStructSlice(l []IJceStruct, tag byte) {
} }
} }
func (w *JceWriter) WriteMap(m interface{}, tag byte) { func (w *JceWriter) WriteMap(m any, tag byte) {
va := reflect.ValueOf(m) va := reflect.ValueOf(m)
if va.Kind() != reflect.Map { if va.Kind() != reflect.Map {
panic("JceWriter.WriteMap: not a map") panic("JceWriter.WriteMap: not a map")

View File

@ -527,6 +527,12 @@ type (
DelType byte `jceId:"2"` DelType byte `jceId:"2"`
Version int32 `jceId:"3"` Version int32 `jceId:"3"`
} }
VipInfo struct {
Open byte `jceId:"0"` // 1 为开通
Type int32 `jceId:"1"` // 1 为年费
Level int32 `jceId:"2"`
}
) )
func (pkt *RequestPacket) ReadFrom(r *JceReader) { func (pkt *RequestPacket) ReadFrom(r *JceReader) {
@ -736,3 +742,9 @@ func (pkt *InstanceInfo) ReadFrom(r *JceReader) {
pkt.ProductType = r.ReadInt64(3) pkt.ProductType = r.ReadInt64(3)
pkt.ClientType = r.ReadInt64(4) pkt.ClientType = r.ReadInt64(4)
} }
func (pkt *VipInfo) ReadFrom(r *JceReader) {
pkt.Open = r.ReadByte(0)
pkt.Type = r.ReadInt32(1)
pkt.Level = r.ReadInt32(2)
}

View File

@ -750,3 +750,11 @@ func (pkt *DelFriendReq) ToBytes() []byte {
w.WriteInt32(pkt.Version, 3) w.WriteInt32(pkt.Version, 3)
return w.Bytes() return w.Bytes()
} }
func (pkt *VipInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteByte(pkt.Open, 0)
w.WriteInt32(pkt.Type, 1)
w.WriteInt32(pkt.Level, 2)
return w.Bytes()
}

View File

@ -2,14 +2,13 @@ package binary
import ( import (
"bytes" "bytes"
"compress/gzip"
"compress/zlib"
"sync" "sync"
"github.com/klauspost/compress/gzip"
"github.com/klauspost/compress/zlib"
) )
var bufferPool = sync.Pool{ var bufferPool = sync.Pool{
New: func() interface{} { New: func() any {
return new(Writer) return new(Writer)
}, },
} }
@ -25,7 +24,7 @@ func SelectWriter() *Writer {
// PutWriter 将 Writer 放回池中 // PutWriter 将 Writer 放回池中
func PutWriter(w *Writer) { func PutWriter(w *Writer) {
// See https://golang.org/issue/23199 // See https://golang.org/issue/23199
const maxSize = 1 << 16 const maxSize = 32 * 1024
if (*bytes.Buffer)(w).Cap() < maxSize { // 对于大Buffer直接丢弃 if (*bytes.Buffer)(w).Cap() < maxSize { // 对于大Buffer直接丢弃
w.Reset() w.Reset()
bufferPool.Put(w) bufferPool.Put(w)
@ -33,7 +32,7 @@ func PutWriter(w *Writer) {
} }
var gzipPool = sync.Pool{ var gzipPool = sync.Pool{
New: func() interface{} { New: func() any {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
w := gzip.NewWriter(buf) w := gzip.NewWriter(buf)
return &GzipWriter{ return &GzipWriter{
@ -65,7 +64,7 @@ type zlibWriter struct {
} }
var zlibPool = sync.Pool{ var zlibPool = sync.Pool{
New: func() interface{} { New: func() any {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
w := zlib.NewWriter(buf) w := zlib.NewWriter(buf)
return &zlibWriter{ return &zlibWriter{

View File

@ -1,94 +0,0 @@
package binary
import (
"bytes"
"encoding/binary"
"math"
)
type DynamicProtoMessage map[uint64]interface{}
type encoder struct {
bytes.Buffer
}
func (msg DynamicProtoMessage) Encode() []byte {
en := &encoder{}
//nolint:staticcheck
for id, value := range msg {
key := id << 3
switch v := value.(type) {
case bool:
en.uvarint(key | 0)
vi := uint64(0)
if v {
vi = 1
}
en.uvarint(vi)
case int:
en.uvarint(key | 0)
en.svarint(int64(v))
case int32:
en.uvarint(key | 0)
en.svarint(int64(v))
case int64:
en.uvarint(key | 0)
en.svarint(v)
case uint32:
en.uvarint(key | 0)
en.uvarint(uint64(v))
case uint64:
en.uvarint(key | 0)
en.uvarint(v)
case float32:
en.uvarint(key | 5)
en.u32(math.Float32bits(v))
case float64:
en.uvarint(key | 1)
en.u64(math.Float64bits(v))
case string:
en.uvarint(key | 2)
b := []byte(v)
en.uvarint(uint64(len(b)))
_, _ = en.Write(b)
case []uint64:
for i := 0; i < len(v); i++ {
en.uvarint(key | 0)
en.uvarint(v[i])
}
case []byte:
en.uvarint(key | 2)
en.uvarint(uint64(len(v)))
_, _ = en.Write(v)
case DynamicProtoMessage:
en.uvarint(key | 2)
b := v.Encode()
en.uvarint(uint64(len(b)))
_, _ = en.Write(b)
}
}
return en.Bytes()
}
func (en *encoder) uvarint(v uint64) {
var b [binary.MaxVarintLen64]byte
n := binary.PutUvarint(b[:], v)
_, _ = en.Write(b[:n])
}
func (en *encoder) svarint(v int64) {
en.uvarint(uint64(v)<<1 ^ uint64(v>>63))
}
func (en *encoder) u32(v uint32) {
var b [4]byte
binary.LittleEndian.PutUint32(b[:], v)
_, _ = en.Write(b[:])
}
func (en *encoder) u64(v uint64) {
var b [8]byte
binary.LittleEndian.PutUint64(b[:], v)
_, _ = en.Write(b[:])
}

View File

@ -17,8 +17,6 @@ type NetworkReader struct {
conn net.Conn conn net.Conn
} }
type TlvMap map[uint16][]byte
// --- ByteStream reader --- // --- ByteStream reader ---
func NewReader(data []byte) *Reader { func NewReader(data []byte) *Reader {
@ -52,17 +50,20 @@ func (r *Reader) ReadBytesShort() []byte {
} }
func (r *Reader) ReadUInt16() uint16 { func (r *Reader) ReadUInt16() uint16 {
b := r.ReadBytes(2) b := make([]byte, 2)
_, _ = r.buf.Read(b)
return binary.BigEndian.Uint16(b) return binary.BigEndian.Uint16(b)
} }
func (r *Reader) ReadInt32() int32 { func (r *Reader) ReadInt32() int32 {
b := r.ReadBytes(4) b := make([]byte, 4)
_, _ = r.buf.Read(b)
return int32(binary.BigEndian.Uint32(b)) return int32(binary.BigEndian.Uint32(b))
} }
func (r *Reader) ReadInt64() int64 { func (r *Reader) ReadInt64() int64 {
b := r.ReadBytes(8) b := make([]byte, 8)
_, _ = r.buf.Read(b)
return int64(binary.BigEndian.Uint64(b)) return int64(binary.BigEndian.Uint64(b))
} }
@ -89,40 +90,12 @@ func (r *Reader) ReadAvailable() []byte {
return r.ReadBytes(r.buf.Len()) return r.ReadBytes(r.buf.Len())
} }
func (r *Reader) ReadTlvMap(tagSize int) (m TlvMap) {
defer func() {
if r := recover(); r != nil {
// TODO: error
}
}()
m = make(map[uint16][]byte)
for {
if r.Len() < tagSize {
return m
}
var k uint16
switch tagSize {
case 1:
k = uint16(r.ReadByte())
case 2:
k = r.ReadUInt16()
case 4:
k = uint16(r.ReadInt32())
}
if k == 255 {
return m
}
m[k] = r.ReadBytes(int(r.ReadUInt16()))
}
}
func (r *Reader) Len() int { func (r *Reader) Len() int {
return r.buf.Len() return r.buf.Len()
} }
func (tlv TlvMap) Exists(key uint16) bool { func (r *Reader) Index() int64 {
_, ok := tlv[key] return r.buf.Size()
return ok
} }
// --- Network reader --- // --- Network reader ---
@ -150,7 +123,8 @@ func (r *NetworkReader) ReadBytes(len int) ([]byte, error) {
} }
func (r *NetworkReader) ReadInt32() (int32, error) { func (r *NetworkReader) ReadInt32() (int32, error) {
b, err := r.ReadBytes(4) b := make([]byte, 4)
_, err := r.conn.Read(b)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -7,10 +7,6 @@ import (
type TEA [4]uint32 type TEA [4]uint32
// randuint32 returns a lock free uint32 value.
//go:linkname randuint32 runtime.fastrand
func randuint32() uint32
// Encrypt tea 加密 // Encrypt tea 加密
// http://bbs.chinaunix.net/thread-583468-1-1.html // http://bbs.chinaunix.net/thread-583468-1-1.html
// 感谢xichen大佬对TEA的解释 // 感谢xichen大佬对TEA的解释
@ -18,9 +14,6 @@ func (t TEA) Encrypt(src []byte) (dst []byte) {
lens := len(src) lens := len(src)
fill := 10 - (lens+1)%8 fill := 10 - (lens+1)%8
dst = make([]byte, fill+lens+7) dst = make([]byte, fill+lens+7)
binary.LittleEndian.PutUint32(dst, randuint32())
binary.LittleEndian.PutUint32(dst[4:], randuint32())
binary.LittleEndian.PutUint32(dst[8:], randuint32())
dst[0] = byte(fill-3) | 0xF8 // 存储pad长度 dst[0] = byte(fill-3) | 0xF8 // 存储pad长度
copy(dst[fill:], src) copy(dst[fill:], src)
@ -58,80 +51,81 @@ func (t *TEA) encode(n uint64) uint64 {
v0, v1 := uint32(n>>32), uint32(n) v0, v1 := uint32(n>>32), uint32(n)
t0, t1, t2, t3 := t[0], t[1], t[2], t[3] t0, t1, t2, t3 := t[0], t[1], t[2], t[3]
v0 += (v1 + 0x9e3779b9) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x9e3779b9) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x9e3779b9) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x9e3779b9) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x3c6ef372) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x3c6ef372) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x3c6ef372) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x3c6ef372) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xdaa66d2b) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0xdaa66d2b) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xdaa66d2b) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0xdaa66d2b) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x78dde6e4) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x78dde6e4) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x78dde6e4) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x78dde6e4) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x1715609d) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x1715609d) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x1715609d) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x1715609d) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xb54cda56) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0xb54cda56) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xb54cda56) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0xb54cda56) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x5384540f) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x5384540f) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x5384540f) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x5384540f) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xf1bbcdc8) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0xf1bbcdc8) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xf1bbcdc8) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0xf1bbcdc8) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x8ff34781) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x8ff34781) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x8ff34781) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x8ff34781) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x2e2ac13a) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x2e2ac13a) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x2e2ac13a) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x2e2ac13a) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xcc623af3) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0xcc623af3) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xcc623af3) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0xcc623af3) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x6a99b4ac) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x6a99b4ac) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x6a99b4ac) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x6a99b4ac) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x08d12e65) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x08d12e65) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x08d12e65) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x08d12e65) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xa708a81e) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0xa708a81e) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xa708a81e) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0xa708a81e) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x454021d7) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0x454021d7) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x454021d7) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0x454021d7) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xe3779b90) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 += (v1 + 0xe3779b90) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xe3779b90) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 += (v0 + 0xe3779b90) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
return uint64(v0)<<32 | uint64(v1) return uint64(v0)<<32 | uint64(v1)
} }
// 每次8字节 // 每次8字节
//
//go:nosplit //go:nosplit
func (t *TEA) decode(n uint64) uint64 { func (t *TEA) decode(n uint64) uint64 {
v0, v1 := uint32(n>>32), uint32(n) v0, v1 := uint32(n>>32), uint32(n)
t0, t1, t2, t3 := t[0], t[1], t[2], t[3] t0, t1, t2, t3 := t[0], t[1], t[2], t[3]
v1 -= (v0 + 0xe3779b90) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0xe3779b90) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xe3779b90) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0xe3779b90) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x454021d7) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x454021d7) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x454021d7) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x454021d7) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xa708a81e) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0xa708a81e) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xa708a81e) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0xa708a81e) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x08d12e65) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x08d12e65) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x08d12e65) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x08d12e65) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x6a99b4ac) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x6a99b4ac) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x6a99b4ac) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x6a99b4ac) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xcc623af3) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0xcc623af3) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xcc623af3) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0xcc623af3) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x2e2ac13a) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x2e2ac13a) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x2e2ac13a) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x2e2ac13a) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x8ff34781) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x8ff34781) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x8ff34781) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x8ff34781) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xf1bbcdc8) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0xf1bbcdc8) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xf1bbcdc8) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0xf1bbcdc8) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x5384540f) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x5384540f) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x5384540f) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x5384540f) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xb54cda56) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0xb54cda56) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xb54cda56) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0xb54cda56) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x1715609d) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x1715609d) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x1715609d) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x1715609d) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x78dde6e4) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x78dde6e4) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x78dde6e4) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x78dde6e4) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xdaa66d2b) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0xdaa66d2b) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xdaa66d2b) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0xdaa66d2b) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x3c6ef372) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x3c6ef372) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x3c6ef372) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x3c6ef372) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x9e3779b9) ^ ((v0 << 4) + t2) ^ ((v0 >> 5) + t3) v1 -= (v0 + 0x9e3779b9) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x9e3779b9) ^ ((v1 << 4) + t0) ^ ((v1 >> 5) + t1) v0 -= (v1 + 0x9e3779b9) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
return uint64(v0)<<32 | uint64(v1) return uint64(v0)<<32 | uint64(v1)
} }
@ -141,9 +135,9 @@ func NewTeaCipher(key []byte) (t TEA) {
if len(key) != 16 { if len(key) != 16 {
return TEA{} return TEA{}
} }
t[3] = binary.BigEndian.Uint32(key[12:]) t[3] = binary.BigEndian.Uint32(key[12:16])
t[2] = binary.BigEndian.Uint32(key[8:]) t[2] = binary.BigEndian.Uint32(key[8:12])
t[1] = binary.BigEndian.Uint32(key[4:]) t[1] = binary.BigEndian.Uint32(key[4:8])
t[0] = binary.BigEndian.Uint32(key[0:]) t[0] = binary.BigEndian.Uint32(key[:4])
return t return t
} }

View File

@ -2,14 +2,12 @@ package binary
import ( import (
"bytes" "bytes"
"compress/gzip"
"compress/zlib"
binary2 "encoding/binary" binary2 "encoding/binary"
"encoding/hex" "encoding/hex"
"io"
"net" "net"
"github.com/klauspost/compress/gzip"
"github.com/klauspost/compress/zlib"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
@ -35,7 +33,7 @@ func ZlibUncompress(src []byte) []byte {
var out bytes.Buffer var out bytes.Buffer
r, _ := zlib.NewReader(b) r, _ := zlib.NewReader(b)
defer r.Close() defer r.Close()
io.Copy(&out, r) _, _ = out.ReadFrom(r)
return out.Bytes() return out.Bytes()
} }
@ -43,7 +41,8 @@ func ZlibCompress(data []byte) []byte {
zw := acquireZlibWriter() zw := acquireZlibWriter()
_, _ = zw.w.Write(data) _, _ = zw.w.Write(data)
_ = zw.w.Close() _ = zw.w.Close()
ret := append([]byte(nil), zw.buf.Bytes()...) ret := make([]byte, len(zw.buf.Bytes()))
copy(ret, zw.buf.Bytes())
releaseZlibWriter(zw) releaseZlibWriter(zw)
return ret return ret
} }
@ -52,7 +51,8 @@ func GZipCompress(data []byte) []byte {
gw := AcquireGzipWriter() gw := AcquireGzipWriter()
_, _ = gw.Write(data) _, _ = gw.Write(data)
_ = gw.Close() _ = gw.Close()
ret := append([]byte(nil), gw.buf.Bytes()...) ret := make([]byte, len(gw.buf.Bytes()))
copy(ret, gw.buf.Bytes())
ReleaseGzipWriter(gw) ReleaseGzipWriter(gw)
return ret return ret
} }
@ -62,7 +62,7 @@ func GZipUncompress(src []byte) []byte {
var out bytes.Buffer var out bytes.Buffer
r, _ := gzip.NewReader(b) r, _ := gzip.NewReader(b)
defer r.Close() defer r.Close()
_, _ = io.Copy(&out, r) _, _ = out.ReadFrom(r)
return out.Bytes() return out.Bytes()
} }
@ -98,28 +98,13 @@ func AppendUUID(dst []byte, uuid []byte) []byte {
return dst return dst
} }
func ToIPV4Address(arr []byte) string {
ip := (net.IP)(arr)
return ip.String()
}
func UInt32ToIPV4Address(i uint32) string { func UInt32ToIPV4Address(i uint32) string {
ip := net.IP{0, 0, 0, 0} ip := net.IP{0, 0, 0, 0}
binary2.LittleEndian.PutUint32(ip, i) binary2.LittleEndian.PutUint32(ip, i)
return ip.String() return ip.String()
} }
func ToChunkedBytesF(b []byte, size int, f func([]byte)) { func ToBytes(i any) []byte {
r := NewReader(b)
for r.Len() >= size {
f(r.ReadBytes(size))
}
if r.Len() > 0 {
f(r.ReadAvailable())
}
}
func ToBytes(i interface{}) []byte {
return NewWriterF(func(w *Writer) { return NewWriterF(func(w *Writer) {
// TODO: more types // TODO: more types
switch t := i.(type) { switch t := i.(type) {

View File

@ -12,7 +12,8 @@ type Writer bytes.Buffer
func NewWriterF(f func(writer *Writer)) []byte { func NewWriterF(f func(writer *Writer)) []byte {
w := SelectWriter() w := SelectWriter()
f(w) f(w)
b := append([]byte(nil), w.Bytes()...) b := make([]byte, len(w.Bytes()))
copy(b, w.Bytes())
w.put() w.put()
return b return b
} }

View File

@ -1,7 +1,7 @@
package binary package binary
import ( import (
"math/rand" "crypto/rand"
"testing" "testing"
) )

View File

@ -5,7 +5,7 @@ import (
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
) )
var privateMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){ var privateMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
9: privateMessageDecoder, 10: privateMessageDecoder, 31: privateMessageDecoder, 9: privateMessageDecoder, 10: privateMessageDecoder, 31: privateMessageDecoder,
79: privateMessageDecoder, 97: privateMessageDecoder, 120: privateMessageDecoder, 79: privateMessageDecoder, 97: privateMessageDecoder, 120: privateMessageDecoder,
132: privateMessageDecoder, 133: privateMessageDecoder, 166: privateMessageDecoder, 132: privateMessageDecoder, 133: privateMessageDecoder, 166: privateMessageDecoder,
@ -13,21 +13,21 @@ var privateMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Respon
208: privatePttDecoder, 208: privatePttDecoder,
} }
var nonSvcNotifyTroopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){ var nonSvcNotifyTroopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
36: troopSystemMessageDecoder, 85: troopSystemMessageDecoder, 36: troopSystemMessageDecoder, 85: troopSystemMessageDecoder,
} }
var troopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){ var troopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
35: troopSystemMessageDecoder, 37: troopSystemMessageDecoder, 35: troopSystemMessageDecoder, 37: troopSystemMessageDecoder,
45: troopSystemMessageDecoder, 46: troopSystemMessageDecoder, 84: troopSystemMessageDecoder, 45: troopSystemMessageDecoder, 46: troopSystemMessageDecoder, 84: troopSystemMessageDecoder,
86: troopSystemMessageDecoder, 87: troopSystemMessageDecoder, 86: troopSystemMessageDecoder, 87: troopSystemMessageDecoder,
} // IsSvcNotify } // IsSvcNotify
var sysMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){ var sysMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
187: systemMessageDecoder, 188: systemMessageDecoder, 189: systemMessageDecoder, 187: systemMessageDecoder, 188: systemMessageDecoder, 189: systemMessageDecoder,
190: systemMessageDecoder, 191: systemMessageDecoder, 190: systemMessageDecoder, 191: systemMessageDecoder,
} // IsSvcNotify } // IsSvcNotify
var otherDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){ var otherDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
33: troopAddMemberBroadcastDecoder, 529: msgType0x211Decoder, 33: troopAddMemberBroadcastDecoder, 529: msgType0x211Decoder,
} }

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@ const (
AddressBookSource // 来自通讯录 AddressBookSource // 来自通讯录
) )
func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, resp *network.Response) { func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info network.RequestParams) {
c.sig.SyncCookie = rsp.SyncCookie c.sig.SyncCookie = rsp.SyncCookie
c.sig.PubAccountCookie = rsp.PubAccountCookie c.sig.PubAccountCookie = rsp.PubAccountCookie
// c.msgCtrlBuf = rsp.MsgCtrlBuf // c.msgCtrlBuf = rsp.MsgCtrlBuf
@ -51,63 +51,62 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, resp *ne
for _, pMsg := range pairMsg.Messages { for _, pMsg := range pairMsg.Messages {
// delete message // delete message
delItem := &pb.MessageItem{ delItem := &pb.MessageItem{
FromUin: pMsg.Head.GetFromUin(), FromUin: pMsg.Head.FromUin.Unwrap(),
ToUin: pMsg.Head.GetToUin(), ToUin: pMsg.Head.ToUin.Unwrap(),
MsgType: pMsg.Head.GetMsgType(), MsgType: pMsg.Head.MsgType.Unwrap(),
MsgSeq: pMsg.Head.GetMsgSeq(), MsgSeq: pMsg.Head.MsgSeq.Unwrap(),
MsgUid: pMsg.Head.GetMsgUid(), MsgUid: pMsg.Head.MsgUid.Unwrap(),
} }
delItems = append(delItems, delItem) delItems = append(delItems, delItem)
if pMsg.Head.GetToUin() != c.Uin { if pMsg.Head.ToUin.Unwrap() != c.Uin {
continue continue
} }
if (int64(pairMsg.GetLastReadTime()) & 4294967295) > int64(pMsg.Head.GetMsgTime()) { if (int64(pairMsg.LastReadTime.Unwrap()) & 4294967295) > int64(pMsg.Head.MsgTime.Unwrap()) {
continue continue
} }
c.commMsgProcessor(pMsg, resp) c.commMsgProcessor(pMsg, info)
} }
} }
if delItems != nil { if delItems != nil {
_, _ = c.call(c.buildDeleteMessageRequestPacket(delItems)) _, _ = c.sendAndWait(c.buildDeleteMessageRequestPacket(delItems))
} }
if rsp.GetSyncFlag() != msg.SyncFlag_STOP { if rsp.SyncFlag.Unwrap() != msg.SyncFlag_STOP {
c.Debug("continue sync with flag: %v", rsp.SyncFlag) c.debug("continue sync with flag: %v", rsp.SyncFlag)
req := c.buildGetMessageRequest(rsp.GetSyncFlag(), time.Now().Unix()) seq, pkt := c.buildGetMessageRequestPacket(rsp.SyncFlag.Unwrap(), time.Now().Unix())
req.Params = resp.Params() _, _ = c.sendAndWait(seq, pkt, info)
_, _ = c.callAndDecode(req)
} }
} }
func (c *QQClient) commMsgProcessor(pMsg *msg.Message, resp *network.Response) { func (c *QQClient) commMsgProcessor(pMsg *msg.Message, info network.RequestParams) {
strKey := fmt.Sprintf("%d%d%d%d", pMsg.Head.GetFromUin(), pMsg.Head.GetToUin(), pMsg.Head.GetMsgSeq(), pMsg.Head.GetMsgUid()) strKey := fmt.Sprintf("%d%d%d%d", pMsg.Head.FromUin.Unwrap(), pMsg.Head.ToUin.Unwrap(), pMsg.Head.MsgSeq.Unwrap(), pMsg.Head.MsgUid.Unwrap())
if _, ok := c.msgSvcCache.GetAndUpdate(strKey, time.Hour); ok { if _, ok := c.msgSvcCache.GetAndUpdate(strKey, time.Hour); ok {
c.Debug("c2c msg %v already exists in cache. skip.", pMsg.Head.GetMsgUid()) c.debug("c2c msg %v already exists in cache. skip.", pMsg.Head.MsgUid.Unwrap())
return return
} }
c.msgSvcCache.Add(strKey, "", time.Hour) c.msgSvcCache.Add(strKey, unit{}, time.Hour)
if c.lastC2CMsgTime > int64(pMsg.Head.GetMsgTime()) && (c.lastC2CMsgTime-int64(pMsg.Head.GetMsgTime())) > 60*10 { if c.lastC2CMsgTime > int64(pMsg.Head.MsgTime.Unwrap()) && (c.lastC2CMsgTime-int64(pMsg.Head.MsgTime.Unwrap())) > 60*10 {
c.Debug("c2c msg filtered by time. lastMsgTime: %v msgTime: %v", c.lastC2CMsgTime, pMsg.Head.GetMsgTime()) c.debug("c2c msg filtered by time. lastMsgTime: %v msgTime: %v", c.lastC2CMsgTime, pMsg.Head.MsgTime.Unwrap())
return return
} }
c.lastC2CMsgTime = int64(pMsg.Head.GetMsgTime()) c.lastC2CMsgTime = int64(pMsg.Head.MsgTime.Unwrap())
if resp.Params().Bool("init") { if info.Bool("init") {
return return
} }
if decoder, _ := peekC2CDecoder(pMsg.Head.GetMsgType()); decoder != nil { if decoder, _ := peekC2CDecoder(pMsg.Head.MsgType.Unwrap()); decoder != nil {
decoder(c, pMsg, resp) decoder(c, pMsg, info)
} else { } else {
c.Debug("unknown msg type on c2c processor: %v - %v", pMsg.Head.GetMsgType(), pMsg.Head.GetC2CCmd()) c.debug("unknown msg type on c2c processor: %v - %v", pMsg.Head.MsgType.Unwrap(), pMsg.Head.C2CCmd.Unwrap())
} }
} }
func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ *network.Response) { func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
switch pMsg.Head.GetC2CCmd() { switch pMsg.Head.C2CCmd.Unwrap() {
case 11, 175: // friend msg case 11, 175: // friend msg
if pMsg.Head.GetFromUin() == c.Uin { if pMsg.Head.FromUin.Unwrap() == c.Uin {
for { for {
frdSeq := c.friendSeq.Load() frdSeq := c.friendSeq.Load()
if frdSeq < pMsg.Head.GetMsgSeq() { if frdSeq < pMsg.Head.MsgSeq.Unwrap() {
if c.friendSeq.CAS(frdSeq, pMsg.Head.GetMsgSeq()) { if c.friendSeq.CompareAndSwap(frdSeq, pMsg.Head.MsgSeq.Unwrap()) {
break break
} }
} else { } else {
@ -118,53 +117,67 @@ func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ *network.Response)
if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil { if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil {
return return
} }
if pMsg.Head.GetFromUin() == c.Uin {
c.dispatchPrivateMessageSelf(c.parsePrivateMessage(pMsg)) // handle fragmented message
if pMsg.Content != nil && pMsg.Content.PkgNum.Unwrap() > 1 {
seq := pMsg.Content.DivSeq.Unwrap()
builder := c.messageBuilder(seq)
builder.append(pMsg)
if builder.len() < pMsg.Content.PkgNum.Unwrap() {
// continue to receive other fragments
return
}
c.msgBuilders.Delete(seq)
pMsg = builder.build()
}
if pMsg.Head.FromUin.Unwrap() == c.Uin {
c.SelfPrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg))
return return
} }
c.dispatchPrivateMessage(c.parsePrivateMessage(pMsg)) c.PrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg))
default: default:
c.Debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.GetC2CCmd()) c.debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.C2CCmd.Unwrap())
} }
} }
func privatePttDecoder(c *QQClient, pMsg *msg.Message, _ *network.Response) { func privatePttDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
if pMsg.Body == nil || pMsg.Body.RichText == nil || pMsg.Body.RichText.Ptt == nil { if pMsg.Body == nil || pMsg.Body.RichText == nil || pMsg.Body.RichText.Ptt == nil {
return return
} }
if len(pMsg.Body.RichText.Ptt.Reserve) != 0 { // if len(pMsg.Body.RichText.Ptt.Reserve) != 0 {
// m := binary.NewReader(pMsg.Body.RichText.Ptt.Reserve[1:]).ReadTlvMap(1) // m := binary.NewReader(pMsg.Body.RichText.Ptt.Reserve[1:]).ReadTlvMap(1)
// T3 -> timestamp T8 -> voiceType T9 -> voiceLength T10 -> PbReserveStruct // T3 -> timestamp T8 -> voiceType T9 -> voiceLength T10 -> PbReserveStruct
} //}
c.dispatchPrivateMessage(c.parsePrivateMessage(pMsg)) c.PrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg))
} }
func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ *network.Response) { func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
if pMsg.Head.C2CTmpMsgHead == nil || pMsg.Body == nil { if pMsg.Head.C2CTmpMsgHead == nil || pMsg.Body == nil {
return return
} }
if (pMsg.Head.GetMsgType() == 529 && pMsg.Head.GetC2CCmd() == 6) || pMsg.Body.RichText != nil { if (pMsg.Head.MsgType.Unwrap() == 529 && pMsg.Head.C2CCmd.Unwrap() == 6) || pMsg.Body.RichText != nil {
genTempSessionInfo := func() *TempSessionInfo { genTempSessionInfo := func() *TempSessionInfo {
if pMsg.Head.C2CTmpMsgHead.GetServiceType() == 0 { if pMsg.Head.C2CTmpMsgHead.ServiceType.Unwrap() == 0 {
group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GetGroupCode()) group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GroupCode.Unwrap())
if group == nil { if group == nil {
return nil return nil
} }
return &TempSessionInfo{ return &TempSessionInfo{
Source: GroupSource, Source: GroupSource,
GroupCode: group.Code, GroupCode: group.Code,
Sender: pMsg.Head.GetFromUin(), Sender: pMsg.Head.FromUin.Unwrap(),
client: c, client: c,
} }
} }
info := &TempSessionInfo{ info := &TempSessionInfo{
Source: 0, Source: 0,
Sender: pMsg.Head.GetFromUin(), Sender: pMsg.Head.FromUin.Unwrap(),
sig: pMsg.Head.C2CTmpMsgHead.GetSig(), sig: pMsg.Head.C2CTmpMsgHead.Sig,
client: c, client: c,
} }
switch pMsg.Head.C2CTmpMsgHead.GetServiceType() { switch pMsg.Head.C2CTmpMsgHead.ServiceType.Unwrap() {
case 1: case 1:
info.Source = MultiChatSource info.Source = MultiChatSource
case 130: case 130:
@ -185,41 +198,41 @@ func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ *network.Response) {
return return
} }
/* /*
group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GetGroupCode()) group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GroupCode.Unwrap())
if group == nil { if group == nil {
return return
} }
*/ */
if pMsg.Head.GetFromUin() == c.Uin { if pMsg.Head.FromUin.Unwrap() == c.Uin {
return return
} }
c.dispatchTempMessage(&TempMessageEvent{ c.TempMessageEvent.dispatch(c, &TempMessageEvent{
Message: c.parseTempMessage(pMsg), Message: c.parseTempMessage(pMsg),
Session: session, Session: session,
}) })
} }
} }
func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, resp *network.Response) { func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
groupJoinLock.Lock() groupJoinLock.Lock()
defer groupJoinLock.Unlock() defer groupJoinLock.Unlock()
group := c.FindGroupByUin(pMsg.Head.GetFromUin()) group := c.FindGroupByUin(pMsg.Head.FromUin.Unwrap())
if pMsg.Head.GetAuthUin() == c.Uin { if pMsg.Head.AuthUin.Unwrap() == c.Uin {
if group == nil && c.ReloadGroupList() == nil { if group == nil && c.ReloadGroupList() == nil {
c.dispatchJoinGroupEvent(c.FindGroupByUin(pMsg.Head.GetFromUin())) c.GroupJoinEvent.dispatch(c, c.FindGroupByUin(pMsg.Head.FromUin.Unwrap()))
} }
} else { } else {
if group != nil && group.FindMember(pMsg.Head.GetAuthUin()) == nil { if group != nil && group.FindMember(pMsg.Head.AuthUin.Unwrap()) == nil {
mem, err := c.GetMemberInfo(group.Code, pMsg.Head.GetAuthUin()) mem, err := c.GetMemberInfo(group.Code, pMsg.Head.AuthUin.Unwrap())
if err != nil { if err != nil {
c.Debug("error to fetch new member info: %v", err) c.debug("failed to fetch new member info: %v", err)
return return
} }
group.Update(func(info *GroupInfo) { group.Update(func(info *GroupInfo) {
info.Members = append(info.Members, mem) info.Members = append(info.Members, mem)
info.sort() info.sort()
}) })
c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
Group: group, Group: group,
Member: mem, Member: mem,
}) })
@ -227,44 +240,45 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, resp *networ
} }
} }
func systemMessageDecoder(c *QQClient, _ *msg.Message, _ *network.Response) { func systemMessageDecoder(c *QQClient, _ *msg.Message, _ network.RequestParams) {
_, _ = c.call(c.buildSystemMsgNewFriendRequest()) _, pkt := c.buildSystemMsgNewFriendPacket()
_ = c.sendPacket(pkt)
} }
func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message, info *network.Response) { func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message, info network.RequestParams) {
if !info.Params().Bool("used_reg_proxy") && pMsg.Head.GetMsgType() != 85 && pMsg.Head.GetMsgType() != 36 { if !info.Bool("used_reg_proxy") && pMsg.Head.MsgType.Unwrap() != 85 && pMsg.Head.MsgType.Unwrap() != 36 {
c.exceptAndDispatchGroupSysMsg() c.exceptAndDispatchGroupSysMsg()
} }
if len(pMsg.Body.GetMsgContent()) == 0 { if len(pMsg.Body.MsgContent) == 0 {
return return
} }
reader := binary.NewReader(pMsg.GetBody().GetMsgContent()) reader := binary.NewReader(pMsg.Body.MsgContent)
groupCode := uint32(reader.ReadInt32()) groupCode := uint32(reader.ReadInt32())
if info := c.FindGroup(int64(groupCode)); info != nil && pMsg.Head.GetGroupName() != "" && info.Name != pMsg.Head.GetGroupName() { if info := c.FindGroup(int64(groupCode)); info != nil && pMsg.Head.GroupName.Unwrap() != "" && info.Name != pMsg.Head.GroupName.Unwrap() {
c.Debug("group %v name updated. %v -> %v", groupCode, info.Name, pMsg.Head.GetGroupName()) c.debug("group %v name updated. %v -> %v", groupCode, info.Name, pMsg.Head.GroupName.Unwrap())
info.Name = pMsg.Head.GetGroupName() info.Name = pMsg.Head.GroupName.Unwrap()
} }
} }
func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, info *network.Response) { func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, info network.RequestParams) {
if pMsg.Head.GetC2CCmd() == 6 || pMsg.Head.C2CTmpMsgHead != nil { if pMsg.Head.C2CCmd.Unwrap() == 6 || pMsg.Head.C2CTmpMsgHead != nil {
tempSessionDecoder(c, pMsg, info) tempSessionDecoder(c, pMsg, info)
} }
sub4 := msg.SubMsgType0X4Body{} sub4 := msg.SubMsgType0X4Body{}
if err := proto.Unmarshal(pMsg.Body.MsgContent, &sub4); err != nil { if err := proto.Unmarshal(pMsg.Body.MsgContent, &sub4); err != nil {
err = errors.Wrap(err, "unmarshal sub msg 0x4 error") err = errors.Wrap(err, "unmarshal sub msg 0x4 error")
c.Error("unmarshal sub msg 0x4 error: %v", err) c.error("unmarshal sub msg 0x4 error: %v", err)
return return
} }
if sub4.NotOnlineFile != nil && sub4.NotOnlineFile.GetSubcmd() == 1 { // subcmd: 1 -> sendPacket, 2-> recv if sub4.NotOnlineFile != nil && sub4.NotOnlineFile.Subcmd.Unwrap() == 1 { // subcmd: 1 -> sendPacket, 2-> recv
rsp, err := c.callAndDecode(c.buildOfflineFileDownloadRequestPacket(sub4.NotOnlineFile.FileUuid)) // offline_file.go rsp, err := c.sendAndWait(c.buildOfflineFileDownloadRequestPacket(sub4.NotOnlineFile.FileUuid)) // offline_file.go
if err != nil { if err != nil {
return return
} }
c.dispatchOfflineFileEvent(&OfflineFileEvent{ c.OfflineFileEvent.dispatch(c, &OfflineFileEvent{
FileName: string(sub4.NotOnlineFile.FileName), FileName: string(sub4.NotOnlineFile.FileName),
FileSize: sub4.NotOnlineFile.GetFileSize(), FileSize: sub4.NotOnlineFile.FileSize.Unwrap(),
Sender: pMsg.Head.GetFromUin(), Sender: pMsg.Head.FromUin.Unwrap(),
DownloadUrl: rsp.(string), DownloadUrl: rsp.(string),
}) })
} }

View File

@ -16,7 +16,7 @@ const (
troopSystemMsgDecoders troopSystemMsgDecoders
) )
func peekC2CDecoder(msgType int32) (decoder func(*QQClient, *msg.Message, *network.Response), decoderType uint8) { func peekC2CDecoder(msgType int32) (decoder func(*QQClient, *msg.Message, network.RequestParams), decoderType uint8) {
switch msgType { switch msgType {
case 9: case 9:
return privateMessageDecoder, privateMsgDecoders return privateMessageDecoder, privateMsgDecoders

View File

@ -4,22 +4,25 @@ import (
"crypto/md5" "crypto/md5"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net/netip"
"sort" "sort"
"strconv" "strconv"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.uber.org/atomic"
"github.com/RomiChan/syncx"
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/binary/jce"
"github.com/Mrs4s/MiraiGo/client/internal/auth" "github.com/Mrs4s/MiraiGo/client/internal/auth"
"github.com/Mrs4s/MiraiGo/client/internal/highway" "github.com/Mrs4s/MiraiGo/client/internal/highway"
"github.com/Mrs4s/MiraiGo/client/internal/intern"
"github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq" "github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
@ -31,7 +34,8 @@ type QQClient struct {
once sync.Once once sync.Once
// option // option
AllowSlider bool AllowSlider bool
UseFragmentMessage bool
// account info // account info
Online atomic.Bool Online atomic.Bool
@ -47,20 +51,21 @@ type QQClient struct {
// protocol public field // protocol public field
SequenceId atomic.Int32 SequenceId atomic.Int32
SessionId []byte SessionId []byte
RandomKey []byte TCP *network.TCPClient // todo: combine other protocol state into one struct
ConnectTime time.Time ConnectTime time.Time
// todo: combine net conn, transport, pending into one struct
pendingMu sync.Mutex
pending map[int32]*network.Call
// TCP *network.TCPListener
transport *network.Transport transport *network.Transport
oicq *oicq.Codec oicq *oicq.Codec
logger Logger
// internal state // internal state
waiters sync.Map handlers syncx.Map[uint16, *handlerInfo]
version *auth.AppVersion waiters syncx.Map[string, func(any, error)]
deviceInfo *auth.Device initServerOnce sync.Once
servers []netip.AddrPort
currServerIndex int
retryTimes int
alive bool
// session info // session info
qwebSeq atomic.Int64 qwebSeq atomic.Int64
@ -70,22 +75,52 @@ type QQClient struct {
// timeDiff int64 // timeDiff int64
// address // address
otherSrvAddrs []string // otherSrvAddrs []string
fileStorageInfo *jce.FileStoragePushFSSvcList // fileStorageInfo *jce.FileStoragePushFSSvcList
// event handles
eventHandlers eventHandlers
PrivateMessageEvent EventHandle[*message.PrivateMessage]
TempMessageEvent EventHandle[*TempMessageEvent]
GroupMessageEvent EventHandle[*message.GroupMessage]
SelfPrivateMessageEvent EventHandle[*message.PrivateMessage]
SelfGroupMessageEvent EventHandle[*message.GroupMessage]
GroupMuteEvent EventHandle[*GroupMuteEvent]
GroupMessageRecalledEvent EventHandle[*GroupMessageRecalledEvent]
FriendMessageRecalledEvent EventHandle[*FriendMessageRecalledEvent]
GroupJoinEvent EventHandle[*GroupInfo]
GroupLeaveEvent EventHandle[*GroupLeaveEvent]
GroupMemberJoinEvent EventHandle[*MemberJoinGroupEvent]
GroupMemberLeaveEvent EventHandle[*MemberLeaveGroupEvent]
MemberCardUpdatedEvent EventHandle[*MemberCardUpdatedEvent]
GroupNameUpdatedEvent EventHandle[*GroupNameUpdatedEvent]
GroupMemberPermissionChangedEvent EventHandle[*MemberPermissionChangedEvent]
GroupInvitedEvent EventHandle[*GroupInvitedRequest]
UserWantJoinGroupEvent EventHandle[*UserJoinGroupRequest]
NewFriendEvent EventHandle[*NewFriendEvent]
NewFriendRequestEvent EventHandle[*NewFriendRequest]
DisconnectedEvent EventHandle[*ClientDisconnectedEvent]
GroupNotifyEvent EventHandle[INotifyEvent]
FriendNotifyEvent EventHandle[INotifyEvent]
MemberSpecialTitleUpdatedEvent EventHandle[*MemberSpecialTitleUpdatedEvent]
GroupDigestEvent EventHandle[*GroupDigestEvent]
OtherClientStatusChangedEvent EventHandle[*OtherClientStatusChangedEvent]
OfflineFileEvent EventHandle[*OfflineFileEvent]
GroupDisbandEvent EventHandle[*GroupDisbandEvent]
DeleteFriendEvent EventHandle[*DeleteFriendEvent]
// message state // message state
msgSvcCache *utils.Cache msgSvcCache *utils.Cache[unit]
lastC2CMsgTime int64 lastC2CMsgTime int64
transCache *utils.Cache transCache *utils.Cache[unit]
groupSysMsgCache *GroupSystemMessages groupSysMsgCache *GroupSystemMessages
groupMsgBuilders sync.Map msgBuilders syncx.Map[int32, *messageBuilder]
onlinePushCache *utils.Cache onlinePushCache *utils.Cache[unit]
heartbeatEnabled bool heartbeatEnabled bool
requestPacketRequestID atomic.Int32 requestPacketRequestID atomic.Int32
groupSeq atomic.Int32 groupSeq atomic.Int32
friendSeq atomic.Int32 friendSeq atomic.Int32
highwayApplyUpSeq atomic.Int32 highwayApplyUpSeq atomic.Int32
eventHandlers *eventHandlers
groupListLock sync.Mutex groupListLock sync.Mutex
} }
@ -99,18 +134,44 @@ type QiDianAccountInfo struct {
bigDataReqSession *bigDataSessionInfo bigDataReqSession *bigDataSessionInfo
} }
var decoders = map[string]func(*QQClient, *network.Response) (interface{}, error){ type handlerInfo struct {
"StatSvc.ReqMSFOffline": decodeMSFOfflinePacket, fun func(i any, err error)
"MessageSvc.PushNotify": decodeSvcNotify, dynamic bool
"OnlinePush.ReqPush": decodeOnlinePushReqPacket, params network.RequestParams
"OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket,
"OnlinePush.SidTicketExpired": decodeSidExpiredPacket,
"ConfigPushSvc.PushReq": decodePushReqPacket,
"MessageSvc.PushForceOffline": decodeForceOfflinePacket,
} }
func init() { func (h *handlerInfo) getParams() network.RequestParams {
rand.Seed(time.Now().UTC().UnixNano()) if h == nil {
return nil
}
return h.params
}
var decoders = map[string]func(*QQClient, *network.Packet) (any, error){
"wtlogin.login": decodeLoginResponse,
"wtlogin.exchange_emp": decodeExchangeEmpResponse,
"wtlogin.trans_emp": decodeTransEmpResponse,
"StatSvc.register": decodeClientRegisterResponse,
"StatSvc.ReqMSFOffline": decodeMSFOfflinePacket,
"MessageSvc.PushNotify": decodeSvcNotify,
"OnlinePush.ReqPush": decodeOnlinePushReqPacket,
"OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket,
"OnlinePush.SidTicketExpired": decodeSidExpiredPacket,
"ConfigPushSvc.PushReq": decodePushReqPacket,
"MessageSvc.PbGetMsg": decodeMessageSvcPacket,
"MessageSvc.PushForceOffline": decodeForceOfflinePacket,
"PbMessageSvc.PbMsgWithDraw": decodeMsgWithDrawResponse,
"friendlist.getFriendGroupList": decodeFriendGroupListResponse,
"friendlist.delFriend": decodeFriendDeleteResponse,
"friendlist.GetTroopListReqV2": decodeGroupListResponse,
"friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse,
"group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse,
"LongConn.OffPicUp": decodeOffPicUpResponse,
"ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket,
"ProfileService.Pb.ReqSystemMsgNew.Friend": decodeSystemMsgFriendPacket,
"OidbSvc.0xd79": decodeWordSegmentation,
"OidbSvc.0x990": decodeTranslateResponse,
"SummaryCard.ReqSummaryCard": decodeSummaryCardResponse,
} }
// NewClient create new qq client // NewClient create new qq client
@ -127,29 +188,21 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
Uin: uin, Uin: uin,
PasswordMd5: passwordMd5, PasswordMd5: passwordMd5,
AllowSlider: true, AllowSlider: true,
// TCP: &network.TCPListener{}, TCP: &network.TCPClient{},
sig: &auth.SigInfo{ sig: &auth.SigInfo{
OutPacketSessionID: []byte{0x02, 0xB0, 0x5B, 0x8B}, OutPacketSessionID: []byte{0x02, 0xB0, 0x5B, 0x8B},
}, },
eventHandlers: &eventHandlers{}, msgSvcCache: utils.NewCache[unit](time.Second * 15),
msgSvcCache: utils.NewCache(time.Second * 15), transCache: utils.NewCache[unit](time.Second * 15),
transCache: utils.NewCache(time.Second * 15), onlinePushCache: utils.NewCache[unit](time.Second * 15),
onlinePushCache: utils.NewCache(time.Second * 15), alive: true,
highwaySession: new(highway.Session), highwaySession: new(highway.Session),
pending: make(map[int32]*network.Call),
version: new(auth.AppVersion),
deviceInfo: new(auth.Device),
} }
cli.transport = &network.Transport{ cli.transport = &network.Transport{Sig: cli.sig}
Sig: cli.sig,
Version: cli.version,
Device: cli.deviceInfo,
}
cli.oicq = oicq.NewCodec(cli.Uin) cli.oicq = oicq.NewCodec(cli.Uin)
{ // init atomic values { // init atomic values
cli.SequenceId.Store(0x3635) cli.SequenceId.Store(int32(rand.Intn(100000)))
cli.requestPacketRequestID.Store(1921334513) cli.requestPacketRequestID.Store(1921334513)
cli.groupSeq.Store(int32(rand.Intn(20000))) cli.groupSeq.Store(int32(rand.Intn(20000)))
cli.friendSeq.Store(22911) cli.friendSeq.Store(22911)
@ -157,65 +210,23 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
} }
cli.highwaySession.Uin = strconv.FormatInt(cli.Uin, 10) cli.highwaySession.Uin = strconv.FormatInt(cli.Uin, 10)
cli.GuildService = &GuildService{c: cli} cli.GuildService = &GuildService{c: cli}
cli.UseDevice(SystemDeviceInfo) cli.TCP.PlannedDisconnect(cli.plannedDisconnect)
sso, err := getSSOAddress() cli.TCP.UnexpectedDisconnect(cli.unexpectedDisconnect)
if err == nil && len(sso) > 0 {
for _, addr := range sso {
cli.transport.AddServerAddr(addr)
}
}
adds, err := net.LookupIP("msfwifi.3g.qq.com") // host servers
if err == nil && len(adds) > 0 {
for _, addr := range adds {
cli.transport.AddServerAddr(&net.TCPAddr{
IP: addr,
Port: 8080,
})
}
}
if cli.transport.ServerCount() == 0 {
for _, addr := range []*net.TCPAddr{ // default servers
{IP: net.IP{42, 81, 172, 22}, Port: 80},
{IP: net.IP{42, 81, 172, 81}, Port: 80},
{IP: net.IP{42, 81, 172, 147}, Port: 443},
{IP: net.IP{114, 221, 144, 215}, Port: 80},
{IP: net.IP{114, 221, 148, 59}, Port: 14000},
{IP: net.IP{125, 94, 60, 146}, Port: 80},
} {
cli.transport.AddServerAddr(addr)
}
}
/*pings := make([]int64, len(cli.servers))
wg := sync.WaitGroup{}
wg.Add(len(cli.servers))
for i := range cli.servers {
go func(index int) {
defer wg.Done()
p, err := qualityTest(cli.servers[index].String())
if err != nil {
pings[index] = 9999
return
}
pings[index] = p
}(i)
}
wg.Wait()
sort.Slice(cli.servers, func(i, j int) bool {
return pings[i] < pings[j]
})
if len(cli.servers) > 3 {
cli.servers = cli.servers[0 : len(cli.servers)/2] // 保留ping值中位数以上的server
}*/
cli.transport.PlannedDisconnect(cli.plannedDisconnect)
cli.transport.UnexpectedDisconnect(cli.unexpectedDisconnect)
rand.Read(cli.RandomKey)
return cli return cli
} }
func (c *QQClient) version() *auth.AppVersion {
return c.transport.Version
}
func (c *QQClient) Device() *DeviceInfo {
return c.transport.Device
}
func (c *QQClient) UseDevice(info *auth.Device) { func (c *QQClient) UseDevice(info *auth.Device) {
*c.version = *info.Protocol.Version() c.transport.Version = info.Protocol.Version()
*c.deviceInfo = *info c.transport.Device = info
c.highwaySession.AppID = int32(c.version.AppId) c.highwaySession.AppID = int32(c.version().AppId)
c.sig.Ksid = []byte(fmt.Sprintf("|%s|A8.2.7.27f6ea96", info.IMEI)) c.sig.Ksid = []byte(fmt.Sprintf("|%s|A8.2.7.27f6ea96", info.IMEI))
} }
@ -223,6 +234,7 @@ func (c *QQClient) Release() {
if c.Online.Load() { if c.Online.Load() {
c.Disconnect() c.Disconnect()
} }
c.alive = false
} }
// Login send login request // Login send login request
@ -234,7 +246,7 @@ func (c *QQClient) Login() (*LoginResponse, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rsp, err := c.callAndDecode(c.buildLoginRequest()) rsp, err := c.sendAndWait(c.buildLoginPacket())
if err != nil { if err != nil {
c.Disconnect() c.Disconnect()
return nil, err return nil, err
@ -250,50 +262,11 @@ func (c *QQClient) TokenLogin(token []byte) error {
if c.Online.Load() { if c.Online.Load() {
return ErrAlreadyOnline return ErrAlreadyOnline
} }
err := c.LoadToken(token) err := c.connect()
if err != nil { if err != nil {
return err return err
} }
return c.ReLogin() {
}
func (c *QQClient) ReLogin() error {
if c.Online.Load() {
return ErrAlreadyOnline
}
err := c.connectFastest()
if err != nil {
return err
}
_, err = c.callAndDecode(c.buildRequestChangeSigRequest(c.version.MainSigMap))
if err != nil {
return err
}
err = c.init(true)
// 登录失败
if err != nil {
c.Disconnect()
}
return err
}
func (c *QQClient) DumpToken() []byte {
return binary.NewWriterF(func(w *binary.Writer) {
w.WriteUInt64(uint64(c.Uin))
w.WriteBytesShort(c.sig.D2)
w.WriteBytesShort(c.sig.D2Key)
w.WriteBytesShort(c.sig.TGT)
w.WriteBytesShort(c.sig.SrmToken)
w.WriteBytesShort(c.sig.T133)
w.WriteBytesShort(c.sig.EncryptedA1)
w.WriteBytesShort(c.oicq.WtSessionTicketKey)
w.WriteBytesShort(c.sig.OutPacketSessionID)
w.WriteBytesShort(c.deviceInfo.TgtgtKey)
})
}
func (c *QQClient) LoadToken(token []byte) error {
return utils.CoverError(func() {
r := binary.NewReader(token) r := binary.NewReader(token)
c.Uin = r.ReadInt64() c.Uin = r.ReadInt64()
c.sig.D2 = r.ReadBytesShort() c.sig.D2 = r.ReadBytesShort()
@ -305,84 +278,19 @@ func (c *QQClient) LoadToken(token []byte) error {
c.oicq.WtSessionTicketKey = r.ReadBytesShort() c.oicq.WtSessionTicketKey = r.ReadBytesShort()
c.sig.OutPacketSessionID = r.ReadBytesShort() c.sig.OutPacketSessionID = r.ReadBytesShort()
// SystemDeviceInfo.TgtgtKey = r.ReadBytesShort() // SystemDeviceInfo.TgtgtKey = r.ReadBytesShort()
c.deviceInfo.TgtgtKey = r.ReadBytesShort() c.Device().TgtgtKey = r.ReadBytesShort()
copy(SystemDeviceInfo.TgtgtKey, c.deviceInfo.TgtgtKey) }
}) _, err = c.sendAndWait(c.buildRequestChangeSigPacket(true))
if err != nil {
return err
}
return c.init(true)
} }
func (c *QQClient) DumpDevice() []byte {
return binary.NewWriterF(func(w *binary.Writer) {
w.WriteBytesShort(c.deviceInfo.Display)
w.WriteBytesShort(c.deviceInfo.Product)
w.WriteBytesShort(c.deviceInfo.Device)
w.WriteBytesShort(c.deviceInfo.Board)
w.WriteBytesShort(c.deviceInfo.Brand)
w.WriteBytesShort(c.deviceInfo.Model)
w.WriteBytesShort(c.deviceInfo.Bootloader)
w.WriteBytesShort(c.deviceInfo.FingerPrint)
w.WriteBytesShort(c.deviceInfo.BootId)
w.WriteBytesShort(c.deviceInfo.ProcVersion)
w.WriteBytesShort(c.deviceInfo.BaseBand)
w.WriteBytesShort(c.deviceInfo.SimInfo)
w.WriteBytesShort(c.deviceInfo.OSType)
w.WriteBytesShort(c.deviceInfo.MacAddress)
w.WriteBytesShort(c.deviceInfo.IpAddress)
w.WriteBytesShort(c.deviceInfo.WifiBSSID)
w.WriteBytesShort(c.deviceInfo.WifiSSID)
w.WriteBytesShort(c.deviceInfo.IMSIMd5)
w.WriteStringShort(c.deviceInfo.IMEI)
w.WriteBytesShort(c.deviceInfo.APN)
w.WriteBytesShort(c.deviceInfo.VendorName)
w.WriteBytesShort(c.deviceInfo.VendorOSName)
w.WriteBytesShort(c.deviceInfo.AndroidId)
w.Write(c.PasswordMd5[:])
})
}
func (c *QQClient) LoadDevice(device []byte) error {
return utils.CoverError(func() {
r := binary.NewReader(device)
c.deviceInfo.Display = r.ReadBytesShort()
c.deviceInfo.Product = r.ReadBytesShort()
c.deviceInfo.Device = r.ReadBytesShort()
c.deviceInfo.Board = r.ReadBytesShort()
c.deviceInfo.Brand = r.ReadBytesShort()
c.deviceInfo.Model = r.ReadBytesShort()
c.deviceInfo.Bootloader = r.ReadBytesShort()
c.deviceInfo.FingerPrint = r.ReadBytesShort()
c.deviceInfo.BootId = r.ReadBytesShort()
c.deviceInfo.ProcVersion = r.ReadBytesShort()
c.deviceInfo.BaseBand = r.ReadBytesShort()
c.deviceInfo.SimInfo = r.ReadBytesShort()
c.deviceInfo.OSType = r.ReadBytesShort()
c.deviceInfo.MacAddress = r.ReadBytesShort()
c.deviceInfo.IpAddress = r.ReadBytesShort()
c.deviceInfo.WifiBSSID = r.ReadBytesShort()
c.deviceInfo.WifiSSID = r.ReadBytesShort()
c.deviceInfo.IMSIMd5 = r.ReadBytesShort()
c.deviceInfo.IMEI = r.ReadStringShort()
c.deviceInfo.APN = r.ReadBytesShort()
c.deviceInfo.VendorName = r.ReadBytesShort()
c.deviceInfo.VendorOSName = r.ReadBytesShort()
c.deviceInfo.AndroidId = r.ReadBytesShort()
copy(c.PasswordMd5[:], r.ReadBytes(md5.Size))
})
}
// FetchQRCode 以默认值获取登录二维码
// 函数已被弃用 请使用FetchQRCodeCustomSize获得更可控结果
// 但该兼容函数不会被删除
// Deprecated use FetchQRCodeCustomSize(3, 4, 2) instead
func (c *QQClient) FetchQRCode() (*QRCodeLoginResponse, error) { func (c *QQClient) FetchQRCode() (*QRCodeLoginResponse, error) {
return c.FetchQRCodeCustomSize(3, 4, 2) return c.FetchQRCodeCustomSize(3, 4, 2)
} }
// FetchQRCodeCustomSize 以特定参数获取登录二维码
// size: 块尺寸 默认值3 即单个黑/白块大小为3x3像素
// margin: 与图片边界的距离 默认值4 即二维码主体至图片边界有4像素白色填充
// ecLevel: 纠错等级 可用值1,2,3 默认值2
func (c *QQClient) FetchQRCodeCustomSize(size, margin, ecLevel uint32) (*QRCodeLoginResponse, error) { func (c *QQClient) FetchQRCodeCustomSize(size, margin, ecLevel uint32) (*QRCodeLoginResponse, error) {
if c.Online.Load() { if c.Online.Load() {
return nil, ErrAlreadyOnline return nil, ErrAlreadyOnline
@ -391,9 +299,7 @@ func (c *QQClient) FetchQRCodeCustomSize(size, margin, ecLevel uint32) (*QRCodeL
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.transport.Version = auth.AndroidWatch.Version() i, err := c.sendAndWait(c.buildQRCodeFetchRequestPacket(size, margin, ecLevel))
i, err := c.callAndDecode(c.buildQRCodeFetchRequest(size, margin, ecLevel))
c.transport.Version = c.version
if err != nil { if err != nil {
return nil, errors.Wrap(err, "fetch qrcode error") return nil, errors.Wrap(err, "fetch qrcode error")
} }
@ -401,9 +307,7 @@ func (c *QQClient) FetchQRCodeCustomSize(size, margin, ecLevel uint32) (*QRCodeL
} }
func (c *QQClient) QueryQRCodeStatus(sig []byte) (*QRCodeLoginResponse, error) { func (c *QQClient) QueryQRCodeStatus(sig []byte) (*QRCodeLoginResponse, error) {
c.transport.Version = auth.AndroidWatch.Version() i, err := c.sendAndWait(c.buildQRCodeResultQueryRequestPacket(sig))
i, err := c.callAndDecode(c.buildQRCodeResultQueryRequest(sig))
c.transport.Version = c.version
if err != nil { if err != nil {
return nil, errors.Wrap(err, "query result error") return nil, errors.Wrap(err, "query result error")
} }
@ -411,7 +315,7 @@ func (c *QQClient) QueryQRCodeStatus(sig []byte) (*QRCodeLoginResponse, error) {
} }
func (c *QQClient) QRCodeLogin(info *QRCodeLoginInfo) (*LoginResponse, error) { func (c *QQClient) QRCodeLogin(info *QRCodeLoginInfo) (*LoginResponse, error) {
i, err := c.callAndDecode(c.buildQRCodeLoginRequest(info.tmpPwd, info.tmpNoPicSig, info.tgtQR)) i, err := c.sendAndWait(c.buildQRCodeLoginPacket(info.tmpPwd, info.tmpNoPicSig, info.tgtQR))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "qrcode login error") return nil, errors.Wrap(err, "qrcode login error")
} }
@ -424,8 +328,8 @@ func (c *QQClient) QRCodeLogin(info *QRCodeLoginInfo) (*LoginResponse, error) {
// SubmitCaptcha send captcha to server // SubmitCaptcha send captcha to server
func (c *QQClient) SubmitCaptcha(result string, sign []byte) (*LoginResponse, error) { func (c *QQClient) SubmitCaptcha(result string, sign []byte) (*LoginResponse, error) {
req := c.buildCaptchaRequest(result, sign) seq, packet := c.buildCaptchaPacket(result, sign)
rsp, err := c.callAndDecode(req) rsp, err := c.sendAndWait(seq, packet)
if err != nil { if err != nil {
c.Disconnect() c.Disconnect()
return nil, err return nil, err
@ -438,8 +342,8 @@ func (c *QQClient) SubmitCaptcha(result string, sign []byte) (*LoginResponse, er
} }
func (c *QQClient) SubmitTicket(ticket string) (*LoginResponse, error) { func (c *QQClient) SubmitTicket(ticket string) (*LoginResponse, error) {
req := c.buildTicketSubmitRequest(ticket) seq, packet := c.buildTicketSubmitPacket(ticket)
rsp, err := c.callAndDecode(req) rsp, err := c.sendAndWait(seq, packet)
if err != nil { if err != nil {
c.Disconnect() c.Disconnect()
return nil, err return nil, err
@ -452,7 +356,7 @@ func (c *QQClient) SubmitTicket(ticket string) (*LoginResponse, error) {
} }
func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) { func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) {
rsp, err := c.callAndDecode(c.buildSMSCodeSubmitRequest(code)) rsp, err := c.sendAndWait(c.buildSMSCodeSubmitPacket(code))
if err != nil { if err != nil {
c.Disconnect() c.Disconnect()
return nil, err return nil, err
@ -465,9 +369,9 @@ func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) {
} }
func (c *QQClient) RequestSMS() bool { func (c *QQClient) RequestSMS() bool {
rsp, err := c.callAndDecode(c.buildSMSRequest()) rsp, err := c.sendAndWait(c.buildSMSRequestPacket())
if err != nil { if err != nil {
c.Error("request sms error: %v", err) c.error("request sms error: %v", err)
return false return false
} }
return rsp.(LoginResponse).Error == SMSNeededError return rsp.(LoginResponse).Error == SMSNeededError
@ -475,18 +379,18 @@ func (c *QQClient) RequestSMS() bool {
func (c *QQClient) init(tokenLogin bool) error { func (c *QQClient) init(tokenLogin bool) error {
if len(c.sig.G) == 0 { if len(c.sig.G) == 0 {
c.Warning("device lock is disable. http api may fail.") c.warning("device lock is disabled. HTTP API may fail.")
} }
c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10) c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10)
if err := c.registerClient(); err != nil { if err := c.registerClient(); err != nil {
return errors.Wrap(err, "register error") return errors.Wrap(err, "register error")
} }
if tokenLogin { if tokenLogin {
notify := make(chan struct{}) notify := make(chan struct{}, 2)
d := c.waitPacket("StatSvc.ReqMSFOffline", func(i interface{}, err error) { d := c.waitPacket("StatSvc.ReqMSFOffline", func(i any, err error) {
notify <- struct{}{} notify <- struct{}{}
}) })
d2 := c.waitPacket("MessageSvc.PushForceOffline", func(i interface{}, err error) { d2 := c.waitPacket("MessageSvc.PushForceOffline", func(i any, err error) {
notify <- struct{}{} notify <- struct{}{}
}) })
select { select {
@ -499,16 +403,17 @@ func (c *QQClient) init(tokenLogin bool) error {
d2() d2()
} }
} }
go c.doHeartbeat()
_ = c.RefreshStatus()
if c.version.Protocol == auth.QiDian {
_, _ = c.callAndDecode(c.buildLoginExtraPacket()) // 小登录
_, _ = c.callAndDecode(c.buildConnKeyRequestPacket()) // big data key 如果等待 config push 的话时间来不及
}
c.groupSysMsgCache, _ = c.GetGroupSystemMessages() c.groupSysMsgCache, _ = c.GetGroupSystemMessages()
req := c.buildGetMessageRequest(msg.SyncFlag_START, time.Now().Unix()) if !c.heartbeatEnabled {
req.Params = network.Params{"used_reg_proxy": true, "init": true} go c.doHeartbeat()
_, _ = c.callAndDecode(req) }
_ = c.RefreshStatus()
if c.version().Protocol == auth.QiDian {
_, _ = c.sendAndWait(c.buildLoginExtraPacket()) // 小登录
_, _ = c.sendAndWait(c.buildConnKeyRequestPacket()) // big data key 如果等待 config push 的话时间来不及
}
seq, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix())
_, _ = c.sendAndWait(seq, pkt, network.RequestParams{"used_reg_proxy": true, "init": true})
c.syncChannelFirstView() c.syncChannelFirstView()
return nil return nil
} }
@ -524,20 +429,20 @@ func (c *QQClient) GenToken() []byte {
w.WriteBytesShort(c.sig.EncryptedA1) w.WriteBytesShort(c.sig.EncryptedA1)
w.WriteBytesShort(c.oicq.WtSessionTicketKey) w.WriteBytesShort(c.oicq.WtSessionTicketKey)
w.WriteBytesShort(c.sig.OutPacketSessionID) w.WriteBytesShort(c.sig.OutPacketSessionID)
w.WriteBytesShort(c.deviceInfo.TgtgtKey) w.WriteBytesShort(c.Device().TgtgtKey)
}) })
} }
func (c *QQClient) SetOnlineStatus(s UserOnlineStatus) { func (c *QQClient) SetOnlineStatus(s UserOnlineStatus) {
if s < 1000 { if s < 1000 {
_, _ = c.call(c.buildStatusSetPacket(int32(s), 0)) _, _ = c.sendAndWait(c.buildStatusSetPacket(int32(s), 0))
return return
} }
_, _ = c.call(c.buildStatusSetPacket(11, int32(s))) _, _ = c.sendAndWait(c.buildStatusSetPacket(11, int32(s)))
} }
func (c *QQClient) GetWordSegmentation(text string) ([]string, error) { func (c *QQClient) GetWordSegmentation(text string) ([]string, error) {
rsp, err := c.callAndDecode(c.buildWordSegmentationPacket([]byte(text))) rsp, err := c.sendAndWait(c.buildWordSegmentationPacket([]byte(text)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -552,7 +457,7 @@ func (c *QQClient) GetWordSegmentation(text string) ([]string, error) {
} }
func (c *QQClient) GetSummaryInfo(target int64) (*SummaryCardInfo, error) { func (c *QQClient) GetSummaryInfo(target int64) (*SummaryCardInfo, error) {
rsp, err := c.callAndDecode(c.buildSummaryCardRequest(target)) rsp, err := c.sendAndWait(c.buildSummaryCardRequestPacket(target))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -573,7 +478,7 @@ func (c *QQClient) ReloadFriendList() error {
// 当使用普通QQ时: 请求好友列表 // 当使用普通QQ时: 请求好友列表
// 当使用企点QQ时: 请求外部联系人列表 // 当使用企点QQ时: 请求外部联系人列表
func (c *QQClient) GetFriendList() (*FriendListResponse, error) { func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
if c.version.Protocol == auth.QiDian { if c.version().Protocol == auth.QiDian {
rsp, err := c.getQiDianAddressDetailList() rsp, err := c.getQiDianAddressDetailList()
if err != nil { if err != nil {
return nil, err return nil, err
@ -583,8 +488,7 @@ func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
curFriendCount := 0 curFriendCount := 0
r := &FriendListResponse{} r := &FriendListResponse{}
for { for {
call := c.buildFriendGroupListRequest(int16(curFriendCount), 150, 0, 0) rsp, err := c.sendAndWait(c.buildFriendGroupListRequestPacket(int16(curFriendCount), 150, 0, 0))
rsp, err := c.callAndDecode(call)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -600,11 +504,11 @@ func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
} }
func (c *QQClient) SendGroupPoke(groupCode, target int64) { func (c *QQClient) SendGroupPoke(groupCode, target int64) {
_, _ = c.call(c.buildGroupPokeRequest(groupCode, target)) _, _ = c.sendAndWait(c.buildGroupPokePacket(groupCode, target))
} }
func (c *QQClient) SendFriendPoke(target int64) { func (c *QQClient) SendFriendPoke(target int64) {
_, _ = c.call(c.buildFriendPokeRequest(target)) _, _ = c.sendAndWait(c.buildFriendPokePacket(target))
} }
func (c *QQClient) ReloadGroupList() error { func (c *QQClient) ReloadGroupList() error {
@ -619,10 +523,11 @@ func (c *QQClient) ReloadGroupList() error {
} }
func (c *QQClient) GetGroupList() ([]*GroupInfo, error) { func (c *QQClient) GetGroupList() ([]*GroupInfo, error) {
rsp, err := c.callAndDecode(c.buildGroupListRequest(EmptyBytes)) rsp, err := c.sendAndWait(c.buildGroupListRequestPacket(EmptyBytes))
if err != nil { if err != nil {
return nil, err return nil, err
} }
interner := intern.NewStringInterner()
r := rsp.([]*GroupInfo) r := rsp.([]*GroupInfo)
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
batch := 50 batch := 50
@ -635,11 +540,12 @@ func (c *QQClient) GetGroupList() ([]*GroupInfo, error) {
for j := i; j < k; j++ { for j := i; j < k; j++ {
go func(g *GroupInfo, wg *sync.WaitGroup) { go func(g *GroupInfo, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
m, err := c.GetGroupMembers(g) m, err := c.getGroupMembers(g, interner)
if err != nil { if err != nil {
return return
} }
g.Members = m g.Members = m
g.Name = interner.Intern(g.Name)
}(r[j], &wg) }(r[j], &wg)
} }
wg.Wait() wg.Wait()
@ -648,15 +554,20 @@ func (c *QQClient) GetGroupList() ([]*GroupInfo, error) {
} }
func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error) { func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error) {
interner := intern.NewStringInterner()
return c.getGroupMembers(group, interner)
}
func (c *QQClient) getGroupMembers(group *GroupInfo, interner *intern.StringInterner) ([]*GroupMemberInfo, error) {
var nextUin int64 var nextUin int64
var list []*GroupMemberInfo var list []*GroupMemberInfo
for { for {
data, err := c.callAndDecode(c.buildGroupMemberListRequest(group.Uin, group.Code, nextUin)) data, err := c.sendAndWait(c.buildGroupMemberListRequestPacket(group.Uin, group.Code, nextUin))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if data == nil { if data == nil {
return nil, errors.New("group member list unavailable: rsp is nil") return nil, errors.New("group members list is unavailable: rsp is nil")
} }
rsp := data.(*groupMemberListResponse) rsp := data.(*groupMemberListResponse)
nextUin = rsp.NextUin nextUin = rsp.NextUin
@ -665,6 +576,9 @@ func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error)
if m.Uin == group.OwnerUin { if m.Uin == group.OwnerUin {
m.Permission = Owner m.Permission = Owner
} }
m.CardName = interner.Intern(m.CardName)
m.Nickname = interner.Intern(m.Nickname)
m.SpecialTitle = interner.Intern(m.SpecialTitle)
} }
list = append(list, rsp.list...) list = append(list, rsp.list...)
if nextUin == 0 { if nextUin == 0 {
@ -677,7 +591,7 @@ func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error)
} }
func (c *QQClient) GetMemberInfo(groupCode, memberUin int64) (*GroupMemberInfo, error) { func (c *QQClient) GetMemberInfo(groupCode, memberUin int64) (*GroupMemberInfo, error) {
info, err := c.callAndDecode(c.buildGroupMemberInfoRequest(groupCode, memberUin)) info, err := c.sendAndWait(c.buildGroupMemberInfoRequestPacket(groupCode, memberUin))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -685,6 +599,12 @@ func (c *QQClient) GetMemberInfo(groupCode, memberUin int64) (*GroupMemberInfo,
} }
func (c *QQClient) FindFriend(uin int64) *FriendInfo { func (c *QQClient) FindFriend(uin int64) *FriendInfo {
if uin == c.Uin {
return &FriendInfo{
Uin: uin,
Nickname: c.Nickname,
}
}
for _, t := range c.FriendList { for _, t := range c.FriendList {
f := t f := t
if f.Uin == uin { if f.Uin == uin {
@ -698,7 +618,7 @@ func (c *QQClient) DeleteFriend(uin int64) error {
if c.FindFriend(uin) == nil { if c.FindFriend(uin) == nil {
return errors.New("friend not found") return errors.New("friend not found")
} }
_, err := c.callAndDecode(c.buildFriendDeletePacket(uin)) _, err := c.sendAndWait(c.buildFriendDeletePacket(uin))
return errors.Wrap(err, "delete friend error") return errors.Wrap(err, "delete friend error")
} }
@ -721,7 +641,7 @@ func (c *QQClient) FindGroup(code int64) *GroupInfo {
return nil return nil
} }
func (c *QQClient) SolveGroupJoinRequest(i interface{}, accept, block bool, reason string) { func (c *QQClient) SolveGroupJoinRequest(i any, accept, block bool, reason string) {
if accept { if accept {
block = false block = false
reason = "" reason = ""
@ -729,28 +649,29 @@ func (c *QQClient) SolveGroupJoinRequest(i interface{}, accept, block bool, reas
switch req := i.(type) { switch req := i.(type) {
case *UserJoinGroupRequest: case *UserJoinGroupRequest:
call := c.buildSystemMsgGroupActionPacket(req.RequestId, req.RequesterUin, req.GroupCode, func() int32 { _, pkt := c.buildSystemMsgGroupActionPacket(req.RequestId, req.RequesterUin, req.GroupCode, func() int32 {
if req.Suspicious { if req.Suspicious {
return 2 return 2
} else { } else {
return 1 return 1
} }
}(), false, accept, block, reason) }(), false, accept, block, reason)
_, _ = c.call(call) _ = c.sendPacket(pkt)
case *GroupInvitedRequest: case *GroupInvitedRequest:
call := c.buildSystemMsgGroupActionPacket(req.RequestId, req.InvitorUin, req.GroupCode, 1, true, accept, block, reason) _, pkt := c.buildSystemMsgGroupActionPacket(req.RequestId, req.InvitorUin, req.GroupCode, 1, true, accept, block, reason)
_, _ = c.call(call) _ = c.sendPacket(pkt)
} }
} }
func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) { func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) {
_, _ = c.call(c.buildSystemMsgFriendActionPacket(req.RequestId, req.RequesterUin, accept)) _, pkt := c.buildSystemMsgFriendActionPacket(req.RequestId, req.RequesterUin, accept)
_ = c.sendPacket(pkt)
} }
func (c *QQClient) getSKey() string { func (c *QQClient) getSKey() string {
if c.sig.SKeyExpiredTime < time.Now().Unix() && len(c.sig.G) > 0 { if c.sig.SKeyExpiredTime < time.Now().Unix() && len(c.sig.G) > 0 {
c.Debug("skey expired. refresh...") c.debug("skey expired. refresh...")
_, _ = c.callAndDecode(c.buildRequestTgtgtNopicsigRequest()) _, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket())
} }
return string(c.sig.SKey) return string(c.sig.SKey)
} }
@ -778,39 +699,35 @@ func (c *QQClient) getCSRFToken() int {
} }
func (c *QQClient) editMemberCard(groupCode, memberUin int64, card string) { func (c *QQClient) editMemberCard(groupCode, memberUin int64, card string) {
_, _ = c.call(c.buildEditGroupTagPacket(groupCode, memberUin, card)) _, _ = c.sendAndWait(c.buildEditGroupTagPacket(groupCode, memberUin, card))
} }
func (c *QQClient) editMemberSpecialTitle(groupCode, memberUin int64, title string) { func (c *QQClient) editMemberSpecialTitle(groupCode, memberUin int64, title string) {
_, _ = c.call(c.buildEditSpecialTitlePacket(groupCode, memberUin, title)) _, _ = c.sendAndWait(c.buildEditSpecialTitlePacket(groupCode, memberUin, title))
} }
func (c *QQClient) setGroupAdmin(groupCode, memberUin int64, flag bool) { func (c *QQClient) setGroupAdmin(groupCode, memberUin int64, flag bool) {
_, _ = c.call(c.buildGroupAdminSetPacket(groupCode, memberUin, flag)) _, _ = c.sendAndWait(c.buildGroupAdminSetPacket(groupCode, memberUin, flag))
} }
func (c *QQClient) updateGroupName(groupCode int64, newName string) { func (c *QQClient) updateGroupName(groupCode int64, newName string) {
_, _ = c.call(c.buildGroupNameUpdateRequest(groupCode, newName)) _, _ = c.sendAndWait(c.buildGroupNameUpdatePacket(groupCode, newName))
}
func (c *QQClient) updateGroupMemo(groupCode int64, newMemo string) {
_, _ = c.call(c.buildGroupMemoUpdatePacket(groupCode, newMemo))
} }
func (c *QQClient) groupMuteAll(groupCode int64, mute bool) { func (c *QQClient) groupMuteAll(groupCode int64, mute bool) {
_, _ = c.call(c.buildGroupMuteAllPacket(groupCode, mute)) _, _ = c.sendAndWait(c.buildGroupMuteAllPacket(groupCode, mute))
} }
func (c *QQClient) groupMute(groupCode, memberUin int64, time uint32) { func (c *QQClient) groupMute(groupCode, memberUin int64, time uint32) {
_, _ = c.call(c.buildGroupMutePacket(groupCode, memberUin, time)) _, _ = c.sendAndWait(c.buildGroupMutePacket(groupCode, memberUin, time))
} }
func (c *QQClient) quitGroup(groupCode int64) { func (c *QQClient) quitGroup(groupCode int64) {
_, _ = c.call(c.buildQuitGroupPacket(groupCode)) _, _ = c.sendAndWait(c.buildQuitGroupPacket(groupCode))
} }
func (c *QQClient) kickGroupMember(groupCode, memberUin int64, msg string, block bool) { func (c *QQClient) KickGroupMembers(groupCode int64, msg string, block bool, memberUins ...int64) {
_, _ = c.call(c.buildGroupKickPacket(groupCode, memberUin, msg, block)) _, _ = c.sendAndWait(c.buildGroupKickPacket(groupCode, msg, block, memberUins...))
} }
func (g *GroupInfo) removeMember(uin int64) { func (g *GroupInfo) removeMember(uin int64) {
@ -825,14 +742,21 @@ func (g *GroupInfo) removeMember(uin int64) {
}) })
} }
func (c *QQClient) SetCustomServer(servers []*net.TCPAddr) { func (c *QQClient) setGroupAnonymous(groupCode int64, enable bool) {
for _, server := range servers { _, _ = c.sendAndWait(c.buildSetGroupAnonymous(groupCode, enable))
c.transport.AddServerAddr(server) }
}
// UpdateProfile 修改个人资料
func (c *QQClient) UpdateProfile(profile ProfileDetailUpdate) {
_, _ = c.sendAndWait(c.buildUpdateProfileDetailPacket(profile))
}
func (c *QQClient) SetCustomServer(servers []netip.AddrPort) {
c.servers = append(servers, c.servers...)
} }
func (c *QQClient) registerClient() error { func (c *QQClient) registerClient() error {
_, err := c.callAndDecode(c.buildClientRegisterPacket()) _, err := c.sendAndWait(c.buildClientRegisterPacket())
if err == nil { if err == nil {
c.Online.Store(true) c.Online.Store(true)
} }
@ -840,7 +764,12 @@ func (c *QQClient) registerClient() error {
} }
func (c *QQClient) nextSeq() uint16 { func (c *QQClient) nextSeq() uint16 {
return uint16(c.SequenceId.Add(1) & 0x7FFF) seq := c.SequenceId.Add(1)
if seq > 1000000 {
seq = int32(rand.Intn(100000)) + 60000
c.SequenceId.Store(seq)
}
return uint16(seq)
} }
func (c *QQClient) nextPacketSeq() int32 { func (c *QQClient) nextPacketSeq() int32 {
@ -864,21 +793,10 @@ func (c *QQClient) nextHighwayApplySeq() int32 {
} }
func (c *QQClient) doHeartbeat() { func (c *QQClient) doHeartbeat() {
// 不需要atomic/锁
if c.heartbeatEnabled {
return
}
c.heartbeatEnabled = true c.heartbeatEnabled = true
defer func() {
c.heartbeatEnabled = false
}()
times := 0 times := 0
ticker := time.NewTicker(time.Second * 30) for c.Online.Load() {
for range ticker.C { time.Sleep(time.Second * 30)
if !c.Online.Load() {
ticker.Stop()
return // 下线停止goroutinefor gc
}
seq := c.nextSeq() seq := c.nextSeq()
req := network.Request{ req := network.Request{
Type: network.RequestTypeLogin, Type: network.RequestTypeLogin,
@ -888,12 +806,10 @@ func (c *QQClient) doHeartbeat() {
CommandName: "Heartbeat.Alive", CommandName: "Heartbeat.Alive",
Body: EmptyBytes, Body: EmptyBytes,
} }
_, err := c.call(&req) packet := c.transport.PackPacket(&req)
if err != nil { _, err := c.sendAndWait(seq, packet)
if errors.Is(err, network.ErrConnectionBroken) { if errors.Is(err, network.ErrConnectionClosed) {
break continue
}
continue // skip time++
} }
times++ times++
if times >= 7 { if times >= 7 {
@ -901,4 +817,5 @@ func (c *QQClient) doHeartbeat() {
times = 0 times = 0
} }
} }
c.heartbeatEnabled = false
} }

View File

@ -2,9 +2,8 @@ package client
import ( import (
"crypto/md5" "crypto/md5"
"encoding/hex"
"fmt" "fmt"
"net" "net/netip"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -14,7 +13,9 @@ import (
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/binary/jce" "github.com/Mrs4s/MiraiGo/binary/jce"
"github.com/Mrs4s/MiraiGo/client/internal/auth"
"github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/tlv"
"github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x352" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x352"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff"
@ -32,29 +33,36 @@ var (
) )
// wtlogin.login // wtlogin.login
func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
reader := binary.NewReader(resp.Body) reader := binary.NewReader(pkt.Payload)
reader.ReadUInt16() // sub command reader.ReadUInt16() // sub command
t := reader.ReadByte() t := reader.ReadByte()
reader.ReadUInt16() reader.ReadUInt16()
m := reader.ReadTlvMap(2) m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(reader.ReadAvailable())
if err != nil {
return nil, err
}
if m.Exists(0x402) { if m.Exists(0x402) {
c.sig.Dpwd = []byte(utils.RandomString(16)) c.sig.Dpwd = []byte(utils.RandomString(16))
c.sig.T402 = m[0x402] c.sig.T402 = m[0x402]
h := md5.Sum(append(append(c.deviceInfo.Guid, c.sig.Dpwd...), c.sig.T402...)) h := md5.Sum(append(append(c.Device().Guid, c.sig.Dpwd...), c.sig.T402...))
c.sig.G = h[:] c.sig.G = h[:]
} }
if m.Exists(0x546) {
c.sig.T547 = auth.CalcPow(m[0x546])
}
// c.logger.Info("login response %v", t)
if t == 0 { // login success if t == 0 { // login success
// if t150, ok := m[0x150]; ok { // if t150, ok := m[0x150]; ok {
// c.t150 = t150 // c.t150 = t150
// }
// if t161, ok := m[0x161]; ok {
// c.decodeT161(t161)
// } // }
if t161, ok := m[0x161]; ok {
c.decodeT161(t161)
}
if m.Exists(0x403) { if m.Exists(0x403) {
c.sig.RandSeed = m[0x403] c.sig.RandSeed = m[0x403]
} }
c.decodeT119(m[0x119], c.deviceInfo.TgtgtKey) c.decodeT119(m[0x119], c.Device().TgtgtKey)
return LoginResponse{ return LoginResponse{
Success: true, Success: true,
}, nil }, nil
@ -64,6 +72,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
if m.Exists(0x192) { if m.Exists(0x192) {
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
VerifyUrl: string(m[0x192]), VerifyUrl: string(m[0x192]),
Error: SliderNeededError, Error: SliderNeededError,
}, nil }, nil
@ -75,6 +84,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
sign := imgData.ReadBytes(int(signLen)) sign := imgData.ReadBytes(int(signLen))
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: NeedCaptcha, Error: NeedCaptcha,
CaptchaImage: imgData.ReadAvailable(), CaptchaImage: imgData.ReadAvailable(),
CaptchaSign: sign, CaptchaSign: sign,
@ -82,6 +92,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
} else { } else {
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: UnknownLoginError, Error: UnknownLoginError,
}, nil }, nil
} }
@ -90,6 +101,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
if t == 40 { if t == 40 {
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
ErrorMessage: "账号被冻结", ErrorMessage: "账号被冻结",
Error: UnknownLoginError, Error: UnknownLoginError,
}, nil }, nil
@ -102,11 +114,13 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
c.sig.RandSeed = m[0x403] c.sig.RandSeed = m[0x403]
phone := func() string { phone := func() string {
r := binary.NewReader(m[0x178]) r := binary.NewReader(m[0x178])
return r.ReadStringLimit(int(r.ReadInt32())) r.ReadStringShort()
return r.ReadStringShort()
}() }()
if t204, ok := m[0x204]; ok { // 同时支持扫码验证 ? if t204, ok := m[0x204]; ok { // 同时支持扫码验证 ?
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: SMSOrVerifyNeededError, Error: SMSOrVerifyNeededError,
VerifyUrl: string(t204), VerifyUrl: string(t204),
SMSPhone: phone, SMSPhone: phone,
@ -115,6 +129,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
} }
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: SMSNeededError, Error: SMSNeededError,
SMSPhone: phone, SMSPhone: phone,
ErrorMessage: string(m[0x17e]), ErrorMessage: string(m[0x17e]),
@ -125,6 +140,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
c.sig.T104 = m[0x104] c.sig.T104 = m[0x104]
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: SMSNeededError, Error: SMSNeededError,
}, nil }, nil
} }
@ -132,6 +148,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
if t204, ok := m[0x204]; ok { // 扫码验证 if t204, ok := m[0x204]; ok { // 扫码验证
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: UnsafeDeviceError, Error: UnsafeDeviceError,
VerifyUrl: string(t204), VerifyUrl: string(t204),
ErrorMessage: "", ErrorMessage: "",
@ -141,6 +158,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
if t == 162 { if t == 162 {
return LoginResponse{ return LoginResponse{
Code: t,
Error: TooManySMSRequestError, Error: TooManySMSRequestError,
}, nil }, nil
} }
@ -148,7 +166,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
if t == 204 { if t == 204 {
c.sig.T104 = m[0x104] c.sig.T104 = m[0x104]
c.sig.RandSeed = m[0x403] c.sig.RandSeed = m[0x403]
return c.callAndDecode(c.buildDeviceLockLoginRequest()) return c.sendAndWait(c.buildDeviceLockLoginPacket())
} // drive lock } // drive lock
if t149, ok := m[0x149]; ok { if t149, ok := m[0x149]; ok {
@ -157,6 +175,7 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
t149r.ReadStringShort() // title t149r.ReadStringShort() // title
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: OtherLoginError, Error: OtherLoginError,
ErrorMessage: t149r.ReadStringShort(), ErrorMessage: t149r.ReadStringShort(),
}, nil }, nil
@ -168,28 +187,29 @@ func decodeLoginResponse(c *QQClient, resp *network.Response) (interface{}, erro
t146r.ReadStringShort() // title t146r.ReadStringShort() // title
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: OtherLoginError, Error: OtherLoginError,
ErrorMessage: t146r.ReadStringShort(), ErrorMessage: t146r.ReadStringShort(),
}, nil }, nil
} }
c.Debug("unknown login response: %v", t) c.debug("unknown login response: %v", t)
for k, v := range m { for k, v := range m {
c.Debug("Type: %v Value: %v", strconv.FormatInt(int64(k), 16), hex.EncodeToString(v)) c.debug("Type: %d Value: %x", k, v)
} }
return nil, errors.Errorf("unknown login response: %v", t) // ? return nil, errors.Errorf("unknown login response: %v", t) // ?
} }
// StatSvc.register // StatSvc.register
func decodeClientRegisterResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeClientRegisterResponse(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{} data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
svcRsp := &jce.SvcRespRegister{} svcRsp := &jce.SvcRespRegister{}
svcRsp.ReadFrom(jce.NewJceReader(data.Map["SvcRespRegister"]["QQService.SvcRespRegister"][1:])) svcRsp.ReadFrom(jce.NewJceReader(data.Map["SvcRespRegister"]["QQService.SvcRespRegister"][1:]))
if svcRsp.Result != "" || svcRsp.ReplyCode != 0 { if svcRsp.Result != "" || svcRsp.ReplyCode != 0 {
if svcRsp.Result != "" { if svcRsp.Result != "" {
c.Error("reg error: %v", svcRsp.Result) c.error("reg error: %v", svcRsp.Result)
} }
return nil, errors.New("reg failed") return nil, errors.New("reg failed")
} }
@ -197,12 +217,15 @@ func decodeClientRegisterResponse(c *QQClient, resp *network.Response) (interfac
} }
// wtlogin.exchange_emp // wtlogin.exchange_emp
func decodeExchangeEmpResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeExchangeEmpResponse(c *QQClient, pkt *network.Packet) (any, error) {
reader := binary.NewReader(resp.Body) reader := binary.NewReader(pkt.Payload)
cmd := reader.ReadUInt16() cmd := reader.ReadUInt16()
t := reader.ReadByte() t := reader.ReadByte()
reader.ReadUInt16() reader.ReadUInt16()
m := reader.ReadTlvMap(2) m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(reader.ReadAvailable())
if err != nil {
return nil, err
}
if t != 0 { if t != 0 {
return nil, errors.Errorf("exchange_emp failed: %v", t) return nil, errors.Errorf("exchange_emp failed: %v", t)
} }
@ -217,11 +240,11 @@ func decodeExchangeEmpResponse(c *QQClient, resp *network.Response) (interface{}
} }
// wtlogin.trans_emp // wtlogin.trans_emp
func decodeTransEmpResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeTransEmpResponse(c *QQClient, pkt *network.Packet) (any, error) {
if len(resp.Body) < 48 { if len(pkt.Payload) < 48 {
return nil, errors.New("missing payload length") return nil, errors.New("missing payload length")
} }
reader := binary.NewReader(resp.Body) reader := binary.NewReader(pkt.Payload)
reader.ReadBytes(5) // trans req head reader.ReadBytes(5) // trans req head
reader.ReadByte() reader.ReadByte()
reader.ReadUInt16() reader.ReadUInt16()
@ -242,7 +265,10 @@ func decodeTransEmpResponse(c *QQClient, resp *network.Response) (interface{}, e
} }
sig := body.ReadBytesShort() sig := body.ReadBytesShort()
body.ReadUInt16() body.ReadUInt16()
m := body.ReadTlvMap(2) m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(body.ReadAvailable())
if err != nil {
return nil, err
}
if m.Exists(0x17) { if m.Exists(0x17) {
return &QRCodeLoginResponse{ return &QRCodeLoginResponse{
State: QRCodeImageFetch, State: QRCodeImageFetch,
@ -267,30 +293,32 @@ func decodeTransEmpResponse(c *QQClient, resp *network.Response) (interface{}, e
body.ReadInt32() // app id? body.ReadInt32() // app id?
code := body.ReadByte() code := body.ReadByte()
if code != 0 { if code != 0 {
var qrResp QRCodeLoginResponse if code == 0x30 {
switch code { return &QRCodeLoginResponse{State: QRCodeWaitingForScan}, nil
case 0x30:
qrResp.State = QRCodeWaitingForScan
case 0x35:
qrResp.State = QRCodeWaitingForConfirm
case 0x36:
qrResp.State = QRCodeCanceled
case 0x11:
qrResp.State = QRCodeTimeout
default:
return nil, errors.Errorf("wtlogin.trans_emp sub cmd 0x12 error: %v", code)
} }
return qrResp, nil if code == 0x35 {
return &QRCodeLoginResponse{State: QRCodeWaitingForConfirm}, nil
}
if code == 0x36 {
return &QRCodeLoginResponse{State: QRCodeCanceled}, nil
}
if code == 0x11 {
return &QRCodeLoginResponse{State: QRCodeTimeout}, nil
}
return nil, errors.Errorf("wtlogin.trans_emp sub cmd 0x12 error: %v", code)
} }
c.Uin = body.ReadInt64() c.Uin = body.ReadInt64()
c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10) c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10)
body.ReadInt32() // sig create time body.ReadInt32() // sig create time
body.ReadUInt16() body.ReadUInt16()
m := body.ReadTlvMap(2) m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(body.ReadAvailable())
if err != nil {
return nil, err
}
if !m.Exists(0x18) || !m.Exists(0x1e) || !m.Exists(0x19) { if !m.Exists(0x18) || !m.Exists(0x1e) || !m.Exists(0x19) {
return nil, errors.New("wtlogin.trans_emp sub cmd 0x12 error: tlv error") return nil, errors.New("wtlogin.trans_emp sub cmd 0x12 error: tlv error")
} }
c.deviceInfo.TgtgtKey = m[0x1e] c.Device().TgtgtKey = m[0x1e]
return &QRCodeLoginResponse{State: QRCodeConfirmed, LoginInfo: &QRCodeLoginInfo{ return &QRCodeLoginResponse{State: QRCodeConfirmed, LoginInfo: &QRCodeLoginInfo{
tmpPwd: m[0x18], tmpPwd: m[0x18],
tmpNoPicSig: m[0x19], tmpNoPicSig: m[0x19],
@ -301,9 +329,9 @@ func decodeTransEmpResponse(c *QQClient, resp *network.Response) (interface{}, e
} }
// ConfigPushSvc.PushReq // ConfigPushSvc.PushReq
func decodePushReqPacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{} data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["PushReq"]["ConfigPush.PushReq"][1:]) r := jce.NewJceReader(data.Map["PushReq"]["ConfigPush.PushReq"][1:])
@ -315,16 +343,16 @@ func decodePushReqPacket(c *QQClient, resp *network.Response) (interface{}, erro
ssoPkt := jce.NewJceReader(jceBuf) ssoPkt := jce.NewJceReader(jceBuf)
servers := ssoPkt.ReadSsoServerInfos(1) servers := ssoPkt.ReadSsoServerInfos(1)
if len(servers) > 0 { if len(servers) > 0 {
var adds []*net.TCPAddr var adds []netip.AddrPort
for _, s := range servers { for _, s := range servers {
if strings.Contains(s.Server, "com") { if strings.Contains(s.Server, "com") {
continue continue
} }
c.Debug("got new server addr: %v location: %v", s.Server, s.Location) c.debug("got new server addr: %v location: %v", s.Server, s.Location)
adds = append(adds, &net.TCPAddr{ addr, err := netip.ParseAddr(s.Server)
IP: net.ParseIP(s.Server), if err == nil {
Port: int(s.Port), adds = append(adds, netip.AddrPortFrom(addr, uint16(s.Port)))
}) }
} }
f := true f := true
for _, e := range c.eventHandlers.serverUpdatedHandlers { for _, e := range c.eventHandlers.serverUpdatedHandlers {
@ -343,52 +371,55 @@ func decodePushReqPacket(c *QQClient, resp *network.Response) (interface{}, erro
fmtPkt := jce.NewJceReader(jceBuf) fmtPkt := jce.NewJceReader(jceBuf)
list := &jce.FileStoragePushFSSvcList{} list := &jce.FileStoragePushFSSvcList{}
list.ReadFrom(fmtPkt) list.ReadFrom(fmtPkt)
c.Debug("got file storage svc push.") c.debug("got file storage svc push.")
c.fileStorageInfo = list // c.fileStorageInfo = list
rsp := cmd0x6ff.C501RspBody{} rsp := cmd0x6ff.C501RspBody{}
if err := proto.Unmarshal(list.BigDataChannel.PbBuf, &rsp); err == nil && rsp.RspBody != nil { if err := proto.Unmarshal(list.BigDataChannel.PbBuf, &rsp); err == nil && rsp.RspBody != nil {
c.highwaySession.SigSession = rsp.RspBody.SigSession c.highwaySession.SigSession = rsp.RspBody.SigSession
c.highwaySession.SessionKey = rsp.RspBody.SessionKey c.highwaySession.SessionKey = rsp.RspBody.SessionKey
for _, srv := range rsp.RspBody.Addrs { for _, srv := range rsp.RspBody.Addrs {
if srv.GetServiceType() == 10 { if srv.ServiceType.Unwrap() == 10 {
for _, addr := range srv.Addrs { for _, addr := range srv.Addrs {
c.highwaySession.AppendAddr(addr.GetIp(), addr.GetPort()) c.highwaySession.AppendAddr(addr.Ip.Unwrap(), addr.Port.Unwrap())
} }
} }
if srv.GetServiceType() == 21 { /*
for _, addr := range srv.Addrs { if srv.ServiceType.Unwrap() == 21 {
c.otherSrvAddrs = append(c.otherSrvAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr.GetIp()), addr.GetPort())) for _, addr := range srv.Addrs {
c.otherSrvAddrs = append(c.otherSrvAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr.Ip.Unwrap()), addr.Port.Unwrap()))
}
} }
}
*/
} }
} }
} }
} }
seq := r.ReadInt64(3) seq := r.ReadInt64(3)
err := c.sendPacket(c.transport.PackPacket(c.buildConfPushRespPacket(t, seq, jceBuf))) _, resp := c.buildConfPushRespPacket(t, seq, jceBuf)
return nil, err return nil, c.sendPacket(resp)
} }
// MessageSvc.PbGetMsg // MessageSvc.PbGetMsg
func decodeMessageSvcPacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodeMessageSvcPacket(c *QQClient, pkt *network.Packet) (any, error) {
rsp := msg.GetMessageResponse{} rsp := msg.GetMessageResponse{}
err := proto.Unmarshal(resp.Body, &rsp) err := proto.Unmarshal(pkt.Payload, &rsp)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
c.c2cMessageSyncProcessor(&rsp, resp) c.c2cMessageSyncProcessor(&rsp, pkt.Params)
return nil, nil return nil, nil
} }
// MessageSvc.PushNotify // MessageSvc.PushNotify
func decodeSvcNotify(c *QQClient, resp *network.Response) (interface{}, error) { func decodeSvcNotify(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body[4:])) request.ReadFrom(jce.NewJceReader(pkt.Payload[4:]))
data := &jce.RequestDataVersion2{} data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
if len(data.Map) == 0 { if len(data.Map) == 0 {
_, err := c.callAndDecode(c.buildGetMessageRequest(msg.SyncFlag_START, time.Now().Unix())) _, err := c.sendAndWait(c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix()))
return nil, err return nil, err
} }
notify := &jce.RequestPushNotify{} notify := &jce.RequestPushNotify{}
@ -400,18 +431,18 @@ func decodeSvcNotify(c *QQClient, resp *network.Response) (interface{}, error) {
return nil, nil return nil, nil
} }
if typ == sysMsgDecoders { if typ == sysMsgDecoders {
_, err := c.callAndDecode(c.buildSystemMsgNewFriendRequest()) _, pkt := c.buildSystemMsgNewFriendPacket()
return nil, err return nil, c.sendPacket(pkt)
} }
} }
_, err := c.callAndDecode(c.buildGetMessageRequest(msg.SyncFlag_START, time.Now().Unix())) _, err := c.sendAndWait(c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix()))
return nil, err return nil, err
} }
// SummaryCard.ReqSummaryCard // SummaryCard.ReqSummaryCard
func decodeSummaryCardResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeSummaryCardResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{} data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
rsp := func() *jce.JceReader { rsp := func() *jce.JceReader {
@ -421,16 +452,34 @@ func decodeSummaryCardResponse(_ *QQClient, resp *network.Response) (interface{}
return jce.NewJceReader(data.Map["RespSummaryCard"]["SummaryCard_Old.RespSummaryCard"][1:]) return jce.NewJceReader(data.Map["RespSummaryCard"]["SummaryCard_Old.RespSummaryCard"][1:])
}() }()
info := &SummaryCardInfo{ info := &SummaryCardInfo{
Sex: rsp.ReadByte(1), Sex: rsp.ReadByte(1),
Age: rsp.ReadByte(2), Age: rsp.ReadByte(2),
Nickname: rsp.ReadString(3), Nickname: rsp.ReadString(3),
Level: rsp.ReadInt32(5), Level: rsp.ReadInt32(5),
City: rsp.ReadString(7), City: rsp.ReadString(7),
Sign: rsp.ReadString(8), Sign: rsp.ReadString(8),
Mobile: rsp.ReadString(11), Mobile: rsp.ReadString(11),
Uin: rsp.ReadInt64(23), Uin: rsp.ReadInt64(23),
LoginDays: rsp.ReadInt64(36),
} }
vipInfo := rsp.ReadMapIntVipInfo(29) // 1 -> vip, 3 -> svip
if v1, v3 := vipInfo[1], vipInfo[3]; v1 != nil || v3 != nil {
if v1.Open != 0 {
info.VipLevel = fmt.Sprintf("vip%d", v1.Level)
}
if v3.Open != 0 {
info.VipLevel = fmt.Sprintf("svip%d", v3.Level)
}
}
richSign := rsp.ReadBytes(32)
records, _ := tlv.NewDecoder(1, 1).Decode(richSign)
for _, r := range records {
if r.Tag == 3 {
info.Sign = string(r.Value)
}
}
info.LoginDays = rsp.ReadInt64(36)
services := rsp.ReadByteArrArr(46) services := rsp.ReadByteArrArr(46)
readService := func(buf []byte) (*profilecard.BusiComm, []byte) { readService := func(buf []byte) (*profilecard.BusiComm, []byte) {
r := binary.NewReader(buf) r := binary.NewReader(buf)
@ -445,11 +494,11 @@ func decodeSummaryCardResponse(_ *QQClient, resp *network.Response) (interface{}
} }
for _, buf := range services { for _, buf := range services {
comm, payload := readService(buf) comm, payload := readService(buf)
if comm.GetService() == 16 { if comm.Service.Unwrap() == 16 {
rsp := profilecard.GateVaProfileGateRsp{} rsp := profilecard.GateVaProfileGateRsp{}
_ = proto.Unmarshal(payload, &rsp) _ = proto.Unmarshal(payload, &rsp)
if rsp.QidInfo != nil { if rsp.QidInfo != nil {
info.Qid = rsp.QidInfo.GetQid() info.Qid = rsp.QidInfo.Qid.Unwrap()
} }
} }
} }
@ -457,9 +506,9 @@ func decodeSummaryCardResponse(_ *QQClient, resp *network.Response) (interface{}
} }
// friendlist.getFriendGroupList // friendlist.getFriendGroupList
func decodeFriendGroupListResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeFriendGroupListResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion3{} data := &jce.RequestDataVersion3{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["FLRESP"][1:]) r := jce.NewJceReader(data.Map["FLRESP"][1:])
@ -482,9 +531,9 @@ func decodeFriendGroupListResponse(_ *QQClient, resp *network.Response) (interfa
} }
// friendlist.delFriend // friendlist.delFriend
func decodeFriendDeleteResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeFriendDeleteResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion3{} data := &jce.RequestDataVersion3{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["DFRESP"][1:]) r := jce.NewJceReader(data.Map["DFRESP"][1:])
@ -495,9 +544,9 @@ func decodeFriendDeleteResponse(_ *QQClient, resp *network.Response) (interface{
} }
// friendlist.GetTroopListReqV2 // friendlist.GetTroopListReqV2
func decodeGroupListResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeGroupListResponse(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion3{} data := &jce.RequestDataVersion3{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["GetTroopListRespV2"][1:]) r := jce.NewJceReader(data.Map["GetTroopListRespV2"][1:])
@ -509,7 +558,6 @@ func decodeGroupListResponse(c *QQClient, resp *network.Response) (interface{},
Uin: g.GroupUin, Uin: g.GroupUin,
Code: g.GroupCode, Code: g.GroupCode,
Name: g.GroupName, Name: g.GroupName,
Memo: g.GroupMemo,
OwnerUin: g.GroupOwnerUin, OwnerUin: g.GroupOwnerUin,
MemberCount: uint16(g.MemberNum), MemberCount: uint16(g.MemberNum),
MaxMemberCount: uint16(g.MaxGroupMemberNum), MaxMemberCount: uint16(g.MaxGroupMemberNum),
@ -517,7 +565,7 @@ func decodeGroupListResponse(c *QQClient, resp *network.Response) (interface{},
}) })
} }
if len(vecCookie) > 0 { if len(vecCookie) > 0 {
rsp, err := c.callAndDecode(c.buildGroupListRequest(vecCookie)) rsp, err := c.sendAndWait(c.buildGroupListRequestPacket(vecCookie))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -527,9 +575,9 @@ func decodeGroupListResponse(c *QQClient, resp *network.Response) (interface{},
} }
// friendlist.GetTroopMemberListReq // friendlist.GetTroopMemberListReq
func decodeGroupMemberListResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeGroupMemberListResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion3{} data := &jce.RequestDataVersion3{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["GTMLRESP"][1:]) r := jce.NewJceReader(data.Map["GTMLRESP"][1:])
@ -537,23 +585,21 @@ func decodeGroupMemberListResponse(_ *QQClient, resp *network.Response) (interfa
next := r.ReadInt64(4) next := r.ReadInt64(4)
l := make([]*GroupMemberInfo, 0, len(members)) l := make([]*GroupMemberInfo, 0, len(members))
for _, m := range members { for _, m := range members {
permission := Member
if m.Flag&1 != 0 {
permission = Administrator
}
l = append(l, &GroupMemberInfo{ l = append(l, &GroupMemberInfo{
Uin: m.MemberUin, Uin: m.MemberUin,
Nickname: m.Nick, Nickname: m.Nick,
Gender: m.Gender, Gender: m.Gender,
CardName: m.Name, CardName: m.Name,
Level: uint16(m.MemberLevel), Level: uint16(m.MemberLevel),
JoinTime: m.JoinTime, JoinTime: m.JoinTime,
LastSpeakTime: m.LastSpeakTime, LastSpeakTime: m.LastSpeakTime,
SpecialTitle: m.SpecialTitle, SpecialTitle: m.SpecialTitle,
SpecialTitleExpireTime: m.SpecialTitleExpireTime, ShutUpTimestamp: m.ShutUpTimestap,
ShutUpTimestamp: m.ShutUpTimestap, Permission: permission,
Permission: func() MemberPermission {
if m.Flag == 1 {
return Administrator
}
return Member
}(),
}) })
} }
return &groupMemberListResponse{ return &groupMemberListResponse{
@ -563,105 +609,103 @@ func decodeGroupMemberListResponse(_ *QQClient, resp *network.Response) (interfa
} }
// group_member_card.get_group_member_card_info // group_member_card.get_group_member_card_info
func decodeGroupMemberInfoResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeGroupMemberInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := pb.GroupMemberRspBody{} rsp := pb.GroupMemberRspBody{}
if err := proto.Unmarshal(resp.Body, &rsp); err != nil { if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if rsp.MemInfo == nil || (rsp.MemInfo.Nick == nil && rsp.MemInfo.Age == 0) { if rsp.MemInfo == nil || (rsp.MemInfo.Nick == nil && rsp.MemInfo.Age == 0) {
return nil, errors.WithStack(ErrMemberNotFound) return nil, errors.WithStack(ErrMemberNotFound)
} }
group := c.FindGroup(rsp.GroupCode) group := c.FindGroup(rsp.GroupCode)
permission := Member
if rsp.MemInfo.Uin == group.OwnerUin {
permission = Owner
}
if rsp.MemInfo.Role == 2 {
permission = Administrator
}
return &GroupMemberInfo{ return &GroupMemberInfo{
Group: group, Group: group,
Uin: rsp.MemInfo.Uin, Uin: rsp.MemInfo.Uin,
Gender: byte(rsp.MemInfo.Sex), Gender: byte(rsp.MemInfo.Sex),
Nickname: string(rsp.MemInfo.Nick), Nickname: string(rsp.MemInfo.Nick),
CardName: string(rsp.MemInfo.Card), CardName: string(rsp.MemInfo.Card),
Level: uint16(rsp.MemInfo.Level), Level: uint16(rsp.MemInfo.Level),
JoinTime: rsp.MemInfo.Join, JoinTime: rsp.MemInfo.Join,
LastSpeakTime: rsp.MemInfo.LastSpeak, LastSpeakTime: rsp.MemInfo.LastSpeak,
SpecialTitle: string(rsp.MemInfo.SpecialTitle), SpecialTitle: string(rsp.MemInfo.SpecialTitle),
SpecialTitleExpireTime: int64(rsp.MemInfo.SpecialTitleExpireTime), Permission: permission,
Permission: func() MemberPermission {
if rsp.MemInfo.Uin == group.OwnerUin {
return Owner
}
if rsp.MemInfo.Role == 2 {
return Administrator
}
return Member
}(),
}, nil }, nil
} }
// LongConn.OffPicUp // LongConn.OffPicUp
func decodeOffPicUpResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeOffPicUpResponse(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := cmd0x352.RspBody{} rsp := cmd0x352.RspBody{}
if err := proto.Unmarshal(resp.Body, &rsp); err != nil { if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if rsp.GetFailMsg() != nil { if rsp.FailMsg != nil {
return &imageUploadResponse{ return &imageUploadResponse{
ResultCode: -1, ResultCode: -1,
Message: string(rsp.FailMsg), Message: string(rsp.FailMsg),
}, nil }, nil
} }
if rsp.GetSubcmd() != 1 || len(rsp.GetTryupImgRsp()) == 0 { if rsp.Subcmd.Unwrap() != 1 || len(rsp.TryupImgRsp) == 0 {
return &imageUploadResponse{ return &imageUploadResponse{
ResultCode: -2, ResultCode: -2,
}, nil }, nil
} }
imgRsp := rsp.GetTryupImgRsp()[0] imgRsp := rsp.TryupImgRsp[0]
if imgRsp.GetResult() != 0 { if imgRsp.Result.Unwrap() != 0 {
return &imageUploadResponse{ return &imageUploadResponse{
ResultCode: int32(*imgRsp.Result), ResultCode: int32(imgRsp.Result.Unwrap()),
Message: string(imgRsp.GetFailMsg()), Message: string(imgRsp.FailMsg),
}, nil }, nil
} }
if imgRsp.GetFileExit() { if imgRsp.FileExit.Unwrap() {
return &imageUploadResponse{ return &imageUploadResponse{
IsExists: true, IsExists: true,
ResourceId: string(imgRsp.GetUpResid()), ResourceId: string(imgRsp.UpResid),
}, nil }, nil
} }
return &imageUploadResponse{ return &imageUploadResponse{
ResourceId: string(imgRsp.GetUpResid()), ResourceId: string(imgRsp.UpResid),
UploadKey: imgRsp.GetUpUkey(), UploadKey: imgRsp.UpUkey,
UploadIp: imgRsp.GetUpIp(), UploadIp: imgRsp.UpIp,
UploadPort: imgRsp.GetUpPort(), UploadPort: imgRsp.UpPort,
}, nil }, nil
} }
// OnlinePush.PbPushTransMsg // OnlinePush.PbPushTransMsg
func decodeOnlinePushTransPacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodeOnlinePushTransPacket(c *QQClient, pkt *network.Packet) (any, error) {
info := msg.TransMsgInfo{} info := msg.TransMsgInfo{}
err := proto.Unmarshal(resp.Body, &info) err := proto.Unmarshal(pkt.Payload, &info)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
data := binary.NewReader(info.MsgData) data := binary.NewReader(info.MsgData)
idStr := strconv.FormatInt(info.GetMsgUid(), 10) idStr := strconv.FormatInt(info.MsgUid.Unwrap(), 10)
if _, ok := c.transCache.Get(idStr); ok { if _, ok := c.transCache.Get(idStr); ok {
return nil, nil return nil, nil
} }
c.transCache.Add(idStr, "", time.Second*15) c.transCache.Add(idStr, unit{}, time.Second*15)
if info.GetMsgType() == 34 { if info.MsgType.Unwrap() == 34 {
data.ReadInt32() data.ReadInt32()
data.ReadByte() data.ReadByte()
target := int64(uint32(data.ReadInt32())) target := int64(uint32(data.ReadInt32()))
typ := int32(data.ReadByte()) typ := int32(data.ReadByte())
operator := int64(uint32(data.ReadInt32())) operator := int64(uint32(data.ReadInt32()))
if g := c.FindGroupByUin(info.GetFromUin()); g != nil { if g := c.FindGroupByUin(info.FromUin.Unwrap()); g != nil {
groupLeaveLock.Lock() groupLeaveLock.Lock()
defer groupLeaveLock.Unlock() defer groupLeaveLock.Unlock()
switch typ { switch typ {
case 0x02: case 0x02:
if target == c.Uin { if target == c.Uin {
c.dispatchLeaveGroupEvent(&GroupLeaveEvent{Group: g}) c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{Group: g})
} else if m := g.FindMember(target); m != nil { } else if m := g.FindMember(target); m != nil {
g.removeMember(target) g.removeMember(target)
c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{ c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{
Group: g, Group: g,
Member: m, Member: m,
}) })
@ -671,13 +715,13 @@ func decodeOnlinePushTransPacket(c *QQClient, resp *network.Response) (interface
return nil, err return nil, err
} }
if target == c.Uin { if target == c.Uin {
c.dispatchLeaveGroupEvent(&GroupLeaveEvent{ c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{
Group: g, Group: g,
Operator: g.FindMember(operator), Operator: g.FindMember(operator),
}) })
} else if m := g.FindMember(target); m != nil { } else if m := g.FindMember(target); m != nil {
g.removeMember(target) g.removeMember(target)
c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{ c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{
Group: g, Group: g,
Member: m, Member: m,
Operator: g.FindMember(operator), Operator: g.FindMember(operator),
@ -686,7 +730,7 @@ func decodeOnlinePushTransPacket(c *QQClient, resp *network.Response) (interface
case 0x82: case 0x82:
if m := g.FindMember(target); m != nil { if m := g.FindMember(target); m != nil {
g.removeMember(target) g.removeMember(target)
c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{ c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{
Group: g, Group: g,
Member: m, Member: m,
}) })
@ -694,16 +738,25 @@ func decodeOnlinePushTransPacket(c *QQClient, resp *network.Response) (interface
case 0x83: case 0x83:
if m := g.FindMember(target); m != nil { if m := g.FindMember(target); m != nil {
g.removeMember(target) g.removeMember(target)
c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{ c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{
Group: g, Group: g,
Member: m, Member: m,
Operator: g.FindMember(operator), Operator: g.FindMember(operator),
}) })
} }
case 0x01, 0x81: // kosbot add: 群解散. 暂时这样 See https://github.com/lz1998/ricq/blob/064ddddca19aa0410e2514852e3a151fd9913371/ricq-core/src/command/online_push/decoder.rs#L86
c.GroupDisbandEvent.dispatch(c, &GroupDisbandEvent{
Group: g,
Operator: g.FindMember(operator),
Time: int64(info.MsgTime.Unwrap()),
})
if err = c.ReloadGroupList(); err != nil {
return nil, err
}
} }
} }
} }
if info.GetMsgType() == 44 { if info.MsgType.Unwrap() == 44 {
data.ReadBytes(5) data.ReadBytes(5)
var4 := int32(data.ReadByte()) var4 := int32(data.ReadByte())
var5 := int64(0) var5 := int64(0)
@ -711,19 +764,17 @@ func decodeOnlinePushTransPacket(c *QQClient, resp *network.Response) (interface
if var4 != 0 && var4 != 1 { if var4 != 0 && var4 != 1 {
var5 = int64(uint32(data.ReadInt32())) var5 = int64(uint32(data.ReadInt32()))
} }
if g := c.FindGroupByUin(info.GetFromUin()); g != nil { if g := c.FindGroupByUin(info.FromUin.Unwrap()); g != nil {
if var5 == 0 && data.Len() == 1 { if var5 == 0 && data.Len() == 1 {
newPermission := func() MemberPermission { newPermission := Member
if data.ReadByte() == 1 { if data.ReadByte() == 1 {
return Administrator newPermission = Administrator
} }
return Member
}()
mem := g.FindMember(target) mem := g.FindMember(target)
if mem.Permission != newPermission { if mem.Permission != newPermission {
old := mem.Permission old := mem.Permission
mem.Permission = newPermission mem.Permission = newPermission
c.dispatchPermissionChanged(&MemberPermissionChangedEvent{ c.GroupMemberPermissionChangedEvent.dispatch(c, &MemberPermissionChangedEvent{
Group: g, Group: g,
Member: mem, Member: mem,
OldPermission: old, OldPermission: old,
@ -737,9 +788,9 @@ func decodeOnlinePushTransPacket(c *QQClient, resp *network.Response) (interface
} }
// ProfileService.Pb.ReqSystemMsgNew.Friend // ProfileService.Pb.ReqSystemMsgNew.Friend
func decodeSystemMsgFriendPacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodeSystemMsgFriendPacket(c *QQClient, pkt *network.Packet) (any, error) {
rsp := structmsg.RspSystemMsgNew{} rsp := structmsg.RspSystemMsgNew{}
if err := proto.Unmarshal(resp.Body, &rsp); err != nil { if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if len(rsp.Friendmsgs) == 0 { if len(rsp.Friendmsgs) == 0 {
@ -747,7 +798,7 @@ func decodeSystemMsgFriendPacket(c *QQClient, resp *network.Response) (interface
} }
st := rsp.Friendmsgs[0] st := rsp.Friendmsgs[0]
if st.Msg != nil { if st.Msg != nil {
c.dispatchNewFriendRequest(&NewFriendRequest{ c.NewFriendRequestEvent.dispatch(c, &NewFriendRequest{
RequestId: st.MsgSeq, RequestId: st.MsgSeq,
Message: st.Msg.MsgAdditional, Message: st.Msg.MsgAdditional,
RequesterUin: st.ReqUin, RequesterUin: st.ReqUin,
@ -759,36 +810,33 @@ func decodeSystemMsgFriendPacket(c *QQClient, resp *network.Response) (interface
} }
// MessageSvc.PushForceOffline // MessageSvc.PushForceOffline
func decodeForceOfflinePacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodeForceOfflinePacket(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{} data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["req_PushForceOffline"]["PushNotifyPack.RequestPushForceOffline"][1:]) r := jce.NewJceReader(data.Map["req_PushForceOffline"]["PushNotifyPack.RequestPushForceOffline"][1:])
tips := r.ReadString(2) tips := r.ReadString(2)
c.Disconnect() c.Disconnect()
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: tips}) go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: tips})
return nil, nil return nil, nil
} }
// StatSvc.ReqMSFOffline // StatSvc.ReqMSFOffline
func decodeMSFOfflinePacket(c *QQClient, _ *network.Response) (interface{}, error) { func decodeMSFOfflinePacket(c *QQClient, _ *network.Packet) (any, error) {
// c.lastLostMsg = "服务器端强制下线." // c.lastLostMsg = "服务器端强制下线."
c.Disconnect() c.Disconnect()
// 这个decoder不能消耗太多时间, event另起线程处理 // 这个decoder不能消耗太多时间, event另起线程处理
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "服务端强制下线."}) go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "服务端强制下线."})
return nil, nil return nil, nil
} }
// OidbSvc.0xd79 // OidbSvc.0xd79
func decodeWordSegmentation(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeWordSegmentation(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{} rsp := oidb.D79RspBody{}
rsp := &oidb.D79RspBody{} err := unpackOIDBPackage(pkt.Payload, &rsp)
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, err
}
if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if rsp.Content != nil { if rsp.Content != nil {
return rsp.Content.SliceContent, nil return rsp.Content.SliceContent, nil
@ -796,28 +844,30 @@ func decodeWordSegmentation(_ *QQClient, resp *network.Response) (interface{}, e
return nil, errors.New("no word received") return nil, errors.New("no word received")
} }
func decodeSidExpiredPacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodeSidExpiredPacket(c *QQClient, pkt *network.Packet) (any, error) {
_, err := c.callAndDecode(c.buildRequestChangeSigRequest(3554528)) /*
if err != nil { _, err := c.sendAndWait(c.buildRequestChangeSigPacket(true))
return nil, errors.Wrap(err, "resign client error") if err != nil {
} return nil, errors.Wrap(err, "resign client error")
if err = c.registerClient(); err != nil { }
return nil, errors.Wrap(err, "register error") if err = c.registerClient(); err != nil {
} return nil, errors.Wrap(err, "register error")
_, _ = c.call(c.uniPacketWithSeq(uint16(resp.SequenceID), "OnlinePush.SidTicketExpired", EmptyBytes, nil)) }
_ = c.sendPacket(c.uniPacketWithSeq(i.SequenceId, "OnlinePush.SidTicketExpired", EmptyBytes))
*/
return nil, nil return nil, nil
} }
/* unused /* unused
// LightAppSvc.mini_app_info.GetAppInfoById // LightAppSvc.mini_app_info.GetAppInfoById
func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo) (interface{}, error) { func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := qweb.QWebRsp{} pkg := qweb.QWebRsp{}
rsp := qweb.GetAppInfoByIdRsp{} rsp := qweb.GetAppInfoByIdRsp{}
if err := proto.Unmarshal(payload, &pkg); err != nil { if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if pkg.GetRetCode() != 0 { if pkg.RetCode.Unwrap() != 0 {
return nil, errors.New(pkg.GetErrMsg()) return nil, errors.New(pkg.ErrMsg.Unwrap())
} }
if err := proto.Unmarshal(pkg.BusiBuff, &rsp); err != nil { if err := proto.Unmarshal(pkg.BusiBuff, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@ -825,3 +875,7 @@ func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo) (interface{}, err
return rsp.AppInfo, nil return rsp.AppInfo, nil
} }
*/ */
func ignoreDecoder(_ *QQClient, _ *network.Packet) (any, error) {
return nil, nil
}

View File

@ -23,10 +23,11 @@ type (
UserOnlineStatus int UserOnlineStatus int
ClientProtocol = auth.Protocol ClientProtocol = auth.ProtocolType
LoginResponse struct { LoginResponse struct {
Success bool Success bool
Code byte
Error LoginError Error LoginError
// Captcha info // Captcha info
@ -77,6 +78,7 @@ type (
Mobile string Mobile string
LoginDays int64 LoginDays int64
Qid string Qid string
VipLevel string
} }
OtherClientInfo struct { OtherClientInfo struct {
@ -175,12 +177,6 @@ type (
client *QQClient client *QQClient
} }
LogEvent struct {
Type string
Message string
Dump []byte
}
ServerUpdatedEvent struct { ServerUpdatedEvent struct {
Servers []jce.SsoServerInfo Servers []jce.SsoServerInfo
} }
@ -196,6 +192,17 @@ type (
DownloadUrl string DownloadUrl string
} }
GroupDisbandEvent struct {
Group *GroupInfo
Time int64
Operator *GroupMemberInfo
}
DeleteFriendEvent struct {
Uin int64
Nickname string
}
// GroupDigest 群精华消息 // GroupDigest 群精华消息
GroupDigest struct { GroupDigest struct {
GroupCode int64 `json:"group_code,string"` GroupCode int64 `json:"group_code,string"`
@ -296,8 +303,12 @@ type (
SigSession []byte SigSession []byte
SessionKey []byte SessionKey []byte
} }
// unit is an alias for struct{}, like `()` in rust
unit = struct{}
) )
//go:generate stringer -type=LoginError
const ( const (
NeedCaptcha LoginError = 1 NeedCaptcha LoginError = 1
OtherLoginError LoginError = 3 OtherLoginError LoginError = 3
@ -350,6 +361,7 @@ const (
MacOS = auth.MacOS MacOS = auth.MacOS
QiDian = auth.QiDian QiDian = auth.QiDian
IPad = auth.IPad IPad = auth.IPad
AndroidPad = auth.AndroidPad
) )
func (r *UserJoinGroupRequest) Accept() { func (r *UserJoinGroupRequest) Accept() {

View File

@ -2,18 +2,62 @@ package client
import ( import (
"fmt" "fmt"
"reflect"
"runtime/debug" "runtime/debug"
"sync" "sync"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
) )
// protected all EventHandle, since write is very rare, use
// only one lock to save memory
var eventMu sync.RWMutex
type EventHandle[T any] struct {
// QQClient?
handlers []func(client *QQClient, event T)
}
func (handle *EventHandle[T]) Subscribe(handler func(client *QQClient, event T)) {
eventMu.Lock()
defer eventMu.Unlock()
// shrink the slice
newHandlers := make([]func(client *QQClient, event T), len(handle.handlers)+1)
copy(newHandlers, handle.handlers)
newHandlers[len(handle.handlers)] = handler
handle.handlers = newHandlers
}
func (handle *EventHandle[T]) dispatch(client *QQClient, event T) {
eventMu.RLock()
defer func() {
eventMu.RUnlock()
if pan := recover(); pan != nil {
fmt.Printf("event error: %v\n%s", pan, debug.Stack())
}
}()
for _, handler := range handle.handlers {
handler(client, event)
}
if len(client.eventHandlers.subscribedEventHandlers) > 0 {
for _, h := range client.eventHandlers.subscribedEventHandlers {
ht := reflect.TypeOf(h)
for i := 0; i < ht.NumMethod(); i++ {
method := ht.Method(i)
if method.Type.NumIn() != 3 {
continue
}
if method.Type.In(1) != reflect.TypeOf(client) || method.Type.In(2) != reflect.TypeOf(event) {
continue
}
method.Func.Call([]reflect.Value{reflect.ValueOf(h), reflect.ValueOf(client), reflect.ValueOf(event)})
}
}
}
}
type eventHandlers struct { type eventHandlers struct {
privateMessageHandlers []func(*QQClient, *message.PrivateMessage) // todo: move to event handle
tempMessageHandlers []func(*QQClient, *TempMessageEvent)
groupMessageHandlers []func(*QQClient, *message.GroupMessage)
selfPrivateMessageHandlers []func(*QQClient, *message.PrivateMessage)
selfGroupMessageHandlers []func(*QQClient, *message.GroupMessage)
guildChannelMessageHandlers []func(*QQClient, *message.GuildChannelMessage) guildChannelMessageHandlers []func(*QQClient, *message.GuildChannelMessage)
guildMessageReactionsUpdatedHandlers []func(*QQClient, *GuildMessageReactionsUpdatedEvent) guildMessageReactionsUpdatedHandlers []func(*QQClient, *GuildMessageReactionsUpdatedEvent)
guildMessageRecalledHandlers []func(*QQClient, *GuildMessageRecalledEvent) guildMessageRecalledHandlers []func(*QQClient, *GuildMessageRecalledEvent)
@ -21,58 +65,14 @@ type eventHandlers struct {
guildChannelCreatedHandlers []func(*QQClient, *GuildChannelOperationEvent) guildChannelCreatedHandlers []func(*QQClient, *GuildChannelOperationEvent)
guildChannelDestroyedHandlers []func(*QQClient, *GuildChannelOperationEvent) guildChannelDestroyedHandlers []func(*QQClient, *GuildChannelOperationEvent)
memberJoinedGuildHandlers []func(*QQClient, *MemberJoinGuildEvent) memberJoinedGuildHandlers []func(*QQClient, *MemberJoinGuildEvent)
groupMuteEventHandlers []func(*QQClient, *GroupMuteEvent)
groupRecalledHandlers []func(*QQClient, *GroupMessageRecalledEvent) serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool
friendRecalledHandlers []func(*QQClient, *FriendMessageRecalledEvent) subscribedEventHandlers []any
joinGroupHandlers []func(*QQClient, *GroupInfo) groupMessageReceiptHandlers sync.Map
leaveGroupHandlers []func(*QQClient, *GroupLeaveEvent)
memberJoinedHandlers []func(*QQClient, *MemberJoinGroupEvent)
memberLeavedHandlers []func(*QQClient, *MemberLeaveGroupEvent)
memberCardUpdatedHandlers []func(*QQClient, *MemberCardUpdatedEvent)
groupNameUpdatedHandlers []func(*QQClient, *GroupNameUpdatedEvent)
permissionChangedHandlers []func(*QQClient, *MemberPermissionChangedEvent)
groupInvitedHandlers []func(*QQClient, *GroupInvitedRequest)
joinRequestHandlers []func(*QQClient, *UserJoinGroupRequest)
friendRequestHandlers []func(*QQClient, *NewFriendRequest)
newFriendHandlers []func(*QQClient, *NewFriendEvent)
disconnectHandlers []func(*QQClient, *ClientDisconnectedEvent)
logHandlers []func(*QQClient, *LogEvent)
serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool
groupNotifyHandlers []func(*QQClient, INotifyEvent)
friendNotifyHandlers []func(*QQClient, INotifyEvent)
memberTitleUpdatedHandlers []func(*QQClient, *MemberSpecialTitleUpdatedEvent)
offlineFileHandlers []func(*QQClient, *OfflineFileEvent)
otherClientStatusChangedHandlers []func(*QQClient, *OtherClientStatusChangedEvent)
groupDigestHandlers []func(*QQClient, *GroupDigestEvent)
groupMessageReceiptHandlers sync.Map
} }
func (c *QQClient) OnPrivateMessage(f func(*QQClient, *message.PrivateMessage)) { func (c *QQClient) SubscribeEventHandler(handler any) {
c.eventHandlers.privateMessageHandlers = append(c.eventHandlers.privateMessageHandlers, f) c.eventHandlers.subscribedEventHandlers = append(c.eventHandlers.subscribedEventHandlers, handler)
}
func (c *QQClient) OnPrivateMessageF(filter func(*message.PrivateMessage) bool, f func(*QQClient, *message.PrivateMessage)) {
c.OnPrivateMessage(func(client *QQClient, msg *message.PrivateMessage) {
if filter(msg) {
f(client, msg)
}
})
}
func (c *QQClient) OnTempMessage(f func(*QQClient, *TempMessageEvent)) {
c.eventHandlers.tempMessageHandlers = append(c.eventHandlers.tempMessageHandlers, f)
}
func (c *QQClient) OnGroupMessage(f func(*QQClient, *message.GroupMessage)) {
c.eventHandlers.groupMessageHandlers = append(c.eventHandlers.groupMessageHandlers, f)
}
func (c *QQClient) OnSelfPrivateMessage(f func(*QQClient, *message.PrivateMessage)) {
c.eventHandlers.selfPrivateMessageHandlers = append(c.eventHandlers.selfPrivateMessageHandlers, f)
}
func (c *QQClient) OnSelfGroupMessage(f func(*QQClient, *message.GroupMessage)) {
c.eventHandlers.selfGroupMessageHandlers = append(c.eventHandlers.selfGroupMessageHandlers, f)
} }
func (s *GuildService) OnGuildChannelMessage(f func(*QQClient, *message.GuildChannelMessage)) { func (s *GuildService) OnGuildChannelMessage(f func(*QQClient, *message.GuildChannelMessage)) {
@ -103,99 +103,10 @@ func (s *GuildService) OnMemberJoinedGuild(f func(*QQClient, *MemberJoinGuildEve
s.c.eventHandlers.memberJoinedGuildHandlers = append(s.c.eventHandlers.memberJoinedGuildHandlers, f) s.c.eventHandlers.memberJoinedGuildHandlers = append(s.c.eventHandlers.memberJoinedGuildHandlers, f)
} }
func (c *QQClient) OnGroupMuted(f func(*QQClient, *GroupMuteEvent)) {
c.eventHandlers.groupMuteEventHandlers = append(c.eventHandlers.groupMuteEventHandlers, f)
}
func (c *QQClient) OnJoinGroup(f func(*QQClient, *GroupInfo)) {
c.eventHandlers.joinGroupHandlers = append(c.eventHandlers.joinGroupHandlers, f)
}
func (c *QQClient) OnLeaveGroup(f func(*QQClient, *GroupLeaveEvent)) {
c.eventHandlers.leaveGroupHandlers = append(c.eventHandlers.leaveGroupHandlers, f)
}
func (c *QQClient) OnGroupMemberJoined(f func(*QQClient, *MemberJoinGroupEvent)) {
c.eventHandlers.memberJoinedHandlers = append(c.eventHandlers.memberJoinedHandlers, f)
}
func (c *QQClient) OnGroupMemberLeaved(f func(*QQClient, *MemberLeaveGroupEvent)) {
c.eventHandlers.memberLeavedHandlers = append(c.eventHandlers.memberLeavedHandlers, f)
}
func (c *QQClient) OnGroupMemberCardUpdated(f func(*QQClient, *MemberCardUpdatedEvent)) {
c.eventHandlers.memberCardUpdatedHandlers = append(c.eventHandlers.memberCardUpdatedHandlers, f)
}
func (c *QQClient) OnGroupNameUpdated(f func(*QQClient, *GroupNameUpdatedEvent)) {
c.eventHandlers.groupNameUpdatedHandlers = append(c.eventHandlers.groupNameUpdatedHandlers, f)
}
func (c *QQClient) OnGroupMemberPermissionChanged(f func(*QQClient, *MemberPermissionChangedEvent)) {
c.eventHandlers.permissionChangedHandlers = append(c.eventHandlers.permissionChangedHandlers, f)
}
func (c *QQClient) OnGroupMessageRecalled(f func(*QQClient, *GroupMessageRecalledEvent)) {
c.eventHandlers.groupRecalledHandlers = append(c.eventHandlers.groupRecalledHandlers, f)
}
func (c *QQClient) OnFriendMessageRecalled(f func(*QQClient, *FriendMessageRecalledEvent)) {
c.eventHandlers.friendRecalledHandlers = append(c.eventHandlers.friendRecalledHandlers, f)
}
func (c *QQClient) OnGroupInvited(f func(*QQClient, *GroupInvitedRequest)) {
c.eventHandlers.groupInvitedHandlers = append(c.eventHandlers.groupInvitedHandlers, f)
}
func (c *QQClient) OnUserWantJoinGroup(f func(*QQClient, *UserJoinGroupRequest)) {
c.eventHandlers.joinRequestHandlers = append(c.eventHandlers.joinRequestHandlers, f)
}
func (c *QQClient) OnNewFriendRequest(f func(*QQClient, *NewFriendRequest)) {
c.eventHandlers.friendRequestHandlers = append(c.eventHandlers.friendRequestHandlers, f)
}
func (c *QQClient) OnNewFriendAdded(f func(*QQClient, *NewFriendEvent)) {
c.eventHandlers.newFriendHandlers = append(c.eventHandlers.newFriendHandlers, f)
}
func (c *QQClient) OnDisconnected(f func(*QQClient, *ClientDisconnectedEvent)) {
c.eventHandlers.disconnectHandlers = append(c.eventHandlers.disconnectHandlers, f)
}
func (c *QQClient) OnServerUpdated(f func(*QQClient, *ServerUpdatedEvent) bool) { func (c *QQClient) OnServerUpdated(f func(*QQClient, *ServerUpdatedEvent) bool) {
c.eventHandlers.serverUpdatedHandlers = append(c.eventHandlers.serverUpdatedHandlers, f) c.eventHandlers.serverUpdatedHandlers = append(c.eventHandlers.serverUpdatedHandlers, f)
} }
func (c *QQClient) OnReceivedOfflineFile(f func(*QQClient, *OfflineFileEvent)) {
c.eventHandlers.offlineFileHandlers = append(c.eventHandlers.offlineFileHandlers, f)
}
func (c *QQClient) OnOtherClientStatusChanged(f func(*QQClient, *OtherClientStatusChangedEvent)) {
c.eventHandlers.otherClientStatusChangedHandlers = append(c.eventHandlers.otherClientStatusChangedHandlers, f)
}
func (c *QQClient) OnLog(f func(*QQClient, *LogEvent)) {
c.eventHandlers.logHandlers = append(c.eventHandlers.logHandlers, f)
}
func (c *QQClient) OnGroupNotify(f func(*QQClient, INotifyEvent)) {
c.eventHandlers.groupNotifyHandlers = append(c.eventHandlers.groupNotifyHandlers, f)
}
func (c *QQClient) OnFriendNotify(f func(*QQClient, INotifyEvent)) {
c.eventHandlers.friendNotifyHandlers = append(c.eventHandlers.friendNotifyHandlers, f)
}
func (c *QQClient) OnMemberSpecialTitleUpdated(f func(*QQClient, *MemberSpecialTitleUpdatedEvent)) {
c.eventHandlers.memberTitleUpdatedHandlers = append(c.eventHandlers.memberTitleUpdatedHandlers, f)
}
// OnGroupDigest 群精华消息事件注册
func (c *QQClient) OnGroupDigest(f func(*QQClient, *GroupDigestEvent)) {
c.eventHandlers.groupDigestHandlers = append(c.eventHandlers.groupDigestHandlers, f)
}
func NewUinFilterPrivate(uin int64) func(*message.PrivateMessage) bool { func NewUinFilterPrivate(uin int64) func(*message.PrivateMessage) bool {
return func(msg *message.PrivateMessage) bool { return func(msg *message.PrivateMessage) bool {
return msg.Sender.Uin == uin return msg.Sender.Uin == uin
@ -210,61 +121,6 @@ func (c *QQClient) onGroupMessageReceipt(id string, f ...func(*QQClient, *groupM
c.eventHandlers.groupMessageReceiptHandlers.LoadOrStore(id, f[0]) c.eventHandlers.groupMessageReceiptHandlers.LoadOrStore(id, f[0])
} }
func (c *QQClient) dispatchPrivateMessage(msg *message.PrivateMessage) {
if msg == nil {
return
}
for _, f := range c.eventHandlers.privateMessageHandlers {
cover(func() {
f(c, msg)
})
}
}
func (c *QQClient) dispatchTempMessage(e *TempMessageEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.tempMessageHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupMessage(msg *message.GroupMessage) {
if msg == nil {
return
}
for _, f := range c.eventHandlers.groupMessageHandlers {
cover(func() {
f(c, msg)
})
}
}
func (c *QQClient) dispatchPrivateMessageSelf(msg *message.PrivateMessage) {
if msg == nil {
return
}
for _, f := range c.eventHandlers.selfPrivateMessageHandlers {
cover(func() {
f(c, msg)
})
}
}
func (c *QQClient) dispatchGroupMessageSelf(msg *message.GroupMessage) {
if msg == nil {
return
}
for _, f := range c.eventHandlers.selfGroupMessageHandlers {
cover(func() {
f(c, msg)
})
}
}
func (c *QQClient) dispatchGuildChannelMessage(msg *message.GuildChannelMessage) { func (c *QQClient) dispatchGuildChannelMessage(msg *message.GuildChannelMessage) {
if msg == nil { if msg == nil {
return return
@ -342,255 +198,13 @@ func (c *QQClient) dispatchMemberJoinedGuildEvent(e *MemberJoinGuildEvent) {
} }
} }
func (c *QQClient) dispatchGroupMuteEvent(e *GroupMuteEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupMuteEventHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupMessageRecalledEvent(e *GroupMessageRecalledEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupRecalledHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchFriendMessageRecalledEvent(e *FriendMessageRecalledEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.friendRecalledHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchJoinGroupEvent(group *GroupInfo) {
if group == nil {
return
}
for _, f := range c.eventHandlers.joinGroupHandlers {
cover(func() {
f(c, group)
})
}
}
func (c *QQClient) dispatchLeaveGroupEvent(e *GroupLeaveEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.leaveGroupHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchNewMemberEvent(e *MemberJoinGroupEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.memberJoinedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchMemberLeaveEvent(e *MemberLeaveGroupEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.memberLeavedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchMemberCardUpdatedEvent(e *MemberCardUpdatedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.memberCardUpdatedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupNameUpdatedEvent(e *GroupNameUpdatedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupNameUpdatedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchPermissionChanged(e *MemberPermissionChangedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.permissionChangedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupMessageReceiptEvent(e *groupMessageReceiptEvent) { func (c *QQClient) dispatchGroupMessageReceiptEvent(e *groupMessageReceiptEvent) {
c.eventHandlers.groupMessageReceiptHandlers.Range(func(_, f interface{}) bool { c.eventHandlers.groupMessageReceiptHandlers.Range(func(_, f any) bool {
go f.(func(*QQClient, *groupMessageReceiptEvent))(c, e) go f.(func(*QQClient, *groupMessageReceiptEvent))(c, e)
return true return true
}) })
} }
func (c *QQClient) dispatchGroupInvitedEvent(e *GroupInvitedRequest) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupInvitedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchJoinGroupRequest(r *UserJoinGroupRequest) {
if r == nil {
return
}
for _, f := range c.eventHandlers.joinRequestHandlers {
cover(func() {
f(c, r)
})
}
}
func (c *QQClient) dispatchNewFriendRequest(r *NewFriendRequest) {
if r == nil {
return
}
for _, f := range c.eventHandlers.friendRequestHandlers {
cover(func() {
f(c, r)
})
}
}
func (c *QQClient) dispatchNewFriendEvent(e *NewFriendEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.newFriendHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupNotifyEvent(e INotifyEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupNotifyHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchFriendNotifyEvent(e INotifyEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.friendNotifyHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchMemberSpecialTitleUpdateEvent(e *MemberSpecialTitleUpdatedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.memberTitleUpdatedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchDisconnectEvent(e *ClientDisconnectedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.disconnectHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchOfflineFileEvent(e *OfflineFileEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.offlineFileHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchOtherClientStatusChangedEvent(e *OtherClientStatusChangedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.otherClientStatusChangedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupDigestEvent(e *GroupDigestEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupDigestHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchLogEvent(e *LogEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.logHandlers {
cover(func() {
f(c, e)
})
}
}
func cover(f func()) { func cover(f func()) {
defer func() { defer func() {
if pan := recover(); pan != nil { if pan := recover(); pan != nil {

View File

@ -15,31 +15,35 @@ type CustomFace struct {
Url string Url string
} }
func init() {
decoders["Faceroam.OpReq"] = decodeFaceroamResponse
}
func (c *QQClient) GetCustomFaces() ([]*CustomFace, error) { func (c *QQClient) GetCustomFaces() ([]*CustomFace, error) {
i, err := c.callAndDecode(c.buildFaceroamRequestPacket()) i, err := c.sendAndWait(c.buildFaceroamRequestPacket())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "get faces error") return nil, errors.Wrap(err, "get faces error")
} }
return i.([]*CustomFace), nil return i.([]*CustomFace), nil
} }
func (c *QQClient) buildFaceroamRequestPacket() *network.Request { func (c *QQClient) buildFaceroamRequestPacket() (uint16, []byte) {
payload, _ := proto.Marshal(&faceroam.FaceroamReqBody{ payload, _ := proto.Marshal(&faceroam.FaceroamReqBody{
Comm: &faceroam.PlatInfo{ Comm: &faceroam.PlatInfo{
Implat: proto.Int64(109), Implat: proto.Int64(109),
Osver: proto.String(string(c.deviceInfo.Version.Release)), Osver: proto.String(string(c.Device().Version.Release)),
Mqqver: &c.version.SortVersionName, Mqqver: proto.Some(c.version().SortVersionName),
}, },
Uin: proto.Uint64(uint64(c.Uin)), Uin: proto.Uint64(uint64(c.Uin)),
SubCmd: proto.Uint32(1), SubCmd: proto.Uint32(1),
ReqUserInfo: &faceroam.ReqUserInfo{}, ReqUserInfo: &faceroam.ReqUserInfo{},
}) })
return c.uniRequest("Faceroam.OpReq", payload, decodeFaceroamResponse) return c.uniPacket("Faceroam.OpReq", payload)
} }
func decodeFaceroamResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeFaceroamResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := faceroam.FaceroamRspBody{} rsp := faceroam.FaceroamRspBody{}
if err := proto.Unmarshal(resp.Body, &rsp); err != nil { if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if rsp.RspUserInfo == nil { if rsp.RspUserInfo == nil {
@ -49,7 +53,7 @@ func decodeFaceroamResponse(c *QQClient, resp *network.Response) (interface{}, e
for i := len(rsp.RspUserInfo.Filename) - 1; i >= 0; i-- { for i := len(rsp.RspUserInfo.Filename) - 1; i >= 0; i-- {
res[len(rsp.RspUserInfo.Filename)-1-i] = &CustomFace{ res[len(rsp.RspUserInfo.Filename)-1-i] = &CustomFace{
ResId: rsp.RspUserInfo.Filename[i], ResId: rsp.RspUserInfo.Filename[i],
Url: fmt.Sprintf("https://p.qpic.cn/%s/%d/%s/0", rsp.RspUserInfo.GetBid(), c.Uin, rsp.RspUserInfo.Filename[i]), Url: fmt.Sprintf("https://p.qpic.cn/%s/%d/%s/0", rsp.RspUserInfo.Bid.Unwrap(), c.Uin, rsp.RspUserInfo.Filename[i]),
} }
} }
return res, nil return res, nil

View File

@ -2,13 +2,16 @@ package client
import ( import (
"crypto/md5" "crypto/md5"
crand "crypto/rand"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"net/netip"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -26,106 +29,99 @@ import (
type ( type (
DeviceInfo = auth.Device DeviceInfo = auth.Device
Version = auth.OSVersion Version = auth.OSVersion
groupMessageBuilder struct {
MessageSlices []*msg.Message
}
) )
var SystemDeviceInfo = &DeviceInfo{
Display: []byte("MIRAI.123456.001"),
Product: []byte("mirai"),
Device: []byte("mirai"),
Board: []byte("mirai"),
Brand: []byte("mamoe"),
Model: []byte("mirai"),
Bootloader: []byte("unknown"),
FingerPrint: []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/1234567:user/release-keys"),
BootId: []byte("cb886ae2-00b6-4d68-a230-787f111d12c7"),
ProcVersion: []byte("Linux version 3.0.31-cb886ae2 (android-build@xxx.xxx.xxx.xxx.com)"),
BaseBand: EmptyBytes,
SimInfo: []byte("T-Mobile"),
OSType: []byte("android"),
MacAddress: []byte("00:50:56:C0:00:08"),
IpAddress: []byte{10, 0, 1, 3}, // 10.0.1.3
WifiBSSID: []byte("00:50:56:C0:00:08"),
WifiSSID: []byte("<unknown ssid>"),
IMEI: "468356291846738",
AndroidId: []byte("MIRAI.123456.001"),
APN: []byte("wifi"),
VendorName: []byte("MIUI"),
VendorOSName: []byte("mirai"),
Protocol: IPad,
Version: &Version{
Incremental: []byte("5891938"),
Release: []byte("10"),
CodeName: []byte("REL"),
SDK: 29,
},
}
var EmptyBytes = make([]byte, 0) var EmptyBytes = make([]byte, 0)
func init() { func GenRandomDevice() *DeviceInfo {
r := make([]byte, 16) r := make([]byte, 16)
rand.Read(r) crand.Read(r)
t := md5.Sum(r)
SystemDeviceInfo.IMSIMd5 = t[:]
SystemDeviceInfo.GenNewGuid()
SystemDeviceInfo.GenNewTgtgtKey()
}
func GenRandomDevice() {
r := make([]byte, 16)
rand.Read(r)
const numberRange = "0123456789" const numberRange = "0123456789"
SystemDeviceInfo.Display = []byte("MIRAI." + utils.RandomStringRange(6, numberRange) + ".001")
SystemDeviceInfo.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, numberRange) + ":user/release-keys") var device = &DeviceInfo{
SystemDeviceInfo.BootId = binary.GenUUID(r) Product: []byte("mirai"),
SystemDeviceInfo.ProcVersion = []byte("Linux version 3.0.31-" + utils.RandomString(8) + " (android-build@xxx.xxx.xxx.xxx.com)") Device: []byte("mirai"),
rand.Read(r) Board: []byte("mirai"),
Brand: []byte("mamoe"),
Model: []byte("mirai"),
Bootloader: []byte("unknown"),
BootId: []byte("cb886ae2-00b6-4d68-a230-787f111d12c7"),
ProcVersion: []byte("Linux version 3.0.31-cb886ae2 (android-build@xxx.xxx.xxx.xxx.com)"),
BaseBand: EmptyBytes,
SimInfo: []byte("T-Mobile"),
OSType: []byte("android"),
MacAddress: []byte("00:50:56:C0:00:08"),
IpAddress: []byte{10, 0, 1, 3}, // 10.0.1.3
WifiBSSID: []byte("00:50:56:C0:00:08"),
WifiSSID: []byte("<unknown ssid>"),
IMEI: "468356291846738",
AndroidId: []byte("MIRAI.123456.001"),
APN: []byte("wifi"),
VendorName: []byte("MIUI"),
VendorOSName: []byte("mirai"),
Protocol: AndroidPad,
Version: &Version{
Incremental: []byte("5891938"),
Release: []byte("10"),
CodeName: []byte("REL"),
SDK: 29,
},
}
device.Display = []byte("MIRAI." + utils.RandomStringRange(6, numberRange) + ".001")
device.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, numberRange) + ":user/release-keys")
device.BootId = binary.GenUUID(r)
device.ProcVersion = []byte("Linux version 3.0.31-" + utils.RandomString(8) + " (android-build@xxx.xxx.xxx.xxx.com)")
crand.Read(r)
t := md5.Sum(r) t := md5.Sum(r)
SystemDeviceInfo.IMSIMd5 = t[:] device.IMSIMd5 = t[:]
SystemDeviceInfo.IMEI = GenIMEI() device.IMEI = GenIMEI()
r = make([]byte, 8) r = make([]byte, 8)
rand.Read(r) crand.Read(r)
hex.Encode(SystemDeviceInfo.AndroidId, r) hex.Encode(device.AndroidId, r)
SystemDeviceInfo.GenNewGuid() device.GenNewGuid()
SystemDeviceInfo.GenNewTgtgtKey() device.GenNewTgtgtKey()
device.RequestQImei()
return device
} }
func GenIMEI() string { func GenIMEI() string {
sum := 0 // the control sum of digits sum := 0 // the control sum of digits
var final strings.Builder var final strings.Builder
randSrc := rand.NewSource(time.Now().UnixNano()) randGen := rand.New(rand.NewSource(time.Now().UnixNano()))
randGen := rand.New(randSrc)
for i := 0; i < 14; i++ { // generating all the base digits for i := 0; i < 14; i++ { // generating all the base digits
toAdd := randGen.Intn(10) toAdd := randGen.Intn(10)
final.WriteString(strconv.Itoa(toAdd))
if (i+1)%2 == 0 { // special proc for every 2nd one if (i+1)%2 == 0 { // special proc for every 2nd one
toAdd *= 2 toAdd *= 2
if toAdd >= 10 { if toAdd >= 10 {
toAdd = (toAdd % 10) + 1 toAdd = (toAdd % 10) + 1
} }
} }
sum += toAdd sum += toAdd // and even add them here!
final.WriteString(fmt.Sprintf("%d", toAdd)) // and even printing them here!
} }
ctrlDigit := (sum * 9) % 10 // calculating the control digit ctrlDigit := (sum * 9) % 10 // calculating the control digit
final.WriteString(fmt.Sprintf("%d", ctrlDigit)) final.WriteString(strconv.Itoa(ctrlDigit))
return final.String() return final.String()
} }
func getSSOAddress() ([]*net.TCPAddr, error) { func UpdateAppVersion(protocolType auth.ProtocolType, data []byte) error {
protocol := SystemDeviceInfo.Protocol.Version() if _, ok := auth.AppVersions[protocolType]; !ok {
return errors.New("unknown protocol type: " + strconv.Itoa(int(protocolType)))
}
return auth.AppVersions[protocolType].UpdateFromJson(data)
}
func getSSOAddress(device *auth.Device) ([]netip.AddrPort, error) {
protocol := device.Protocol.Version()
key, _ := hex.DecodeString("F0441F5FF42DA58FDCF7949ABA62D411") key, _ := hex.DecodeString("F0441F5FF42DA58FDCF7949ABA62D411")
payload := jce.NewJceWriter(). // see ServerConfig.d payload := jce.NewJceWriter(). // see ServerConfig.d
WriteInt64(0, 1).WriteInt64(0, 2).WriteByte(1, 3). WriteInt64(0, 1).WriteInt64(0, 2).WriteByte(1, 3).
WriteString("00000", 4).WriteInt32(100, 5). WriteString("00000", 4).WriteInt32(100, 5).
WriteInt32(int32(protocol.AppId), 6).WriteString(SystemDeviceInfo.IMEI, 7). WriteInt32(int32(protocol.AppId), 6).WriteString(device.IMEI, 7).
WriteInt64(0, 8).WriteInt64(0, 9).WriteInt64(0, 10). WriteInt64(0, 8).WriteInt64(0, 9).WriteInt64(0, 10).
WriteInt64(0, 11).WriteByte(0, 12).WriteInt64(0, 13).WriteByte(1, 14).Bytes() WriteInt64(0, 11).WriteByte(0, 12).WriteInt64(0, 13).Bytes()
buf := &jce.RequestDataVersion3{ buf := &jce.RequestDataVersion3{
Map: map[string][]byte{"HttpServerListReq": packUniRequestData(payload)}, Map: map[string][]byte{"HttpServerListReq": packUniRequestData(payload)},
} }
@ -143,7 +139,7 @@ func getSSOAddress() ([]*net.TCPAddr, error) {
tea := binary.NewTeaCipher(key) tea := binary.NewTeaCipher(key)
encpkt := tea.Encrypt(b) encpkt := tea.Encrypt(b)
cl() cl()
rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp", encpkt) rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp?mType=getssolist", encpkt)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "unable to fetch server list") return nil, errors.Wrap(err, "unable to fetch server list")
} }
@ -153,15 +149,15 @@ func getSSOAddress() ([]*net.TCPAddr, error) {
data.ReadFrom(jce.NewJceReader(rspPkt.SBuffer)) data.ReadFrom(jce.NewJceReader(rspPkt.SBuffer))
reader := jce.NewJceReader(data.Map["HttpServerListRes"][1:]) reader := jce.NewJceReader(data.Map["HttpServerListRes"][1:])
servers := reader.ReadSsoServerInfos(2) servers := reader.ReadSsoServerInfos(2)
adds := make([]*net.TCPAddr, 0, len(servers)) adds := make([]netip.AddrPort, 0, len(servers))
for _, s := range servers { for _, s := range servers {
if strings.Contains(s.Server, "com") { if strings.Contains(s.Server, "com") {
continue continue
} }
adds = append(adds, &net.TCPAddr{ ip, ok := netip.AddrFromSlice(net.ParseIP(s.Server))
IP: net.ParseIP(s.Server), if ok {
Port: int(s.Port), adds = append(adds, netip.AddrPortFrom(ip, uint16(s.Port)))
}) }
} }
return adds, nil return adds, nil
} }
@ -178,33 +174,33 @@ func qualityTest(addr string) (int64, error) {
} }
func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage { func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage {
friend := c.FindFriend(msg.Head.GetFromUin()) friend := c.FindFriend(msg.Head.FromUin.Unwrap())
var sender *message.Sender var sender *message.Sender
if friend == nil { if friend != nil {
sender = &message.Sender{
Uin: msg.Head.GetFromUin(),
Nickname: msg.Head.GetFromNick(),
}
} else {
sender = &message.Sender{ sender = &message.Sender{
Uin: friend.Uin, Uin: friend.Uin,
Nickname: friend.Nickname, Nickname: friend.Nickname,
IsFriend: true, IsFriend: true,
} }
} else {
sender = &message.Sender{
Uin: msg.Head.FromUin.Unwrap(),
Nickname: msg.Head.FromNick.Unwrap(),
}
} }
ret := &message.PrivateMessage{ ret := &message.PrivateMessage{
Id: msg.Head.GetMsgSeq(), Id: msg.Head.MsgSeq.Unwrap(),
Target: msg.Head.GetToUin(), Target: msg.Head.ToUin.Unwrap(),
Time: msg.Head.GetMsgTime(), Time: msg.Head.MsgTime.Unwrap(),
Sender: sender, Sender: sender,
Self: c.Uin, Self: c.Uin,
Elements: func() []message.IMessageElement { Elements: func() []message.IMessageElement {
if msg.Body.RichText.Ptt != nil { if msg.Body.RichText.Ptt != nil {
return []message.IMessageElement{ return []message.IMessageElement{
&message.VoiceElement{ &message.VoiceElement{
Name: msg.Body.RichText.Ptt.GetFileName(), Name: msg.Body.RichText.Ptt.FileName.Unwrap(),
Md5: msg.Body.RichText.Ptt.FileMd5, Md5: msg.Body.RichText.Ptt.FileMd5,
Size: msg.Body.RichText.Ptt.GetFileSize(), Size: msg.Body.RichText.Ptt.FileSize.Unwrap(),
Url: string(msg.Body.RichText.Ptt.DownPara), Url: string(msg.Body.RichText.Ptt.DownPara),
}, },
} }
@ -213,7 +209,7 @@ func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage
}(), }(),
} }
if msg.Body.RichText.Attr != nil { if msg.Body.RichText.Attr != nil {
ret.InternalId = msg.Body.RichText.Attr.GetRandom() ret.InternalId = msg.Body.RichText.Attr.Random.Unwrap()
} }
return ret return ret
} }
@ -221,23 +217,23 @@ func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage
func (c *QQClient) parseTempMessage(msg *msg.Message) *message.TempMessage { func (c *QQClient) parseTempMessage(msg *msg.Message) *message.TempMessage {
var groupCode int64 var groupCode int64
var groupName string var groupName string
group := c.FindGroupByUin(msg.Head.C2CTmpMsgHead.GetGroupUin()) group := c.FindGroupByUin(msg.Head.C2CTmpMsgHead.GroupUin.Unwrap())
sender := &message.Sender{ sender := &message.Sender{
Uin: msg.Head.GetFromUin(), Uin: msg.Head.FromUin.Unwrap(),
Nickname: "Unknown", Nickname: "Unknown",
IsFriend: false, IsFriend: false,
} }
if group != nil { if group != nil {
groupCode = group.Code groupCode = group.Code
groupName = group.Name groupName = group.Name
mem := group.FindMember(msg.Head.GetFromUin()) mem := group.FindMember(msg.Head.FromUin.Unwrap())
if mem != nil { if mem != nil {
sender.Nickname = mem.Nickname sender.Nickname = mem.Nickname
sender.CardName = mem.CardName sender.CardName = mem.CardName
} }
} }
return &message.TempMessage{ return &message.TempMessage{
Id: msg.Head.GetMsgSeq(), Id: msg.Head.MsgSeq.Unwrap(),
GroupCode: groupCode, GroupCode: groupCode,
GroupName: groupName, GroupName: groupName,
Self: c.Uin, Self: c.Uin,
@ -246,12 +242,44 @@ func (c *QQClient) parseTempMessage(msg *msg.Message) *message.TempMessage {
} }
} }
func (b *groupMessageBuilder) build() *msg.Message { func (c *QQClient) messageBuilder(seq int32) *messageBuilder {
sort.Slice(b.MessageSlices, func(i, j int) bool { actual, ok := c.msgBuilders.Load(seq)
return b.MessageSlices[i].Content.GetPkgIndex() < b.MessageSlices[j].Content.GetPkgIndex() if !ok {
builder := &messageBuilder{}
actual, _ = c.msgBuilders.LoadOrStore(seq, builder)
time.AfterFunc(time.Minute, func() {
c.msgBuilders.Delete(seq) // delete avoid memory leak
})
}
return actual
}
type messageBuilder struct {
lock sync.Mutex
slices []*msg.Message
}
func (b *messageBuilder) append(msg *msg.Message) {
b.lock.Lock()
defer b.lock.Unlock()
b.slices = append(b.slices, msg)
}
func (b *messageBuilder) len() int32 {
b.lock.Lock()
x := len(b.slices)
b.lock.Unlock()
return int32(x)
}
func (b *messageBuilder) build() *msg.Message {
b.lock.Lock()
defer b.lock.Unlock()
sort.Slice(b.slices, func(i, j int) bool {
return b.slices[i].Content.PkgIndex.Unwrap() < b.slices[j].Content.PkgIndex.Unwrap()
}) })
base := b.MessageSlices[0] base := b.slices[0]
for _, m := range b.MessageSlices[1:] { for _, m := range b.slices[1:] {
base.Body.RichText.Elems = append(base.Body.RichText.Elems, m.Body.RichText.Elems...) base.Body.RichText.Elems = append(base.Body.RichText.Elems, m.Body.RichText.Elems...)
} }
return base return base
@ -265,15 +293,8 @@ func packUniRequestData(data []byte) []byte {
return r return r
} }
func genForwardTemplate(resID, preview, title, brief, source, summary string, ts int64, items []*msg.PbMultiMsgItem) *message.ForwardElement { func genForwardTemplate(resID, preview, summary string, ts int64, items []*msg.PbMultiMsgItem) *message.ForwardElement {
template := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8'?><msg serviceID="35" templateID="1" action="viewMultiMsg" brief="%s" m_resid="%s" m_fileName="%d" tSum="3" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0"><item layout="1"><title color="#000000" size="34">%s</title> %s<hr></hr><summary size="26" color="#808080">%s</summary></item><source name="%s"></source></msg>`, template := forwardDisplay(resID, strconv.FormatInt(ts, 10), preview, summary)
brief, resID, ts, title, preview, summary, source,
)
for _, item := range items {
if item.GetFileName() == "MultiMsg" {
*item.FileName = strconv.FormatInt(ts, 10)
}
}
return &message.ForwardElement{ return &message.ForwardElement{
FileName: strconv.FormatInt(ts, 10), FileName: strconv.FormatInt(ts, 10),
Content: template, Content: template,
@ -282,46 +303,40 @@ func genForwardTemplate(resID, preview, title, brief, source, summary string, ts
} }
} }
func genLongTemplate(resID, brief string, ts int64) *message.ServiceElement {
limited := func() string {
if len(brief) > 30 {
return brief[:30] + "…"
}
return brief
}()
template := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="35" templateID="1" action="viewMultiMsg" brief="%s" m_resid="%s" m_fileName="%d" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="1"> <item layout="1"> <title>%s</title> <hr hidden="false" style="0"/> <summary>点击查看完整消息</summary> </item> <source name="聊天记录" icon="" action="" appid="-1"/> </msg>`,
utils.XmlEscape(limited), resID, ts, utils.XmlEscape(limited),
)
return &message.ServiceElement{
Id: 35,
Content: template,
ResId: resID,
SubType: "Long",
}
}
func (c *QQClient) getWebDeviceInfo() (i string) { func (c *QQClient) getWebDeviceInfo() (i string) {
qimei := strings.ToLower(utils.RandomString(36)) qimei := strings.ToLower(utils.RandomString(36))
i += fmt.Sprintf("i=%v&imsi=&mac=%v&m=%v&o=%v&", c.deviceInfo.IMEI, utils.B2S(c.deviceInfo.MacAddress), utils.B2S(c.deviceInfo.Device), utils.B2S(c.deviceInfo.Version.Release)) i += fmt.Sprintf("i=%v&imsi=&mac=%v&m=%v&o=%v&", c.Device().IMEI, utils.B2S(c.Device().MacAddress), utils.B2S(c.Device().Device), utils.B2S(c.Device().Version.Release))
i += fmt.Sprintf("a=%v&sd=0&c64=0&sc=1&p=1080*2210&aid=%v&", c.deviceInfo.Version.SDK, c.deviceInfo.IMEI) i += fmt.Sprintf("a=%v&sd=0&c64=0&sc=1&p=1080*2210&aid=%v&", c.Device().Version.SDK, c.Device().IMEI)
i += fmt.Sprintf("f=%v&mm=%v&cf=%v&cc=%v&", c.deviceInfo.Brand, 5629 /* Total Memory*/, 1725 /* CPU Frequency */, 8 /* CPU Core Count */) i += fmt.Sprintf("f=%v&mm=%v&cf=%v&cc=%v&", c.Device().Brand, 5629 /* Total Memory*/, 1725 /* CPU Frequency */, 8 /* CPU Core Count */)
i += fmt.Sprintf("qimei=%v&qimei36=%v&", qimei, qimei) i += fmt.Sprintf("qimei=%v&qimei36=%v&", qimei, qimei)
i += "sharpP=1&n=wifi&support_xsj_live=true&client_mod=default&timezone=Asia/Shanghai&material_sdk_version=2.9.0&vh265=null&refreshrate=60" i += "sharpP=1&n=wifi&support_xsj_live=true&client_mod=default&timezone=Asia/Shanghai&material_sdk_version=2.9.0&vh265=null&refreshrate=60"
return return
} }
var oidbSSOPool = sync.Pool{}
func getOidbSSOPackage() *oidb.OIDBSSOPkg {
g := oidbSSOPool.Get()
if g == nil {
return new(oidb.OIDBSSOPkg)
}
return g.(*oidb.OIDBSSOPkg)
}
func (c *QQClient) packOIDBPackage(cmd, serviceType int32, body []byte) []byte { func (c *QQClient) packOIDBPackage(cmd, serviceType int32, body []byte) []byte {
pkg := &oidb.OIDBSSOPkg{ pkg := getOidbSSOPackage()
defer oidbSSOPool.Put(pkg)
*pkg = oidb.OIDBSSOPkg{
Command: cmd, Command: cmd,
ServiceType: serviceType, ServiceType: serviceType,
Bodybuffer: body, Bodybuffer: body,
ClientVersion: "Android " + c.version.SortVersionName, ClientVersion: "Android " + c.version().SortVersionName,
} }
r, _ := proto.Marshal(pkg) r, _ := proto.Marshal(pkg)
return r return r
} }
func (c *QQClient) packOIDBPackageDynamically(cmd, serviceType int32, msg binary.DynamicProtoMessage) []byte { func (c *QQClient) packOIDBPackageDynamically(cmd, serviceType int32, msg proto.DynamicMessage) []byte {
return c.packOIDBPackage(cmd, serviceType, msg.Encode()) return c.packOIDBPackage(cmd, serviceType, msg.Encode())
} }
@ -330,59 +345,17 @@ func (c *QQClient) packOIDBPackageProto(cmd, serviceType int32, msg proto.Messag
return c.packOIDBPackage(cmd, serviceType, b) return c.packOIDBPackage(cmd, serviceType, b)
} }
func unpackOIDBPackage(buff []byte, payload proto.Message) error { func unpackOIDBPackage(payload []byte, rsp proto.Message) error {
pkg := new(oidb.OIDBSSOPkg) pkg := getOidbSSOPackage()
if err := proto.Unmarshal(buff, pkg); err != nil { defer oidbSSOPool.Put(pkg)
if err := proto.Unmarshal(payload, pkg); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message") return errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if pkg.GetResult() != 0 { if pkg.Result != 0 {
return errors.Errorf("oidb result unsuccessful: %v msg: %v", pkg.GetResult(), pkg.GetErrorMsg()) return errors.Errorf("oidb result unsuccessful: %v msg: %v", pkg.Result, pkg.ErrorMsg)
} }
if err := proto.Unmarshal(pkg.Bodybuffer, payload); err != nil { if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message") return errors.Wrap(err, "failed to unmarshal protobuf message")
} }
return nil return nil
} }
func (c *QQClient) Error(msg string, args ...interface{}) {
c.dispatchLogEvent(&LogEvent{
Type: "ERROR",
Message: fmt.Sprintf(msg, args...),
})
}
func (c *QQClient) Warning(msg string, args ...interface{}) {
c.dispatchLogEvent(&LogEvent{
Type: "WARNING",
Message: fmt.Sprintf(msg, args...),
})
}
func (c *QQClient) Info(msg string, args ...interface{}) {
c.dispatchLogEvent(&LogEvent{
Type: "INFO",
Message: fmt.Sprintf(msg, args...),
})
}
func (c *QQClient) Debug(msg string, args ...interface{}) {
c.dispatchLogEvent(&LogEvent{
Type: "DEBUG",
Message: fmt.Sprintf(msg, args...),
})
}
func (c *QQClient) Trace(msg string, args ...interface{}) {
c.dispatchLogEvent(&LogEvent{
Type: "TRACE",
Message: fmt.Sprintf(msg, args...),
})
}
func (c *QQClient) Dump(msg string, data []byte, args ...interface{}) {
c.dispatchLogEvent(&LogEvent{
Type: "DUMP",
Message: fmt.Sprintf(msg, args...),
Dump: data,
})
}

View File

@ -1,22 +1,17 @@
package client package client
import ( import (
"crypto/sha1"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io"
"math/rand"
"os" "os"
"runtime/debug" "runtime/debug"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/internal/highway"
"github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/exciting"
"github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/internal/proto" "github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/message"
) )
type ( type (
@ -55,12 +50,23 @@ type (
} }
) )
var fsWaiter = utils.NewUploadWaiter() func init() {
decoders["OidbSvc.0x6d8_1"] = decodeOIDB6d81Response
decoders["OidbSvc.0x6d6_0"] = decodeOIDB6d60Response
decoders["OidbSvc.0x6d6_2"] = decodeOIDB6d62Response
decoders["OidbSvc.0x6d6_3"] = decodeOIDB6d63Response
decoders["OidbSvc.0x6d6_4"] = decodeOIDB6d64Response
decoders["OidbSvc.0x6d6_5"] = decodeOIDB6d65Response
decoders["OidbSvc.0x6d7_0"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d7_1"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d7_2"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d9_4"] = ignoreDecoder
}
func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err error) { func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err error) {
defer func() { defer func() {
if pan := recover(); pan != nil { if pan := recover(); pan != nil {
c.Error("get group fs error: %v\n%s", pan, debug.Stack()) c.error("get group fs error: %v\n%s", pan, debug.Stack())
err = errors.New("fs error") err = errors.New("fs error")
} }
}() }()
@ -68,32 +74,32 @@ func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err
if g == nil { if g == nil {
return nil, errors.New("group not found") return nil, errors.New("group not found")
} }
rsp, e := c.callAndDecode(c.buildGroupFileCountRequest(groupCode)) rsp, e := c.sendAndWait(c.buildGroupFileCountRequestPacket(groupCode))
if e != nil { if e != nil {
return nil, e return nil, e
} }
fs = &GroupFileSystem{ fs = &GroupFileSystem{
FileCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.GetAllFileCount(), FileCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.AllFileCount.Unwrap(),
LimitCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.GetLimitCount(), LimitCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.LimitCount.Unwrap(),
GroupCode: groupCode, GroupCode: groupCode,
client: c, client: c,
} }
rsp, err = c.callAndDecode(c.buildGroupFileSpaceRequest(groupCode)) rsp, err = c.sendAndWait(c.buildGroupFileSpaceRequestPacket(groupCode))
if err != nil { if err != nil {
return nil, err return nil, err
} }
fs.TotalSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.GetTotalSpace() fs.TotalSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.TotalSpace.Unwrap()
fs.UsedSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.GetUsedSpace() fs.UsedSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.UsedSpace.Unwrap()
return fs, nil return fs, nil
} }
func (c *QQClient) GetGroupFileUrl(groupCode int64, fileId string, busId int32) string { func (c *QQClient) GetGroupFileUrl(groupCode int64, fileId string, busId int32) string {
i, err := c.callAndDecode(c.buildGroupFileDownloadReq(groupCode, fileId, busId)) i, err := c.sendAndWait(c.buildGroupFileDownloadReqPacket(groupCode, fileId, busId))
if err != nil { if err != nil {
return "" return ""
} }
url := i.(string) url := i.(string)
url += "?fname=" + hex.EncodeToString([]byte(fileId)) url += fmt.Sprintf("?fname=%x", fileId)
return url return url
} }
@ -106,8 +112,7 @@ func (fs *GroupFileSystem) GetFilesByFolder(folderID string) ([]*GroupFile, []*G
var files []*GroupFile var files []*GroupFile
var folders []*GroupFolder var folders []*GroupFolder
for { for {
req := fs.client.buildGroupFileListRequest(fs.GroupCode, folderID, startIndex) i, err := fs.client.sendAndWait(fs.client.buildGroupFileListRequestPacket(fs.GroupCode, folderID, startIndex))
i, err := fs.client.callAndDecode(req)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -119,118 +124,54 @@ func (fs *GroupFileSystem) GetFilesByFolder(folderID string) ([]*GroupFile, []*G
if item.FileInfo != nil { if item.FileInfo != nil {
files = append(files, &GroupFile{ files = append(files, &GroupFile{
GroupCode: fs.GroupCode, GroupCode: fs.GroupCode,
FileId: item.FileInfo.GetFileId(), FileId: item.FileInfo.FileId.Unwrap(),
FileName: item.FileInfo.GetFileName(), FileName: item.FileInfo.FileName.Unwrap(),
BusId: int32(item.FileInfo.GetBusId()), BusId: int32(item.FileInfo.BusId.Unwrap()),
FileSize: int64(item.FileInfo.GetFileSize()), FileSize: int64(item.FileInfo.FileSize.Unwrap()),
UploadTime: int64(item.FileInfo.GetUploadTime()), UploadTime: int64(item.FileInfo.UploadTime.Unwrap()),
DeadTime: int64(item.FileInfo.GetDeadTime()), DeadTime: int64(item.FileInfo.DeadTime.Unwrap()),
ModifyTime: int64(item.FileInfo.GetModifyTime()), ModifyTime: int64(item.FileInfo.ModifyTime.Unwrap()),
DownloadTimes: int64(item.FileInfo.GetDownloadTimes()), DownloadTimes: int64(item.FileInfo.DownloadTimes.Unwrap()),
Uploader: int64(item.FileInfo.GetUploaderUin()), Uploader: int64(item.FileInfo.UploaderUin.Unwrap()),
UploaderName: item.FileInfo.GetUploaderName(), UploaderName: item.FileInfo.UploaderName.Unwrap(),
}) })
} }
if item.FolderInfo != nil { if item.FolderInfo != nil {
folders = append(folders, &GroupFolder{ folders = append(folders, &GroupFolder{
GroupCode: fs.GroupCode, GroupCode: fs.GroupCode,
FolderId: item.FolderInfo.GetFolderId(), FolderId: item.FolderInfo.FolderId.Unwrap(),
FolderName: item.FolderInfo.GetFolderName(), FolderName: item.FolderInfo.FolderName.Unwrap(),
CreateTime: int64(item.FolderInfo.GetCreateTime()), CreateTime: int64(item.FolderInfo.CreateTime.Unwrap()),
Creator: int64(item.FolderInfo.GetCreateUin()), Creator: int64(item.FolderInfo.CreateUin.Unwrap()),
CreatorName: item.FolderInfo.GetCreatorName(), CreatorName: item.FolderInfo.CreatorName.Unwrap(),
TotalFileCount: item.FolderInfo.GetTotalFileCount(), TotalFileCount: item.FolderInfo.TotalFileCount.Unwrap(),
}) })
} }
} }
if rsp.FileListInfoRsp.GetIsEnd() { if rsp.FileListInfoRsp.IsEnd.Unwrap() {
break break
} }
startIndex = rsp.FileListInfoRsp.GetNextIndex() startIndex = rsp.FileListInfoRsp.NextIndex.Unwrap()
} }
return files, folders, nil return files, folders, nil
} }
func (fs *GroupFileSystem) UploadFile(p, name, folderId string) error { func (fs *GroupFileSystem) UploadFile(p, name, folderId string) error {
// 同文件等待其他线程上传
fsWaiter.Wait(p)
defer fsWaiter.Done(p)
file, err := os.OpenFile(p, os.O_RDONLY, 0o666) file, err := os.OpenFile(p, os.O_RDONLY, 0o666)
if err != nil { if err != nil {
return errors.Wrap(err, "open file error") return errors.Wrap(err, "open file error")
} }
defer func() { _ = file.Close() }() defer func() { _ = file.Close() }()
md5Hash, size := utils.ComputeMd5AndLength(file) f := &LocalFile{
_, _ = file.Seek(0, io.SeekStart) FileName: name,
sha1H := sha1.New() Body: file,
_, _ = io.Copy(sha1H, file) RemoteFolder: folderId,
sha1Hash := sha1H.Sum(nil)
_, _ = file.Seek(0, io.SeekStart)
req := fs.client.buildGroupFileUploadReq(folderId, name, fs.GroupCode, size, md5Hash, sha1Hash)
i, err := fs.client.callAndDecode(req)
if err != nil {
return errors.Wrap(err, "query upload failed")
} }
rsp := i.(*oidb.UploadFileRspBody) target := message.Source{
if rsp.GetBoolFileExist() { SourceType: message.SourceGroup,
req := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.GetFileId(), rsp.GetBusId(), rand.Int31()) PrimaryID: fs.GroupCode,
_, err := fs.client.call(req)
return err
} }
if len(rsp.UploadIpLanV4) == 0 { return fs.client.UploadFile(target, f)
return errors.New("server requires unsupported ftn upload")
}
ext, _ := proto.Marshal(&exciting.GroupFileUploadExt{
Unknown1: proto.Int32(100),
Unknown2: proto.Int32(1),
Entry: &exciting.GroupFileUploadEntry{
BusiBuff: &exciting.ExcitingBusiInfo{
BusId: rsp.BusId,
SenderUin: &fs.client.Uin,
ReceiverUin: &fs.GroupCode,
GroupCode: &fs.GroupCode,
},
FileEntry: &exciting.ExcitingFileEntry{
FileSize: &size,
Md5: md5Hash,
Sha1: sha1Hash,
FileId: []byte(rsp.GetFileId()),
UploadKey: rsp.CheckKey,
},
ClientInfo: &exciting.ExcitingClientInfo{
ClientType: proto.Int32(2),
AppId: proto.String(fmt.Sprint(fs.client.version.AppId)),
TerminalType: proto.Int32(2),
ClientVer: proto.String("9e9c09dc"),
Unknown: proto.Int32(4),
},
FileNameInfo: &exciting.ExcitingFileNameInfo{FileName: &name},
Host: &exciting.ExcitingHostConfig{Hosts: []*exciting.ExcitingHostInfo{
{
Url: &exciting.ExcitingUrlInfo{
Unknown: proto.Int32(1),
Host: &rsp.UploadIpLanV4[0],
},
Port: rsp.UploadPort,
},
}},
},
Unknown3: proto.Int32(0),
})
client := fs.client
input := highway.ExcitingInput{
CommandID: 71,
Body: file,
Ticket: fs.client.highwaySession.SigSession,
Ext: ext,
}
if _, err = fs.client.highwaySession.UploadExciting(input); err != nil {
return errors.Wrap(err, "upload failed")
}
req = client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.GetFileId(), rsp.GetBusId(), rand.Int31())
_, err = client.call(req)
return err
} }
func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string { func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string {
@ -238,21 +179,21 @@ func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string {
} }
func (fs *GroupFileSystem) CreateFolder(parentFolder, name string) error { func (fs *GroupFileSystem) CreateFolder(parentFolder, name string) error {
if _, err := fs.client.callAndDecode(fs.client.buildGroupFileCreateFolderRequest(fs.GroupCode, parentFolder, name)); err != nil { if _, err := fs.client.sendAndWait(fs.client.buildGroupFileCreateFolderPacket(fs.GroupCode, parentFolder, name)); err != nil {
return errors.Wrap(err, "create folder error") return errors.Wrap(err, "create folder error")
} }
return nil return nil
} }
func (fs *GroupFileSystem) RenameFolder(folderId, newName string) error { func (fs *GroupFileSystem) RenameFolder(folderId, newName string) error {
if _, err := fs.client.callAndDecode(fs.client.buildGroupFileRenameFolderRequest(fs.GroupCode, folderId, newName)); err != nil { if _, err := fs.client.sendAndWait(fs.client.buildGroupFileRenameFolderPacket(fs.GroupCode, folderId, newName)); err != nil {
return errors.Wrap(err, "rename folder error") return errors.Wrap(err, "rename folder error")
} }
return nil return nil
} }
func (fs *GroupFileSystem) DeleteFolder(folderId string) error { func (fs *GroupFileSystem) DeleteFolder(folderId string) error {
if _, err := fs.client.callAndDecode(fs.client.buildGroupFileDeleteFolderRequest(fs.GroupCode, folderId)); err != nil { if _, err := fs.client.sendAndWait(fs.client.buildGroupFileDeleteFolderPacket(fs.GroupCode, folderId)); err != nil {
return errors.Wrap(err, "rename folder error") return errors.Wrap(err, "rename folder error")
} }
return nil return nil
@ -261,252 +202,281 @@ func (fs *GroupFileSystem) DeleteFolder(folderId string) error {
// DeleteFile 删除群文件,需要管理权限. // DeleteFile 删除群文件,需要管理权限.
// 返回错误, 空为删除成功 // 返回错误, 空为删除成功
func (fs *GroupFileSystem) DeleteFile(parentFolderID, fileId string, busId int32) string { func (fs *GroupFileSystem) DeleteFile(parentFolderID, fileId string, busId int32) string {
i, err := fs.client.callAndDecode(fs.client.buildGroupFileDeleteReq(fs.GroupCode, parentFolderID, fileId, busId)) i, err := fs.client.sendAndWait(fs.client.buildGroupFileDeleteReqPacket(fs.GroupCode, parentFolderID, fileId, busId))
if err != nil { if err != nil {
return err.Error() return err.Error()
} }
return i.(string) return i.(string)
} }
func (c *QQClient) buildGroupFileUploadReq(parentFolderID, fileName string, groupCode, fileSize int64, md5, sha1 []byte) *network.Request { // RenameFile 重命名群文件,需要管理权限或者是自己发的文件.
b, _ := proto.Marshal(&oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{ // 返回错误, 空为重命名成功
GroupCode: &groupCode, func (fs *GroupFileSystem) RenameFile(parentFolderID, fileId string, busId int32, newFileName string) string {
i, err := fs.client.sendAndWait(fs.client.buildGroupFileRenameReqPacket(fs.GroupCode, parentFolderID, fileId, busId, newFileName))
if err != nil {
return err.Error()
}
return i.(string)
}
// MoveFile 移动群文件,需要管理权限或者是自己发的文件.
// 返回错误, 空为移动成功
func (fs *GroupFileSystem) MoveFile(parentFolderID, fileId string, busId int32, DestFolderId string) string {
i, err := fs.client.sendAndWait(fs.client.buildGroupFileMoveReqPacket(fs.GroupCode, parentFolderID, fileId, busId, DestFolderId))
if err != nil {
return err.Error()
}
return i.(string)
}
func (c *QQClient) buildGroupFileUploadReqPacket(groupCode int64, file *LocalFile) (uint16, []byte) {
body := &oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{
GroupCode: proto.Some(groupCode),
AppId: proto.Int32(3), AppId: proto.Int32(3),
BusId: proto.Int32(102), BusId: proto.Int32(102),
Entrance: proto.Int32(5), Entrance: proto.Int32(5),
ParentFolderId: &parentFolderID, ParentFolderId: proto.Some(file.RemoteFolder),
FileName: &fileName, FileName: proto.Some(file.FileName),
LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + fileName), LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + file.FileName),
Int64FileSize: &fileSize, Int64FileSize: proto.Some(file.size),
Sha: sha1, Sha: file.sha1,
Md5: md5, Md5: file.md5,
SupportMultiUpload: proto.Bool(true), SupportMultiUpload: proto.Bool(true),
}}) }}
req := &oidb.OIDBSSOPkg{ payload := c.packOIDBPackageProto(1750, 0, body)
Command: 1750, return c.uniPacket("OidbSvc.0x6d6_0", payload)
ServiceType: 0,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d6_0", payload, decodeOIDB6d60Response)
} }
func (c *QQClient) buildGroupFileFeedsRequest(groupCode int64, fileID string, busId, msgRand int32) *network.Request { func (c *QQClient) buildGroupFileFeedsRequest(groupCode int64, fileID string, busId, msgRand int32) (uint16, []byte) {
req := c.packOIDBPackageProto(1753, 4, &oidb.D6D9ReqBody{FeedsInfoReq: &oidb.FeedsReqBody{ req := c.packOIDBPackageProto(1753, 4, &oidb.D6D9ReqBody{FeedsInfoReq: &oidb.FeedsReqBody{
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3), AppId: proto.Uint32(3),
FeedsInfoList: []*oidb.GroupFileFeedsInfo{{ FeedsInfoList: []*oidb.GroupFileFeedsInfo{{
FileId: &fileID, FileId: proto.Some(fileID),
FeedFlag: proto.Uint32(1), FeedFlag: proto.Uint32(1),
BusId: proto.Uint32(uint32(busId)), BusId: proto.Uint32(uint32(busId)),
MsgRandom: proto.Uint32(uint32(msgRand)), MsgRandom: proto.Uint32(uint32(msgRand)),
}}, }},
}}) }})
return c.uniRequest("OidbSvc.0x6d9_4", req, nil) return c.uniPacket("OidbSvc.0x6d9_4", req)
} }
// OidbSvc.0x6d8_1 // OidbSvc.0x6d8_1
func (c *QQClient) buildGroupFileListRequest(groupCode int64, folderID string, startIndex uint32) *network.Request { func (c *QQClient) buildGroupFileListRequestPacket(groupCode int64, folderID string, startIndex uint32) (uint16, []byte) {
body := &oidb.D6D8ReqBody{FileListInfoReq: &oidb.GetFileListReqBody{ body := &oidb.D6D8ReqBody{FileListInfoReq: &oidb.GetFileListReqBody{
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3), AppId: proto.Uint32(3),
FolderId: &folderID, FolderId: proto.Some(folderID),
FileCount: proto.Uint32(20), FileCount: proto.Uint32(20),
AllFileCount: proto.Uint32(0), AllFileCount: proto.Uint32(0),
ReqFrom: proto.Uint32(3), ReqFrom: proto.Uint32(3),
SortBy: proto.Uint32(1), SortBy: proto.Uint32(1),
FilterCode: proto.Uint32(0), FilterCode: proto.Uint32(0),
Uin: proto.Uint64(0), Uin: proto.Uint64(0),
StartIndex: &startIndex, StartIndex: proto.Some(startIndex),
Context: EmptyBytes, Context: EmptyBytes,
}} }}
b, _ := proto.Marshal(body) payload := c.packOIDBPackageProto(1752, 1, body)
req := &oidb.OIDBSSOPkg{ return c.uniPacket("OidbSvc.0x6d8_1", payload)
Command: 1752,
ServiceType: 1,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d8_1", payload, decodeOIDB6d81Response)
} }
func (c *QQClient) buildGroupFileCountRequest(groupCode int64) *network.Request { func (c *QQClient) buildGroupFileCountRequestPacket(groupCode int64) (uint16, []byte) {
body := &oidb.D6D8ReqBody{GroupFileCountReq: &oidb.GetFileCountReqBody{ body := &oidb.D6D8ReqBody{
GroupCode: proto.Uint64(uint64(groupCode)), GroupFileCountReq: &oidb.GetFileCountReqBody{
AppId: proto.Uint32(3), GroupCode: proto.Uint64(uint64(groupCode)),
BusId: proto.Uint32(0), AppId: proto.Uint32(3),
}} BusId: proto.Uint32(0),
b, _ := proto.Marshal(body) },
req := &oidb.OIDBSSOPkg{
Command: 1752,
ServiceType: 2,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
} }
payload, _ := proto.Marshal(req) payload := c.packOIDBPackageProto(1752, 2, body)
return c.uniRequest("OidbSvc.0x6d8_1", payload, decodeOIDB6d81Response) return c.uniPacket("OidbSvc.0x6d8_1", payload)
} }
func (c *QQClient) buildGroupFileSpaceRequest(groupCode int64) *network.Request { func (c *QQClient) buildGroupFileSpaceRequestPacket(groupCode int64) (uint16, []byte) {
body := &oidb.D6D8ReqBody{GroupSpaceReq: &oidb.GetSpaceReqBody{ body := &oidb.D6D8ReqBody{GroupSpaceReq: &oidb.GetSpaceReqBody{
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3), AppId: proto.Uint32(3),
}} }}
b, _ := proto.Marshal(body) payload := c.packOIDBPackageProto(1752, 3, body)
req := &oidb.OIDBSSOPkg{ return c.uniPacket("OidbSvc.0x6d8_1", payload)
Command: 1752,
ServiceType: 3,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d8_1", payload, decodeOIDB6d81Response)
} }
func (c *QQClient) buildGroupFileCreateFolderRequest(groupCode int64, parentFolder, name string) *network.Request { func (c *QQClient) buildGroupFileCreateFolderPacket(groupCode int64, parentFolder, name string) (uint16, []byte) {
payload := c.packOIDBPackageProto(1751, 0, &oidb.D6D7ReqBody{CreateFolderReq: &oidb.CreateFolderReqBody{ payload := c.packOIDBPackageProto(1751, 0, &oidb.D6D7ReqBody{CreateFolderReq: &oidb.CreateFolderReqBody{
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3), AppId: proto.Uint32(3),
ParentFolderId: &parentFolder, ParentFolderId: proto.Some(parentFolder),
FolderName: &name, FolderName: proto.Some(name),
}}) }})
return c.uniRequest("OidbSvc.0x6d7_0", payload, decodeOIDB6d7Response) return c.uniPacket("OidbSvc.0x6d7_0", payload)
} }
func (c *QQClient) buildGroupFileRenameFolderRequest(groupCode int64, folderId, newName string) *network.Request { func (c *QQClient) buildGroupFileRenameFolderPacket(groupCode int64, folderId, newName string) (uint16, []byte) {
payload := c.packOIDBPackageProto(1751, 2, &oidb.D6D7ReqBody{RenameFolderReq: &oidb.RenameFolderReqBody{ payload := c.packOIDBPackageProto(1751, 2, &oidb.D6D7ReqBody{RenameFolderReq: &oidb.RenameFolderReqBody{
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3), AppId: proto.Uint32(3),
FolderId: proto.String(folderId), FolderId: proto.String(folderId),
NewFolderName: proto.String(newName), NewFolderName: proto.String(newName),
}}) }})
return c.uniRequest("OidbSvc.0x6d7_2", payload, decodeOIDB6d7Response) return c.uniPacket("OidbSvc.0x6d7_2", payload)
} }
func (c *QQClient) buildGroupFileDeleteFolderRequest(groupCode int64, folderId string) *network.Request { func (c *QQClient) buildGroupFileDeleteFolderPacket(groupCode int64, folderId string) (uint16, []byte) {
payload := c.packOIDBPackageProto(1751, 1, &oidb.D6D7ReqBody{DeleteFolderReq: &oidb.DeleteFolderReqBody{ payload := c.packOIDBPackageProto(1751, 1, &oidb.D6D7ReqBody{DeleteFolderReq: &oidb.DeleteFolderReqBody{
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3), AppId: proto.Uint32(3),
FolderId: proto.String(folderId), FolderId: proto.String(folderId),
}}) }})
return c.uniRequest("OidbSvc.0x6d7_1", payload, decodeOIDB6d7Response) return c.uniPacket("OidbSvc.0x6d7_1", payload)
} }
// OidbSvc.0x6d6_2 // OidbSvc.0x6d6_2
func (c *QQClient) buildGroupFileDownloadReq(groupCode int64, fileId string, busId int32) *network.Request { func (c *QQClient) buildGroupFileDownloadReqPacket(groupCode int64, fileId string, busId int32) (uint16, []byte) {
body := &oidb.D6D6ReqBody{ body := &oidb.D6D6ReqBody{
DownloadFileReq: &oidb.DownloadFileReqBody{ DownloadFileReq: &oidb.DownloadFileReqBody{
GroupCode: &groupCode, GroupCode: proto.Some(groupCode),
AppId: proto.Int32(3), AppId: proto.Int32(3),
BusId: &busId, BusId: proto.Some(busId),
FileId: &fileId, FileId: proto.Some(fileId),
}, },
} }
b, _ := proto.Marshal(body) payload := c.packOIDBPackageProto(1750, 2, body)
req := &oidb.OIDBSSOPkg{ return c.uniPacket("OidbSvc.0x6d6_2", payload)
Command: 1750,
ServiceType: 2,
Bodybuffer: b,
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d6_2", payload, decodeOIDB6d62Response)
} }
func (c *QQClient) buildGroupFileDeleteReq(groupCode int64, parentFolderId, fileId string, busId int32) *network.Request { func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId, fileId string, busId int32) (uint16, []byte) {
body := &oidb.D6D6ReqBody{DeleteFileReq: &oidb.DeleteFileReqBody{ body := &oidb.D6D6ReqBody{DeleteFileReq: &oidb.DeleteFileReqBody{
GroupCode: &groupCode, GroupCode: proto.Some(groupCode),
AppId: proto.Int32(3), AppId: proto.Int32(3),
BusId: &busId, BusId: proto.Some(busId),
ParentFolderId: &parentFolderId, ParentFolderId: proto.Some(parentFolderId),
FileId: &fileId, FileId: proto.Some(fileId),
}} }}
b, _ := proto.Marshal(body) payload := c.packOIDBPackageProto(1750, 3, body)
req := &oidb.OIDBSSOPkg{ return c.uniPacket("OidbSvc.0x6d6_3", payload)
Command: 1750,
ServiceType: 3,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d6_3", payload, decodeOIDB6d63Response)
} }
func decodeOIDB6d81Response(_ *QQClient, resp *network.Response) (interface{}, error) { func (c *QQClient) buildGroupFileRenameReqPacket(groupCode int64, parentFolderId string, fileId string, busId int32, newFileName string) (uint16, []byte) {
pkg := oidb.OIDBSSOPkg{} body := &oidb.D6D6ReqBody{RenameFileReq: &oidb.RenameFileReqBody{
GroupCode: proto.Some(groupCode),
AppId: proto.Int32(5),
BusId: proto.Some(busId),
FileId: proto.Some(fileId),
ParentFolderId: proto.Some(parentFolderId),
NewFileName: proto.Some(newFileName),
}}
payload := c.packOIDBPackageProto(1750, 4, body)
return c.uniPacket("OidbSvc.0x6d6_4", payload)
}
func (c *QQClient) buildGroupFileMoveReqPacket(groupCode int64, parentFolderId string, fileId string, busId int32, DestFolderId string) (uint16, []byte) {
body := &oidb.D6D6ReqBody{MoveFileReq: &oidb.MoveFileReqBody{
GroupCode: proto.Some(groupCode),
AppId: proto.Int32(5),
BusId: proto.Some(busId),
FileId: proto.Some(fileId),
ParentFolderId: proto.Some(parentFolderId),
DestFolderId: proto.Some(DestFolderId),
}}
payload := c.packOIDBPackageProto(1750, 5, body)
return c.uniPacket("OidbSvc.0x6d6_5", payload)
}
// GroupFileListRespPacket
func decodeOIDB6d81Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D8RspBody{} rsp := oidb.D6D8RspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
} return nil, err
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
return &rsp, nil return &rsp, nil
} }
// OidbSvc.0x6d6_2 // OidbSvc.0x6d6_2 GroupFileDownloadRespPacket
func decodeOIDB6d62Response(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeOIDB6d62Response(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D6RspBody{} rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
} return nil, err
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if rsp.DownloadFileRsp.DownloadUrl == nil { if rsp.DownloadFileRsp.DownloadUrl == nil {
return nil, errors.New(rsp.DownloadFileRsp.GetClientWording()) return nil, errors.New(rsp.DownloadFileRsp.ClientWording.Unwrap())
} }
ip := rsp.DownloadFileRsp.GetDownloadIp() ip := rsp.DownloadFileRsp.DownloadIp.Unwrap()
url := hex.EncodeToString(rsp.DownloadFileRsp.DownloadUrl) url := hex.EncodeToString(rsp.DownloadFileRsp.DownloadUrl)
return fmt.Sprintf("http://%s/ftn_handler/%s/", ip, url), nil return fmt.Sprintf("http://%s/ftn_handler/%s/", ip, url), nil
} }
func decodeOIDB6d63Response(_ *QQClient, resp *network.Response) (interface{}, error) { // GroupFileDeleteRespPacket
pkg := oidb.OIDBSSOPkg{} func decodeOIDB6d63Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{} rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
return nil, err
} }
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { return rsp.DeleteFileRsp.ClientWording.Unwrap(), nil
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if rsp.DeleteFileRsp == nil {
return "", nil
}
return rsp.DeleteFileRsp.GetClientWording(), nil
} }
func decodeOIDB6d60Response(_ *QQClient, resp *network.Response) (interface{}, error) { // GroupFileUploadRespPacket
pkg := oidb.OIDBSSOPkg{} func decodeOIDB6d60Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{} rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
return nil, err
} }
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { u := rsp.UploadFileRsp
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") r := &fileUploadRsp{
Existed: u.BoolFileExist.Unwrap(),
BusID: u.BusId.Unwrap(),
Uuid: []byte(u.FileId.Unwrap()),
UploadKey: u.CheckKey,
UploadIpLanV4: u.UploadIpLanV4,
UploadPort: u.UploadPort.Unwrap(),
} }
return rsp.UploadFileRsp, nil return r, nil
} }
func decodeOIDB6d7Response(_ *QQClient, resp *network.Response) (interface{}, error) { // GroupFileCreateFolderPacket, GroupFileDeleteFolderPacket, GroupFileRenameFolderPacket
pkg := oidb.OIDBSSOPkg{} func decodeOIDB6d7Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D7RspBody{} rsp := oidb.D6D7RspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
return nil, err
} }
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil { if createRsp := rsp.CreateFolderRsp; createRsp != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if retCode := createRsp.RetCode.Unwrap(); retCode != 0 {
return nil, errors.Errorf("create folder error: %v", retCode)
}
} }
if rsp.CreateFolderRsp != nil && rsp.CreateFolderRsp.GetRetCode() != 0 { if renameRsp := rsp.RenameFolderRsp; renameRsp != nil {
return nil, errors.Errorf("create folder error: %v", rsp.CreateFolderRsp.GetRetCode()) if retCode := renameRsp.RetCode.Unwrap(); retCode != 0 {
return nil, errors.Errorf("rename folder error: %v", retCode)
}
} }
if rsp.RenameFolderRsp != nil && rsp.RenameFolderRsp.GetRetCode() != 0 { if deleteRsp := rsp.DeleteFolderRsp; deleteRsp != nil {
return nil, errors.Errorf("rename folder error: %v", rsp.CreateFolderRsp.GetRetCode()) if retCode := deleteRsp.RetCode.Unwrap(); retCode != 0 {
} return nil, errors.Errorf("delete folder error: %v", retCode)
if rsp.DeleteFolderRsp != nil && rsp.DeleteFolderRsp.GetRetCode() != 0 { }
return nil, errors.Errorf("delete folder error: %v", rsp.CreateFolderRsp.GetRetCode())
} }
return nil, nil return nil, nil
} }
// GroupFileRenameRespPacket
func decodeOIDB6d64Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
return rsp.RenameFileRsp.ClientWording.Unwrap(), nil
}
// GroupFileMoveRespPacket
func decodeOIDB6d65Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
return rsp.MoveFileRsp.ClientWording.Unwrap(), nil
}

View File

@ -27,7 +27,6 @@ type (
Uin int64 Uin int64
Code int64 Code int64
Name string Name string
Memo string
OwnerUin int64 OwnerUin int64
GroupCreateTime uint32 GroupCreateTime uint32
GroupLevel uint32 GroupLevel uint32
@ -43,18 +42,17 @@ type (
} }
GroupMemberInfo struct { GroupMemberInfo struct {
Group *GroupInfo Group *GroupInfo
Uin int64 Uin int64
Gender byte Nickname string
Nickname string CardName string
CardName string JoinTime int64
Level uint16 LastSpeakTime int64
JoinTime int64 SpecialTitle string
LastSpeakTime int64 ShutUpTimestamp int64
SpecialTitle string Permission MemberPermission
SpecialTitleExpireTime int64 Level uint16
ShutUpTimestamp int64 Gender byte
Permission MemberPermission
} }
// GroupSearchInfo 通过搜索得到的群信息 // GroupSearchInfo 通过搜索得到的群信息
@ -65,8 +63,13 @@ type (
} }
) )
func init() {
decoders["SummaryCard.ReqSearch"] = decodeGroupSearchResponse
decoders["OidbSvc.0x88d_0"] = decodeGroupInfoResponse
}
func (c *QQClient) GetGroupInfo(groupCode int64) (*GroupInfo, error) { func (c *QQClient) GetGroupInfo(groupCode int64) (*GroupInfo, error) {
i, err := c.callAndDecode(c.buildGroupInfoRequestPacket(groupCode)) i, err := c.sendAndWait(c.buildGroupInfoRequestPacket(groupCode))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -74,9 +77,9 @@ func (c *QQClient) GetGroupInfo(groupCode int64) (*GroupInfo, error) {
} }
// OidbSvc.0x88d_0 // OidbSvc.0x88d_0
func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) *network.Request { func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte) {
body := &oidb.D88DReqBody{ body := &oidb.D88DReqBody{
AppId: proto.Uint32(c.version.AppId), AppId: proto.Uint32(c.version().AppId),
ReqGroupInfo: []*oidb.ReqGroupInfo{ ReqGroupInfo: []*oidb.ReqGroupInfo{
{ {
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
@ -111,18 +114,13 @@ func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) *network.Request
}, },
PcClientVersion: proto.Uint32(0), PcClientVersion: proto.Uint32(0),
} }
b, _ := proto.Marshal(body) payload := c.packOIDBPackageProto(2189, 0, body)
req := &oidb.OIDBSSOPkg{ return c.uniPacket("OidbSvc.0x88d_0", payload)
Command: 2189,
Bodybuffer: b,
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x88d_0", payload, decodeGroupInfoResponse)
} }
// SearchGroupByKeyword 通过关键词搜索陌生群组 // SearchGroupByKeyword 通过关键词搜索陌生群组
func (c *QQClient) SearchGroupByKeyword(keyword string) ([]GroupSearchInfo, error) { func (c *QQClient) SearchGroupByKeyword(keyword string) ([]GroupSearchInfo, error) {
rsp, err := c.callAndDecode(c.buildGroupSearchPacket(keyword)) rsp, err := c.sendAndWait(c.buildGroupSearchPacket(keyword))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "group search failed") return nil, errors.Wrap(err, "group search failed")
} }
@ -130,7 +128,7 @@ func (c *QQClient) SearchGroupByKeyword(keyword string) ([]GroupSearchInfo, erro
} }
// SummaryCard.ReqSearch // SummaryCard.ReqSearch
func (c *QQClient) buildGroupSearchPacket(keyword string) *network.Request { func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
comm, _ := proto.Marshal(&profilecard.BusiComm{ comm, _ := proto.Marshal(&profilecard.BusiComm{
Ver: proto.Int32(1), Ver: proto.Int32(1),
Seq: proto.Int32(rand.Int31()), Seq: proto.Int32(rand.Int31()),
@ -142,7 +140,7 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) *network.Request {
search, _ := proto.Marshal(&profilecard.AccountSearch{ search, _ := proto.Marshal(&profilecard.AccountSearch{
Start: proto.Int32(0), Start: proto.Int32(0),
End: proto.Uint32(4), End: proto.Uint32(4),
Keyword: &keyword, Keyword: proto.Some(keyword),
Highlight: []string{keyword}, Highlight: []string{keyword},
UserLocation: &profilecard.Location{ UserLocation: &profilecard.Location{
Latitude: proto.Float64(0), Latitude: proto.Float64(0),
@ -179,13 +177,13 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) *network.Request {
Context: make(map[string]string), Context: make(map[string]string),
Status: make(map[string]string), Status: make(map[string]string),
} }
return c.uniRequest("SummaryCard.ReqSearch", pkt.ToBytes(), decodeGroupSearchResponse) return c.uniPacket("SummaryCard.ReqSearch", pkt.ToBytes())
} }
// SummaryCard.ReqSearch // SummaryCard.ReqSearch
func decodeGroupSearchResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeGroupSearchResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{} data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
if len(data.Map["RespHead"]["SummaryCard.RespHead"]) > 20 { if len(data.Map["RespHead"]["SummaryCard.RespHead"]) > 20 {
@ -208,11 +206,11 @@ func decodeGroupSearchResponse(_ *QQClient, resp *network.Response) (interface{}
return nil, errors.Wrap(err, "get search result failed") return nil, errors.Wrap(err, "get search result failed")
} }
var ret []GroupSearchInfo var ret []GroupSearchInfo
for _, g := range searchRsp.GetList() { for _, g := range searchRsp.List {
ret = append(ret, GroupSearchInfo{ ret = append(ret, GroupSearchInfo{
Code: int64(g.GetCode()), Code: int64(g.Code.Unwrap()),
Name: g.GetName(), Name: g.Name.Unwrap(),
Memo: g.GetBrief(), Memo: g.Brief.Unwrap(),
}) })
} }
return ret, nil return ret, nil
@ -221,14 +219,11 @@ func decodeGroupSearchResponse(_ *QQClient, resp *network.Response) (interface{}
} }
// OidbSvc.0x88d_0 // OidbSvc.0x88d_0
func decodeGroupInfoResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeGroupInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D88DRspBody{} rsp := oidb.D88DRspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
} return nil, err
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if len(rsp.RspGroupInfo) == 0 { if len(rsp.RspGroupInfo) == 0 {
return nil, errors.New(string(rsp.StrErrorInfo)) return nil, errors.New(string(rsp.StrErrorInfo))
@ -238,17 +233,16 @@ func decodeGroupInfoResponse(c *QQClient, resp *network.Response) (interface{},
return nil, errors.New("group info not found") return nil, errors.New("group info not found")
} }
return &GroupInfo{ return &GroupInfo{
Uin: int64(*info.GroupInfo.GroupUin), Uin: int64(info.GroupInfo.GroupUin.Unwrap()),
Code: int64(*info.GroupCode), Code: int64(info.GroupCode.Unwrap()),
Name: string(info.GroupInfo.GroupName), Name: string(info.GroupInfo.GroupName),
Memo: string(info.GroupInfo.GroupMemo), GroupCreateTime: info.GroupInfo.GroupCreateTime.Unwrap(),
GroupCreateTime: *info.GroupInfo.GroupCreateTime, GroupLevel: info.GroupInfo.GroupLevel.Unwrap(),
GroupLevel: *info.GroupInfo.GroupLevel, OwnerUin: int64(info.GroupInfo.GroupOwner.Unwrap()),
OwnerUin: int64(*info.GroupInfo.GroupOwner), MemberCount: uint16(info.GroupInfo.GroupMemberNum.Unwrap()),
MemberCount: uint16(*info.GroupInfo.GroupMemberNum), MaxMemberCount: uint16(info.GroupInfo.GroupMemberMaxNum.Unwrap()),
MaxMemberCount: uint16(*info.GroupInfo.GroupMemberMaxNum),
Members: []*GroupMemberInfo{}, Members: []*GroupMemberInfo{},
LastMsgSeq: int64(info.GroupInfo.GetGroupCurMsgSeq()), LastMsgSeq: int64(info.GroupInfo.GroupCurMsgSeq.Unwrap()),
client: c, client: c,
}, nil }, nil
} }
@ -256,7 +250,7 @@ func decodeGroupInfoResponse(c *QQClient, resp *network.Response) (interface{},
func (c *QQClient) uploadGroupHeadPortrait(groupCode int64, img []byte) error { func (c *QQClient) uploadGroupHeadPortrait(groupCode int64, img []byte) error {
url := fmt.Sprintf("http://htdata3.qq.com/cgi-bin/httpconn?htcmd=0x6ff0072&ver=5520&ukey=%v&range=0&uin=%v&seq=23&groupuin=%v&filetype=3&imagetype=5&userdata=0&subcmd=1&subver=101&clip=0_0_0_0&filesize=%v", url := fmt.Sprintf("http://htdata3.qq.com/cgi-bin/httpconn?htcmd=0x6ff0072&ver=5520&ukey=%v&range=0&uin=%v&seq=23&groupuin=%v&filetype=3&imagetype=5&userdata=0&subcmd=1&subver=101&clip=0_0_0_0&filesize=%v",
c.getSKey(), c.Uin, groupCode, len(img)) c.getSKey(), c.Uin, groupCode, len(img))
req, _ := http.NewRequest("POST", url, bytes.NewReader(img)) req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(img))
req.Header["User-Agent"] = []string{"Dalvik/2.1.0 (Linux; U; Android 7.1.2; PCRT00 Build/N2G48H)"} req.Header["User-Agent"] = []string{"Dalvik/2.1.0 (Linux; U; Android 7.1.2; PCRT00 Build/N2G48H)"}
req.Header["Content-Type"] = []string{"multipart/form-data;boundary=****"} req.Header["Content-Type"] = []string{"multipart/form-data;boundary=****"}
rsp, err := http.DefaultClient.Do(req) rsp, err := http.DefaultClient.Do(req)
@ -274,13 +268,6 @@ func (g *GroupInfo) UpdateName(newName string) {
} }
} }
func (g *GroupInfo) UpdateMemo(newMemo string) {
if g.AdministratorOrOwner() {
g.client.updateGroupMemo(g.Code, newMemo)
g.Memo = newMemo
}
}
func (g *GroupInfo) UpdateGroupHeadPortrait(img []byte) { func (g *GroupInfo) UpdateGroupHeadPortrait(img []byte) {
if g.AdministratorOrOwner() { if g.AdministratorOrOwner() {
_ = g.client.uploadGroupHeadPortrait(g.Uin, img) _ = g.client.uploadGroupHeadPortrait(g.Uin, img)
@ -331,7 +318,7 @@ func (g *GroupInfo) AdministratorOrOwner() bool {
} }
func (g *GroupInfo) FindMember(uin int64) *GroupMemberInfo { func (g *GroupInfo) FindMember(uin int64) *GroupMemberInfo {
r := g.Read(func(info *GroupInfo) interface{} { r := g.Read(func(info *GroupInfo) any {
return info.FindMemberWithoutLock(uin) return info.FindMemberWithoutLock(uin)
}) })
if r == nil { if r == nil {
@ -363,7 +350,7 @@ func (g *GroupInfo) Update(f func(*GroupInfo)) {
f(g) f(g)
} }
func (g *GroupInfo) Read(f func(*GroupInfo) interface{}) interface{} { func (g *GroupInfo) Read(f func(*GroupInfo) any) any {
g.lock.RLock() g.lock.RLock()
defer g.lock.RUnlock() defer g.lock.RUnlock()
return f(g) return f(g)
@ -402,7 +389,7 @@ func (m *GroupMemberInfo) EditSpecialTitle(title string) {
func (m *GroupMemberInfo) Kick(msg string, block bool) error { func (m *GroupMemberInfo) Kick(msg string, block bool) error {
if m.Uin != m.Group.client.Uin && m.Manageable() { if m.Uin != m.Group.client.Uin && m.Manageable() {
m.Group.client.kickGroupMember(m.Group.Code, m.Uin, msg, block) m.Group.client.KickGroupMembers(m.Group.Code, msg, block, m.Uin)
return nil return nil
} else { } else {
return errors.New("not manageable") return errors.New("not manageable")
@ -421,6 +408,12 @@ func (m *GroupMemberInfo) Mute(time uint32) error {
} }
} }
func (g *GroupInfo) SetAnonymous(enable bool) {
if g.AdministratorOrOwner() {
g.client.setGroupAnonymous(g.Code, enable)
}
}
func (m *GroupMemberInfo) Manageable() bool { func (m *GroupMemberInfo) Manageable() bool {
if m.Uin == m.Group.client.Uin { if m.Uin == m.Group.client.Uin {
return true return true

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"math" "math"
"math/rand" "math/rand"
"strconv" "strconv"
@ -13,8 +12,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/internal/highway"
"github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x388"
"github.com/Mrs4s/MiraiGo/client/pb/longmsg" "github.com/Mrs4s/MiraiGo/client/pb/longmsg"
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/client/pb/multimsg" "github.com/Mrs4s/MiraiGo/client/pb/multimsg"
@ -26,43 +25,26 @@ import (
func init() { func init() {
decoders["OnlinePush.PbPushGroupMsg"] = decodeGroupMessagePacket decoders["OnlinePush.PbPushGroupMsg"] = decodeGroupMessagePacket
decoders["MessageSvc.PbSendMsg"] = decodeMsgSendResponse
decoders["MessageSvc.PbGetGroupMsg"] = decodeGetGroupMsgResponse
decoders["OidbSvc.0x8a7_0"] = decodeAtAllRemainResponse
decoders["OidbSvc.0xeac_1"] = decodeEssenceMsgResponse
decoders["OidbSvc.0xeac_2"] = decodeEssenceMsgResponse
} }
// SendGroupMessage 发送群消息 // SendGroupMessage 发送群消息
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage, f ...bool) *message.GroupMessage { func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage) *message.GroupMessage {
useFram := false
if len(f) > 0 {
useFram = f[0]
}
imgCount := 0 imgCount := 0
for _, e := range m.Elements { for _, e := range m.Elements {
switch e.Type() { switch e.Type() {
case message.Image: case message.Image:
imgCount++ imgCount++
case message.Reply:
useFram = false
} }
} }
msgLen := message.EstimateLength(m.Elements) msgLen := message.EstimateLength(m.Elements)
if msgLen > message.MaxMessageSize || imgCount > 50 { if msgLen > message.MaxMessageSize || imgCount > 50 {
return nil return nil
} }
if !useFram && (msgLen > 100 || imgCount > 2) {
lmsg, err := c.uploadGroupLongMessage(groupCode,
message.NewForwardMessage().AddNode(&message.ForwardNode{
SenderId: c.Uin,
SenderName: c.Nickname,
Time: int32(time.Now().Unix()),
Message: m.Elements,
}))
if err != nil {
c.Error("%v", err)
return nil
}
ret := c.sendGroupMessage(groupCode, false, &message.SendingMessage{Elements: []message.IMessageElement{lmsg}})
ret.Elements = m.Elements
return ret
}
return c.sendGroupMessage(groupCode, false, m) return c.sendGroupMessage(groupCode, false, m)
} }
@ -75,9 +57,8 @@ func (c *QQClient) SendGroupForwardMessage(groupCode int64, m *message.ForwardEl
// GetGroupMessages 从服务器获取历史信息 // GetGroupMessages 从服务器获取历史信息
func (c *QQClient) GetGroupMessages(groupCode, beginSeq, endSeq int64) ([]*message.GroupMessage, error) { func (c *QQClient) GetGroupMessages(groupCode, beginSeq, endSeq int64) ([]*message.GroupMessage, error) {
req := c.buildGetGroupMsgRequest(groupCode, beginSeq, endSeq) seq, pkt := c.buildGetGroupMsgRequest(groupCode, beginSeq, endSeq)
req.Params = network.Params{"raw": false} i, err := c.sendAndWait(seq, pkt, network.RequestParams{"raw": false})
i, err := c.callAndDecode(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -85,7 +66,7 @@ func (c *QQClient) GetGroupMessages(groupCode, beginSeq, endSeq int64) ([]*messa
} }
func (c *QQClient) GetAtAllRemain(groupCode int64) (*AtAllRemainInfo, error) { func (c *QQClient) GetAtAllRemain(groupCode int64) (*AtAllRemainInfo, error) {
i, err := c.callAndDecode(c.buildAtAllRemainRequestPacket(groupCode)) i, err := c.sendAndWait(c.buildAtAllRemainRequestPacket(groupCode))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -115,16 +96,16 @@ func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.Se
serviceFlag = false serviceFlag = false
} }
} }
if !forward && serviceFlag && (imgCount > 1 || message.EstimateLength(m.Elements) > 100) { if !forward && serviceFlag && c.UseFragmentMessage && (imgCount > 1 || message.EstimateLength(m.Elements) > 100) {
div := int32(rand.Uint32()) div := int32(rand.Uint32())
fragmented := m.ToFragmented() fragmented := m.ToFragmented()
for i, elems := range fragmented { for i, elems := range fragmented {
req := c.buildGroupSendingReq(groupCode, mr, int32(len(fragmented)), int32(i), div, forward, elems) _, pkt := c.buildGroupSendingPacket(groupCode, mr, int32(len(fragmented)), int32(i), div, forward, elems)
c.sendReq(req) _ = c.sendPacket(pkt)
} }
} else { } else {
req := c.buildGroupSendingReq(groupCode, mr, 1, 0, 0, forward, m.Elements) _, pkt := c.buildGroupSendingPacket(groupCode, mr, 1, 0, 0, forward, m.Elements)
c.sendReq(req) _ = c.sendPacket(pkt)
} }
var mid int32 var mid int32
ret := &message.GroupMessage{ ret := &message.GroupMessage{
@ -157,60 +138,8 @@ func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.Se
} }
} }
func (c *QQClient) uploadGroupLongMessage(groupCode int64, m *message.ForwardMessage) (*message.ServiceElement, error) {
ts := time.Now().UnixNano()
seq := c.nextGroupSeq()
data, hash := m.CalculateValidationData(seq, rand.Int31(), groupCode)
rsp, body, err := c.multiMsgApplyUp(groupCode, data, hash, 1)
if err != nil {
return nil, errors.Errorf("upload long message error: %v", err)
}
for i, ip := range rsp.Uint32UpIp {
addr := highway.Addr{IP: uint32(ip), Port: int(rsp.Uint32UpPort[i])}
input := highway.Input{
CommandID: 27,
Key: rsp.MsgSig,
Body: bytes.NewReader(body),
}
err := c.highwaySession.Upload(addr, input)
if err != nil {
c.Error("highway upload long message error: %v", err)
continue
}
return genLongTemplate(rsp.MsgResid, m.Brief(), ts), nil
}
return nil, errors.New("upload long message error: highway server list is empty or not available server.")
}
func (c *QQClient) UploadGroupForwardMessage(groupCode int64, m *message.ForwardMessage) *message.ForwardElement {
if m.Length() > 200 {
return nil
}
ts := time.Now().UnixNano()
seq := c.nextGroupSeq()
data, hash, items := m.CalculateValidationDataForward(seq, rand.Int31(), groupCode)
rsp, body, err := c.multiMsgApplyUp(groupCode, data, hash, 2)
if err != nil {
return nil
}
for i, ip := range rsp.Uint32UpIp {
addr := highway.Addr{IP: uint32(ip), Port: int(rsp.Uint32UpPort[i])}
input := highway.Input{
CommandID: 27,
Key: rsp.MsgSig,
Body: bytes.NewReader(body),
}
err := c.highwaySession.Upload(addr, input)
if err != nil {
continue
}
return genForwardTemplate(rsp.MsgResid, m.Preview(), "群聊的聊天记录", "[聊天记录]", "聊天记录", fmt.Sprintf("查看 %d 条转发消息", m.Length()), ts, items)
}
return nil
}
func (c *QQClient) multiMsgApplyUp(groupCode int64, data []byte, hash []byte, buType int32) (*multimsg.MultiMsgApplyUpRsp, []byte, error) { func (c *QQClient) multiMsgApplyUp(groupCode int64, data []byte, hash []byte, buType int32) (*multimsg.MultiMsgApplyUpRsp, []byte, error) {
i, err := c.callAndDecode(c.buildMultiApplyUpPacket(data, hash, buType, utils.ToGroupUin(groupCode))) i, err := c.sendAndWait(c.buildMultiApplyUpPacket(data, hash, buType, utils.ToGroupUin(groupCode)))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -233,7 +162,7 @@ func (c *QQClient) multiMsgApplyUp(groupCode int64, data []byte, hash []byte, bu
} }
// MessageSvc.PbSendMsg // MessageSvc.PbSendMsg
func (c *QQClient) buildGroupSendingReq(groupCode int64, r, pkgNum, pkgIndex, pkgDiv int32, forward bool, m []message.IMessageElement) *network.Request { func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex, pkgDiv int32, forward bool, m []message.IMessageElement) (uint16, []byte) {
var ptt *message.GroupVoiceElement var ptt *message.GroupVoiceElement
if len(m) > 0 { if len(m) > 0 {
if p, ok := m[0].(*message.GroupVoiceElement); ok { if p, ok := m[0].(*message.GroupVoiceElement); ok {
@ -242,8 +171,8 @@ func (c *QQClient) buildGroupSendingReq(groupCode int64, r, pkgNum, pkgIndex, pk
} }
} }
req := &msg.SendMessageRequest{ req := &msg.SendMessageRequest{
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: &groupCode}}, RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: proto.Some(groupCode)}},
ContentHead: &msg.ContentHead{PkgNum: &pkgNum, PkgIndex: &pkgIndex, DivSeq: &pkgDiv}, ContentHead: &msg.ContentHead{PkgNum: proto.Some(pkgNum), PkgIndex: proto.Some(pkgIndex), DivSeq: proto.Some(pkgDiv)},
MsgBody: &msg.MessageBody{ MsgBody: &msg.MessageBody{
RichText: &msg.RichText{ RichText: &msg.RichText{
Elems: message.ToProtoElems(m, true), Elems: message.ToProtoElems(m, true),
@ -256,7 +185,7 @@ func (c *QQClient) buildGroupSendingReq(groupCode int64, r, pkgNum, pkgIndex, pk
}, },
}, },
MsgSeq: proto.Int32(c.nextGroupSeq()), MsgSeq: proto.Int32(c.nextGroupSeq()),
MsgRand: &r, MsgRand: proto.Some(r),
SyncCookie: EmptyBytes, SyncCookie: EmptyBytes,
MsgVia: proto.Int32(1), MsgVia: proto.Int32(1),
MsgCtrl: func() *msg.MsgCtrl { MsgCtrl: func() *msg.MsgCtrl {
@ -267,10 +196,10 @@ func (c *QQClient) buildGroupSendingReq(groupCode int64, r, pkgNum, pkgIndex, pk
}(), }(),
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
return c.uniRequest("MessageSvc.PbSendMsg", payload, nil) return c.uniPacket("MessageSvc.PbSendMsg", payload)
} }
func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) *network.Request { func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) (uint16, []byte) {
req := &msg.GetGroupMsgReq{ req := &msg.GetGroupMsgReq{
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
BeginSeq: proto.Uint64(uint64(beginSeq)), BeginSeq: proto.Uint64(uint64(beginSeq)),
@ -278,10 +207,10 @@ func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) *n
PublicGroup: proto.Bool(false), PublicGroup: proto.Bool(false),
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
return c.uniRequest("MessageSvc.PbGetGroupMsg", payload, decodeGetGroupMsgResponse) return c.uniPacket("MessageSvc.PbGetGroupMsg", payload)
} }
func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) *network.Request { func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byte) {
payload := c.packOIDBPackageProto(2215, 0, &oidb.D8A7ReqBody{ payload := c.packOIDBPackageProto(2215, 0, &oidb.D8A7ReqBody{
SubCmd: proto.Uint32(1), SubCmd: proto.Uint32(1),
LimitIntervalTypeForUin: proto.Uint32(2), LimitIntervalTypeForUin: proto.Uint32(2),
@ -289,84 +218,94 @@ func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) *network.Reque
Uin: proto.Uint64(uint64(c.Uin)), Uin: proto.Uint64(uint64(c.Uin)),
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
}) })
return c.uniRequest("OidbSvc.0x8a7_0", payload, decodeAtAllRemainResponse) return c.uniPacket("OidbSvc.0x8a7_0", payload)
} }
// OnlinePush.PbPushGroupMsg // OnlinePush.PbPushGroupMsg
func decodeGroupMessagePacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodeGroupMessagePacket(c *QQClient, packet *network.Packet) (any, error) {
pkt := msg.PushMessagePacket{} pkt := msg.PushMessagePacket{}
err := proto.Unmarshal(resp.Body, &pkt) err := proto.Unmarshal(packet.Payload, &pkt)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if pkt.Message.Head.GetFromUin() == c.Uin { if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
c.dispatchGroupMessageReceiptEvent(&groupMessageReceiptEvent{ c.dispatchGroupMessageReceiptEvent(&groupMessageReceiptEvent{
Rand: pkt.Message.Body.RichText.Attr.GetRandom(), Rand: pkt.Message.Body.RichText.Attr.Random.Unwrap(),
Seq: pkt.Message.Head.GetMsgSeq(), Seq: pkt.Message.Head.MsgSeq.Unwrap(),
Msg: c.parseGroupMessage(pkt.Message), Msg: c.parseGroupMessage(pkt.Message),
}) })
} }
if pkt.Message.Content != nil && pkt.Message.Content.GetPkgNum() > 1 { if pkt.Message.Content != nil && pkt.Message.Content.PkgNum.Unwrap() > 1 {
var builder *groupMessageBuilder seq := pkt.Message.Content.DivSeq.Unwrap()
i, ok := c.groupMsgBuilders.Load(pkt.Message.Content.GetDivSeq()) builder := c.messageBuilder(pkt.Message.Content.DivSeq.Unwrap())
if !ok { builder.append(pkt.Message)
builder = &groupMessageBuilder{} if builder.len() >= pkt.Message.Content.PkgNum.Unwrap() {
c.groupMsgBuilders.Store(pkt.Message.Content.GetDivSeq(), builder) c.msgBuilders.Delete(seq)
} else { if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
builder = i.(*groupMessageBuilder) c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build()))
}
builder.MessageSlices = append(builder.MessageSlices, pkt.Message)
if int32(len(builder.MessageSlices)) >= pkt.Message.Content.GetPkgNum() {
c.groupMsgBuilders.Delete(pkt.Message.Content.GetDivSeq())
if pkt.Message.Head.GetFromUin() == c.Uin {
c.dispatchGroupMessageSelf(c.parseGroupMessage(builder.build()))
} else { } else {
c.dispatchGroupMessage(c.parseGroupMessage(builder.build())) c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build()))
} }
} }
return nil, nil return nil, nil
} }
if pkt.Message.Head.GetFromUin() == c.Uin { if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
c.dispatchGroupMessageSelf(c.parseGroupMessage(pkt.Message)) c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message))
} else { } else {
c.dispatchGroupMessage(c.parseGroupMessage(pkt.Message)) c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message))
} }
return nil, nil return nil, nil
} }
func decodeGetGroupMsgResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeMsgSendResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := msg.GetGroupMsgResp{} rsp := msg.SendMessageResponse{}
if err := proto.Unmarshal(resp.Body, &rsp); err != nil { if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if rsp.GetResult() != 0 { switch rsp.Result.Unwrap() {
c.Error("get msg error: %v %v", rsp.GetResult(), rsp.GetErrmsg()) case 0: // OK.
return nil, errors.Errorf("get msg error: %v msg: %v", rsp.GetResult(), rsp.GetErrmsg()) case 46:
c.error("sendPacket msg error: 需要使用安全设备验证")
case 55:
c.error("sendPacket msg error: %v Bot has been blocked ta.'s content", rsp.Result.Unwrap())
default:
c.error("sendPacket msg error: %v %v", rsp.Result.Unwrap(), rsp.ErrMsg.Unwrap())
}
return nil, nil
}
func decodeGetGroupMsgResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := msg.GetGroupMsgResp{}
if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if rsp.Result.Unwrap() != 0 {
c.error("get msg error: %v %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
return nil, errors.Errorf("get msg error: %v msg: %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
} }
var ret []*message.GroupMessage var ret []*message.GroupMessage
for _, m := range rsp.Msg { for _, m := range rsp.Msg {
if m.Head.FromUin == nil { if m.Head.FromUin.IsNone() {
continue continue
} }
if m.Content != nil && m.Content.GetPkgNum() > 1 && !resp.Params().Bool("raw") { if m.Content != nil && m.Content.PkgNum.Unwrap() > 1 && !pkt.Params.Bool("raw") {
if m.Content.GetPkgIndex() == 0 { if m.Content.PkgIndex.Unwrap() == 0 {
c.Debug("build fragmented message from history") c.debug("build fragmented message from history")
i := m.Head.GetMsgSeq() - m.Content.GetPkgNum() i := m.Head.MsgSeq.Unwrap() - m.Content.PkgNum.Unwrap()
builder := &groupMessageBuilder{} builder := &messageBuilder{}
for { for {
end := int32(math.Min(float64(i+19), float64(m.Head.GetMsgSeq()+m.Content.GetPkgNum()))) end := int32(math.Min(float64(i+19), float64(m.Head.MsgSeq.Unwrap()+m.Content.PkgNum.Unwrap())))
req := c.buildGetGroupMsgRequest(m.Head.GroupInfo.GetGroupCode(), int64(i), int64(end)) seq, pkt := c.buildGetGroupMsgRequest(m.Head.GroupInfo.GroupCode.Unwrap(), int64(i), int64(end))
req.Params = network.Params{"raw": true} data, err := c.sendAndWait(seq, pkt, network.RequestParams{"raw": true})
data, err := c.callAndDecode(req)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "build fragmented message error") return nil, errors.Wrap(err, "build fragmented message error")
} }
for _, fm := range data.([]*message.GroupMessage) { for _, fm := range data.([]*message.GroupMessage) {
if fm.OriginalObject.Content != nil && fm.OriginalObject.Content.GetDivSeq() == m.Content.GetDivSeq() { if fm.OriginalObject.Content != nil && fm.OriginalObject.Content.DivSeq.Unwrap() == m.Content.DivSeq.Unwrap() {
builder.MessageSlices = append(builder.MessageSlices, fm.OriginalObject) builder.append(fm.OriginalObject)
} }
} }
if end >= m.Head.GetMsgSeq()+m.Content.GetPkgNum() { if end >= m.Head.MsgSeq.Unwrap()+m.Content.PkgNum.Unwrap() {
break break
} }
i = end i = end
@ -384,29 +323,26 @@ func decodeGetGroupMsgResponse(c *QQClient, resp *network.Response) (interface{}
return ret, nil return ret, nil
} }
func decodeAtAllRemainResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeAtAllRemainResponse(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D8A7RspBody{} rsp := oidb.D8A7RspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
} return nil, err
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
return &AtAllRemainInfo{ return &AtAllRemainInfo{
CanAtAll: rsp.GetCanAtAll(), CanAtAll: rsp.CanAtAll.Unwrap(),
RemainAtAllCountForGroup: rsp.GetRemainAtAllCountForGroup(), RemainAtAllCountForGroup: rsp.RemainAtAllCountForGroup.Unwrap(),
RemainAtAllCountForUin: rsp.GetRemainAtAllCountForUin(), RemainAtAllCountForUin: rsp.RemainAtAllCountForUin.Unwrap(),
}, nil }, nil
} }
func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage { func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
group := c.FindGroup(m.Head.GroupInfo.GetGroupCode()) group := c.FindGroup(m.Head.GroupInfo.GroupCode.Unwrap())
if group == nil { if group == nil {
c.Debug("sync group %v.", m.Head.GroupInfo.GetGroupCode()) c.debug("sync group %v.", m.Head.GroupInfo.GroupCode.Unwrap())
info, err := c.GetGroupInfo(m.Head.GroupInfo.GetGroupCode()) info, err := c.GetGroupInfo(m.Head.GroupInfo.GroupCode.Unwrap())
if err != nil { if err != nil {
c.Error("error to sync group %v : %+v", m.Head.GroupInfo.GetGroupCode(), err) c.error("failed to sync group %v : %+v", m.Head.GroupInfo.GroupCode.Unwrap(), err)
return nil return nil
} }
group = info group = info
@ -415,7 +351,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
if len(group.Members) == 0 { if len(group.Members) == 0 {
mem, err := c.GetGroupMembers(group) mem, err := c.GetGroupMembers(group)
if err != nil { if err != nil {
c.Error("error to sync group %v member : %+v", m.Head.GroupInfo.GroupCode, err) c.error("failed to sync group %v members : %+v", m.Head.GroupInfo.GroupCode, err)
return nil return nil
} }
group.Members = mem group.Members = mem
@ -438,20 +374,20 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
IsFriend: false, IsFriend: false,
} }
} else { } else {
mem := group.FindMember(m.Head.GetFromUin()) mem := group.FindMember(m.Head.FromUin.Unwrap())
if mem == nil { if mem == nil {
group.Update(func(_ *GroupInfo) { group.Update(func(_ *GroupInfo) {
if mem = group.FindMemberWithoutLock(m.Head.GetFromUin()); mem != nil { if mem = group.FindMemberWithoutLock(m.Head.FromUin.Unwrap()); mem != nil {
return return
} }
info, _ := c.GetMemberInfo(group.Code, m.Head.GetFromUin()) info, _ := c.GetMemberInfo(group.Code, m.Head.FromUin.Unwrap())
if info == nil { if info == nil {
return return
} }
mem = info mem = info
group.Members = append(group.Members, mem) group.Members = append(group.Members, mem)
group.sort() group.sort()
go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ go c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
Group: group, Group: group,
Member: info, Member: info,
}) })
@ -469,11 +405,11 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
} }
var g *message.GroupMessage var g *message.GroupMessage
g = &message.GroupMessage{ g = &message.GroupMessage{
Id: m.Head.GetMsgSeq(), Id: m.Head.MsgSeq.Unwrap(),
GroupCode: group.Code, GroupCode: group.Code,
GroupName: string(m.Head.GroupInfo.GroupName), GroupName: string(m.Head.GroupInfo.GroupName),
Sender: sender, Sender: sender,
Time: m.Head.GetMsgTime(), Time: m.Head.MsgTime.Unwrap(),
Elements: message.ParseMessageElems(m.Body.RichText.Elems), Elements: message.ParseMessageElems(m.Body.RichText.Elems),
OriginalObject: m, OriginalObject: m,
} }
@ -481,14 +417,14 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
// pre parse // pre parse
for _, elem := range m.Body.RichText.Elems { for _, elem := range m.Body.RichText.Elems {
// is rich long msg // is rich long msg
if elem.GeneralFlags != nil && elem.GeneralFlags.GetLongTextResid() != "" && len(g.Elements) == 1 { if elem.GeneralFlags != nil && elem.GeneralFlags.LongTextResid.Unwrap() != "" && len(g.Elements) == 1 {
if f := c.GetForwardMessage(elem.GeneralFlags.GetLongTextResid()); f != nil && len(f.Nodes) == 1 { if f := c.GetForwardMessage(elem.GeneralFlags.LongTextResid.Unwrap()); f != nil && len(f.Nodes) == 1 {
g = &message.GroupMessage{ g = &message.GroupMessage{
Id: m.Head.GetMsgSeq(), Id: m.Head.MsgSeq.Unwrap(),
GroupCode: group.Code, GroupCode: group.Code,
GroupName: string(m.Head.GroupInfo.GroupName), GroupName: string(m.Head.GroupInfo.GroupName),
Sender: sender, Sender: sender,
Time: m.Head.GetMsgTime(), Time: m.Head.MsgTime.Unwrap(),
Elements: f.Nodes[0].Message, Elements: f.Nodes[0].Message,
OriginalObject: m, OriginalObject: m,
} }
@ -499,8 +435,8 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
} }
} }
if !sender.IsAnonymous() { if !sender.IsAnonymous() {
mem := group.FindMember(m.Head.GetFromUin()) mem := group.FindMember(m.Head.FromUin.Unwrap())
groupCard := m.Head.GroupInfo.GetGroupCard() groupCard := m.Head.GroupInfo.GroupCard.Unwrap()
if extInfo != nil && len(extInfo.GroupCard) > 0 && extInfo.GroupCard[0] == 0x0A { if extInfo != nil && len(extInfo.GroupCard) > 0 && extInfo.GroupCard[0] == 0x0A {
buf := oidb.D8FCCommCardNameBuf{} buf := oidb.D8FCCommCardNameBuf{}
if err := proto.Unmarshal(extInfo.GroupCard, &buf); err == nil && len(buf.RichCardName) > 0 { if err := proto.Unmarshal(extInfo.GroupCard, &buf); err == nil && len(buf.RichCardName) > 0 {
@ -519,7 +455,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
mem.CardName = groupCard mem.CardName = groupCard
} }
if old != mem.CardName { if old != mem.CardName {
go c.dispatchMemberCardUpdatedEvent(&MemberCardUpdatedEvent{ c.MemberCardUpdatedEvent.dispatch(c, &MemberCardUpdatedEvent{
Group: group, Group: group,
OldCard: old, OldCard: old,
Member: mem, Member: mem,
@ -528,66 +464,95 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
} }
} }
if m.Body.RichText.Ptt != nil { if m.Body.RichText.Ptt != nil {
var url string
if len(m.Body.RichText.Ptt.DownPara) == 0 {
req := &cmd0x388.D388ReqBody{
NetType: proto.Uint32(3),
Subcmd: proto.Uint32(4),
GetpttUrlReq: []*cmd0x388.GetPttUrlReq{
{
GroupCode: proto.Uint64(uint64(m.Head.GroupInfo.GroupCode.Unwrap())),
DstUin: proto.Uint64(uint64(m.Head.ToUin.Unwrap())),
Fileid: proto.Uint64(uint64(m.Body.RichText.Ptt.FileId.Unwrap())),
FileMd5: m.Body.RichText.Ptt.FileMd5,
ReqTerm: proto.Uint32(5),
ReqPlatformType: proto.Uint32(9),
InnerIp: proto.Uint32(0),
BuType: proto.Uint32(3),
FileId: proto.Uint64(0),
FileKey: m.Body.RichText.Ptt.FileKey,
ReqTransferType: proto.Uint32(2),
IsAuto: proto.Uint32(1),
},
},
}
payload, _ := proto.Marshal(req)
rsp_raw, _ := c.sendAndWaitDynamic(c.uniPacket("PttStore.GroupPttDown", payload))
rsp := new(cmd0x388.D388RspBody)
proto.Unmarshal(rsp_raw, rsp)
resp := rsp.GetpttUrlRsp[0]
url = "http://" + string(resp.DownDomain) + string(resp.DownPara)
} else {
url = "http://grouptalk.c2c.qq.com" + string(m.Body.RichText.Ptt.DownPara)
}
g.Elements = []message.IMessageElement{ g.Elements = []message.IMessageElement{
&message.VoiceElement{ &message.VoiceElement{
Name: m.Body.RichText.Ptt.GetFileName(), Name: m.Body.RichText.Ptt.FileName.Unwrap(),
Md5: m.Body.RichText.Ptt.FileMd5, Md5: m.Body.RichText.Ptt.FileMd5,
Size: m.Body.RichText.Ptt.GetFileSize(), Size: m.Body.RichText.Ptt.FileSize.Unwrap(),
Url: "http://grouptalk.c2c.qq.com" + string(m.Body.RichText.Ptt.DownPara), Url: url,
}, },
} }
} }
if m.Body.RichText.Attr != nil { if m.Body.RichText.Attr != nil {
g.InternalId = m.Body.RichText.Attr.GetRandom() g.InternalId = m.Body.RichText.Attr.Random.Unwrap()
} }
return g return g
} }
// SetEssenceMessage 设为群精华消息 // SetEssenceMessage 设为群精华消息
func (c *QQClient) SetEssenceMessage(groupCode int64, msgID, msgInternalId int32) error { func (c *QQClient) SetEssenceMessage(groupCode int64, msgID, msgInternalId int32) error {
r, err := c.callAndDecode(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 1)) r, err := c.sendAndWait(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 1))
if err != nil { if err != nil {
return errors.Wrap(err, "set essence msg network") return errors.Wrap(err, "set essence msg network")
} }
rsp := r.(*oidb.EACRspBody) rsp := r.(*oidb.EACRspBody)
if rsp.GetErrorCode() != 0 { if rsp.ErrorCode.Unwrap() != 0 {
return errors.New(rsp.GetWording()) return errors.New(rsp.Wording.Unwrap())
} }
return nil return nil
} }
// DeleteEssenceMessage 移出群精华消息 // DeleteEssenceMessage 移出群精华消息
func (c *QQClient) DeleteEssenceMessage(groupCode int64, msgID, msgInternalId int32) error { func (c *QQClient) DeleteEssenceMessage(groupCode int64, msgID, msgInternalId int32) error {
r, err := c.callAndDecode(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 2)) r, err := c.sendAndWait(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 2))
if err != nil { if err != nil {
return errors.Wrap(err, "set essence msg networ") return errors.Wrap(err, "set essence msg networ")
} }
rsp := r.(*oidb.EACRspBody) rsp := r.(*oidb.EACRspBody)
if rsp.GetErrorCode() != 0 { if rsp.ErrorCode.Unwrap() != 0 {
return errors.New(rsp.GetWording()) return errors.New(rsp.Wording.Unwrap())
} }
return nil return nil
} }
func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand, opType uint32) *network.Request { func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand, opType uint32) (uint16, []byte) {
commandName := "OidbSvc.0xeac_" + strconv.FormatInt(int64(opType), 10) commandName := "OidbSvc.0xeac_" + strconv.FormatInt(int64(opType), 10)
payload := c.packOIDBPackageProto(3756, int32(opType), &oidb.EACReqBody{ // serviceType 2 取消 payload := c.packOIDBPackageProto(3756, int32(opType), &oidb.EACReqBody{ // serviceType 2 取消
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
Seq: proto.Uint32(msgSeq), Seq: proto.Uint32(msgSeq),
Random: proto.Uint32(msgRand), Random: proto.Uint32(msgRand),
}) })
return c.uniRequest(commandName, payload, decodeEssenceMsgResponse) return c.uniPacket(commandName, payload)
} }
// OidbSvc.0xeac_1/2 // OidbSvc.0xeac_1/2
func decodeEssenceMsgResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeEssenceMsgResponse(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := &oidb.EACRspBody{} rsp := &oidb.EACRspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
} return nil, err
if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
return rsp, nil return rsp, nil
} }

View File

@ -15,7 +15,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb/channel" "github.com/Mrs4s/MiraiGo/client/pb/channel"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
@ -176,31 +175,31 @@ func (g *GuildInfo) removeChannel(id uint64) {
} }
func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error) { func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error) {
flags := binary.DynamicProtoMessage{} flags := proto.DynamicMessage{}
for i := 3; i <= 29; i++ { for i := 3; i <= 29; i++ {
flags[uint64(i)] = uint32(1) flags[uint64(i)] = 1
} }
flags[99] = uint32(1) flags[99] = 1
flags[100] = uint32(1) flags[100] = 1
payload := s.c.packOIDBPackageDynamically(3976, 1, binary.DynamicProtoMessage{ payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
1: flags, 1: flags,
3: tinyId, 3: tinyId,
4: uint32(0), 4: 0,
}) })
rsp, err := s.c.uniCall("OidbSvcTrpcTcp.0xfc9_1", payload) rsp, err := s.c.sendAndWaitDynamic(s.c.uniPacket("OidbSvcTrpcTcp.0xfc9_1", payload))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0Xfc9Rsp) body := new(channel.ChannelOidb0Xfc9Rsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
// todo: 解析个性档案 // todo: 解析个性档案
return &GuildUserProfile{ return &GuildUserProfile{
TinyId: tinyId, TinyId: tinyId,
Nickname: body.Profile.GetNickname(), Nickname: body.Profile.Nickname.Unwrap(),
AvatarUrl: body.Profile.GetAvatarUrl(), AvatarUrl: body.Profile.AvatarUrl.Unwrap(),
JoinTime: body.Profile.GetJoinTime(), JoinTime: body.Profile.JoinTime.Unwrap(),
}, nil }, nil
} }
@ -208,81 +207,92 @@ func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error)
// 第一次请求: startIndex = 0 , roleIdIndex = 2 param = "" // 第一次请求: startIndex = 0 , roleIdIndex = 2 param = ""
// 后续请求请根据上次请求的返回值进行设置 // 后续请求请根据上次请求的返回值进行设置
func (s *GuildService) FetchGuildMemberListWithRole(guildId, channelId uint64, startIndex uint32, roleIdIndex uint64, param string) (*FetchGuildMemberListWithRoleResult, error) { func (s *GuildService) FetchGuildMemberListWithRole(guildId, channelId uint64, startIndex uint32, roleIdIndex uint64, param string) (*FetchGuildMemberListWithRoleResult, error) {
u1 := uint32(1) seq := s.c.nextSeq()
m := binary.DynamicProtoMessage{ m := proto.DynamicMessage{
1: guildId, // guild id 1: guildId, // guild id
2: uint32(3), 2: 3,
3: uint32(0), 3: 0,
4: binary.DynamicProtoMessage{ // unknown param, looks like flags 4: proto.DynamicMessage{ // unknown param, looks like flags
1: u1, 2: u1, 3: u1, 4: u1, 5: u1, 6: u1, 7: u1, 8: u1, 20: u1, 1: 1,
2: 1,
3: 1,
4: 1,
5: 1,
6: 1,
7: 1,
8: 1,
20: 1,
}, },
6: startIndex, 6: startIndex,
8: uint32(50), // count 8: 50, // count
12: channelId, 12: channelId,
} }
if param != "" { if param != "" {
m[13] = param m[13] = param
} }
m[14] = roleIdIndex m[14] = roleIdIndex
rsp, err := s.c.uniCall("OidbSvcTrpcTcp.0xf5b_1", s.c.packOIDBPackageDynamically(3931, 1, m)) packet := s.c.uniPacketWithSeq(seq, "OidbSvcTrpcTcp.0xf5b_1", s.c.packOIDBPackageDynamically(3931, 1, m))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0Xf5BRsp) body := new(channel.ChannelOidb0Xf5BRsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
var ret []*GuildMemberInfo var ret []*GuildMemberInfo
for _, memberWithRole := range body.MemberWithRoles { for _, memberWithRole := range body.MemberWithRoles {
for _, mem := range memberWithRole.Members { for _, mem := range memberWithRole.Members {
ret = append(ret, &GuildMemberInfo{ ret = append(ret, &GuildMemberInfo{
TinyId: mem.GetTinyId(), TinyId: mem.TinyId.Unwrap(),
Title: mem.GetTitle(), Title: mem.Title.Unwrap(),
Nickname: mem.GetNickname(), Nickname: mem.Nickname.Unwrap(),
LastSpeakTime: mem.GetLastSpeakTime(), LastSpeakTime: mem.LastSpeakTime.Unwrap(),
Role: memberWithRole.GetRoleId(), Role: memberWithRole.RoleId.Unwrap(),
RoleName: memberWithRole.GetRoleName(), RoleName: memberWithRole.RoleName.Unwrap(),
}) })
} }
} }
for _, mem := range body.Members { for _, mem := range body.Members {
ret = append(ret, &GuildMemberInfo{ ret = append(ret, &GuildMemberInfo{
TinyId: mem.GetTinyId(), TinyId: mem.TinyId.Unwrap(),
Title: mem.GetTitle(), Title: mem.Title.Unwrap(),
Nickname: mem.GetNickname(), Nickname: mem.Nickname.Unwrap(),
LastSpeakTime: mem.GetLastSpeakTime(), LastSpeakTime: mem.LastSpeakTime.Unwrap(),
Role: 1, Role: 1,
RoleName: "普通成员", RoleName: "普通成员",
}) })
} }
return &FetchGuildMemberListWithRoleResult{ return &FetchGuildMemberListWithRoleResult{
Members: ret, Members: ret,
NextIndex: body.GetNextIndex(), NextIndex: body.NextIndex.Unwrap(),
NextRoleId: body.GetNextRoleIdIndex(), NextRoleId: body.NextRoleIdIndex.Unwrap(),
NextQueryParam: body.GetNextQueryParam(), NextQueryParam: body.NextQueryParam.Unwrap(),
Finished: body.NextIndex == nil, Finished: body.NextIndex.IsNone(),
}, nil }, nil
} }
// FetchGuildMemberProfileInfo 获取单个频道成员资料 // FetchGuildMemberProfileInfo 获取单个频道成员资料
func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*GuildUserProfile, error) { func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*GuildUserProfile, error) {
flags := binary.DynamicProtoMessage{} seq := s.c.nextSeq()
flags := proto.DynamicMessage{}
for i := 3; i <= 29; i++ { for i := 3; i <= 29; i++ {
flags[uint64(i)] = uint32(1) flags[uint64(i)] = 1
} }
flags[99] = uint32(1) flags[99] = 1
flags[100] = uint32(1) flags[100] = 1
payload := s.c.packOIDBPackageDynamically(3976, 1, binary.DynamicProtoMessage{ payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
1: flags, 1: flags,
3: tinyId, 3: tinyId,
4: guildId, 4: guildId,
}) })
rsp, err := s.c.uniCall("OidbSvcTrpcTcp.0xf88_1", payload) packet := s.c.uniPacketWithSeq(seq, "OidbSvcTrpcTcp.0xf88_1", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0Xf88Rsp) body := new(channel.ChannelOidb0Xf88Rsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
roles, err := s.fetchMemberRoles(guildId, tinyId) roles, err := s.fetchMemberRoles(guildId, tinyId)
@ -292,73 +302,72 @@ func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*Gui
// todo: 解析个性档案 // todo: 解析个性档案
return &GuildUserProfile{ return &GuildUserProfile{
TinyId: tinyId, TinyId: tinyId,
Nickname: body.Profile.GetNickname(), Nickname: body.Profile.Nickname.Unwrap(),
AvatarUrl: body.Profile.GetAvatarUrl(), AvatarUrl: body.Profile.AvatarUrl.Unwrap(),
JoinTime: body.Profile.GetJoinTime(), JoinTime: body.Profile.JoinTime.Unwrap(),
Roles: roles, Roles: roles,
}, nil }, nil
} }
func (s *GuildService) GetGuildRoles(guildId uint64) ([]*GuildRole, error) { func (s *GuildService) GetGuildRoles(guildId uint64) ([]*GuildRole, error) {
req := s.c.uniRequest("OidbSvcTrpcTcp.0x1019_1", seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1019_1",
s.c.packOIDBPackageDynamically(4121, 1, binary.DynamicProtoMessage{1: guildId}), nil) s.c.packOIDBPackageDynamically(4121, 1, proto.DynamicMessage{1: guildId}))
rsp, err := s.c.call(req) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0X1019Rsp) body := new(channel.ChannelOidb0X1019Rsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
roles := make([]*GuildRole, 0, len(body.GetRoles())) roles := make([]*GuildRole, 0, len(body.Roles))
for _, role := range body.GetRoles() { for _, role := range body.Roles {
roles = append(roles, &GuildRole{ roles = append(roles, &GuildRole{
RoleId: role.GetRoleId(), RoleId: role.RoleId.Unwrap(),
RoleName: role.GetName(), RoleName: role.Name.Unwrap(),
ArgbColor: role.GetArgbColor(), ArgbColor: role.ArgbColor.Unwrap(),
Independent: role.GetIndependent() == 1, Independent: role.Independent.Unwrap() == 1,
Num: role.GetNum(), Num: role.Num.Unwrap(),
Owned: role.GetOwned() == 1, Owned: role.Owned.Unwrap() == 1,
Disabled: role.GetDisabled() == 1, Disabled: role.Disabled.Unwrap() == 1,
MaxNum: role.GetMaxNum(), MaxNum: role.MaxNum.Unwrap(),
}) })
} }
return roles, nil return roles, nil
} }
func (s *GuildService) CreateGuildRole(guildId uint64, name string, color uint32, independent bool, initialUsers []uint64) (uint64, error) { func (s *GuildService) CreateGuildRole(guildId uint64, name string, color uint32, independent bool, initialUsers []uint64) (uint64, error) {
u1 := uint32(1) seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1016_1", s.c.packOIDBPackageDynamically(4118, 1, proto.DynamicMessage{
req := s.c.uniRequest("OidbSvcTrpcTcp.0x1016_1", s.c.packOIDBPackageDynamically(4118, 1, binary.DynamicProtoMessage{
1: guildId, 1: guildId,
2: binary.DynamicProtoMessage{ // todo: 未知参数 2: proto.DynamicMessage{ // todo: 未知参数
1: u1, 1: 1,
2: u1, 2: 1,
3: u1, 3: 1,
}, },
3: binary.DynamicProtoMessage{ 3: proto.DynamicMessage{
1: name, 1: name,
2: color, 2: color,
3: independent, 3: independent,
}, },
4: initialUsers, 4: initialUsers,
}), nil) }))
rsp, err := s.c.call(req) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return 0, errors.Wrap(err, "send packet error") return 0, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0X1016Rsp) body := new(channel.ChannelOidb0X1016Rsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return 0, errors.Wrap(err, "decode packet error") return 0, errors.Wrap(err, "decode packet error")
} }
return body.GetRoleId(), nil return body.RoleId.Unwrap(), nil
} }
func (s *GuildService) DeleteGuildRole(guildId uint64, roleId uint64) error { func (s *GuildService) DeleteGuildRole(guildId uint64, roleId uint64) error {
req := s.c.uniRequest("OidbSvcTrpcTcp.0x100e_1", s.c.packOIDBPackageDynamically(4110, 1, binary.DynamicProtoMessage{ seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100e_1", s.c.packOIDBPackageDynamically(4110, 1, proto.DynamicMessage{
1: guildId, 1: guildId,
2: roleId, 2: roleId,
}), nil) }))
_, err := s.c.call(req) _, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return errors.Wrap(err, "send packet error") return errors.Wrap(err, "send packet error")
} }
@ -366,7 +375,7 @@ func (s *GuildService) DeleteGuildRole(guildId uint64, roleId uint64) error {
} }
func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint64, user []uint64) error { // remove => p2 = false func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint64, user []uint64) error { // remove => p2 = false
setOrRemove := binary.DynamicProtoMessage{ setOrRemove := proto.DynamicMessage{
1: roleId, 1: roleId,
} }
if set { if set {
@ -374,11 +383,11 @@ func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint6
} else { } else {
setOrRemove[3] = user setOrRemove[3] = user
} }
req := s.c.uniRequest("OidbSvcTrpcTcp.0x101a_1", s.c.packOIDBPackageDynamically(4122, 1, binary.DynamicProtoMessage{ seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x101a_1", s.c.packOIDBPackageDynamically(4122, 1, proto.DynamicMessage{
1: guildId, 1: guildId,
2: setOrRemove, 2: setOrRemove,
}), nil) }))
_, err := s.c.call(req) _, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return errors.Wrap(err, "send packet error") return errors.Wrap(err, "send packet error")
} }
@ -386,22 +395,21 @@ func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint6
} }
func (s *GuildService) ModifyRoleInGuild(guildId uint64, roleId uint64, name string, color uint32, indepedent bool) error { func (s *GuildService) ModifyRoleInGuild(guildId uint64, roleId uint64, name string, color uint32, indepedent bool) error {
u1 := uint32(1) seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100d_1", s.c.packOIDBPackageDynamically(4109, 1, proto.DynamicMessage{
req := s.c.uniRequest("OidbSvcTrpcTcp.0x100d_1", s.c.packOIDBPackageDynamically(4109, 1, binary.DynamicProtoMessage{
1: guildId, 1: guildId,
2: roleId, 2: roleId,
3: binary.DynamicProtoMessage{ 3: proto.DynamicMessage{
1: u1, 1: 1,
2: u1, 2: 1,
3: u1, 3: 1,
}, },
4: binary.DynamicProtoMessage{ 4: proto.DynamicMessage{
1: name, 1: name,
2: color, 2: color,
3: indepedent, 3: indepedent,
}, },
}), nil) }))
_, err := s.c.call(req) _, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return errors.Wrap(err, "send packet error") return errors.Wrap(err, "send packet error")
} }
@ -409,57 +417,56 @@ func (s *GuildService) ModifyRoleInGuild(guildId uint64, roleId uint64, name str
} }
func (s *GuildService) FetchGuestGuild(guildId uint64) (*GuildMeta, error) { func (s *GuildService) FetchGuestGuild(guildId uint64) (*GuildMeta, error) {
u1 := uint32(1) payload := s.c.packOIDBPackageDynamically(3927, 9, proto.DynamicMessage{
payload := s.c.packOIDBPackageDynamically(3927, 9, binary.DynamicProtoMessage{ 1: proto.DynamicMessage{
1: binary.DynamicProtoMessage{ 1: proto.DynamicMessage{
1: binary.DynamicProtoMessage{ 2: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 11: 1, 12: 1, 13: 1, 14: 1, 45: 1,
2: u1, 4: u1, 5: u1, 6: u1, 7: u1, 8: u1, 11: u1, 12: u1, 13: u1, 14: u1, 45: u1, 18: 1, 19: 1, 20: 1, 22: 1, 23: 1, 5002: 1, 5003: 1, 5004: 1, 5005: 1, 10007: 1,
18: u1, 19: u1, 20: u1, 22: u1, 23: u1, 5002: u1, 5003: u1, 5004: u1, 5005: u1, 10007: u1,
}, },
2: binary.DynamicProtoMessage{ 2: proto.DynamicMessage{
3: u1, 4: u1, 6: u1, 11: u1, 14: u1, 15: u1, 16: u1, 17: u1, 3: 1, 4: 1, 6: 1, 11: 1, 14: 1, 15: 1, 16: 1, 17: 1,
}, },
}, },
2: binary.DynamicProtoMessage{ 2: proto.DynamicMessage{
1: guildId, 1: guildId,
}, },
}) })
req := s.c.uniRequest("OidbSvcTrpcTcp.0xf57_9", payload, nil) seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf57_9", payload)
rsp, err := s.c.call(req) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0Xf57Rsp) body := new(channel.ChannelOidb0Xf57Rsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
return &GuildMeta{ return &GuildMeta{
GuildName: body.Rsp.Meta.GetName(), GuildName: body.Rsp.Meta.Name.Unwrap(),
GuildProfile: body.Rsp.Meta.GetProfile(), GuildProfile: body.Rsp.Meta.Profile.Unwrap(),
MaxMemberCount: body.Rsp.Meta.GetMaxMemberCount(), MaxMemberCount: body.Rsp.Meta.MaxMemberCount.Unwrap(),
MemberCount: body.Rsp.Meta.GetMemberCount(), MemberCount: body.Rsp.Meta.MemberCount.Unwrap(),
CreateTime: body.Rsp.Meta.GetCreateTime(), CreateTime: body.Rsp.Meta.CreateTime.Unwrap(),
MaxRobotCount: body.Rsp.Meta.GetRobotMaxNum(), MaxRobotCount: body.Rsp.Meta.RobotMaxNum.Unwrap(),
MaxAdminCount: body.Rsp.Meta.GetAdminMaxNum(), MaxAdminCount: body.Rsp.Meta.AdminMaxNum.Unwrap(),
OwnerId: body.Rsp.Meta.GetOwnerId(), OwnerId: body.Rsp.Meta.OwnerId.Unwrap(),
}, nil }, nil
} }
func (s *GuildService) FetchChannelList(guildId uint64) (r []*ChannelInfo, e error) { func (s *GuildService) FetchChannelList(guildId uint64) (r []*ChannelInfo, e error) {
req := s.c.uniRequest("OidbSvcTrpcTcp.0xf5d_1", seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf5d_1",
s.c.packOIDBPackageDynamically(3933, 1, s.c.packOIDBPackageDynamically(3933, 1,
binary.DynamicProtoMessage{ proto.DynamicMessage{
1: guildId, 1: guildId,
3: binary.DynamicProtoMessage{ 3: proto.DynamicMessage{
1: uint32(1), 1: 1,
}, },
}), nil) }))
rsp, err := s.c.call(req) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0Xf5DRsp) body := new(channel.ChannelOidb0Xf5DRsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
for _, info := range body.Rsp.Channels { for _, info := range body.Rsp.Channels {
@ -469,17 +476,13 @@ func (s *GuildService) FetchChannelList(guildId uint64) (r []*ChannelInfo, e err
} }
func (s *GuildService) FetchChannelInfo(guildId, channelId uint64) (*ChannelInfo, error) { func (s *GuildService) FetchChannelInfo(guildId, channelId uint64) (*ChannelInfo, error) {
req := s.c.uniRequest("OidbSvcTrpcTcp.0xf55_1", seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf55_1", s.c.packOIDBPackageDynamically(3925, 1, proto.DynamicMessage{1: guildId, 2: channelId}))
s.c.packOIDBPackageDynamically(3925, 1, binary.DynamicProtoMessage{ rsp, err := s.c.sendAndWaitDynamic(seq, packet)
1: guildId,
2: channelId,
}), nil)
rsp, err := s.c.call(req)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0Xf55Rsp) body := new(channel.ChannelOidb0Xf55Rsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
return convertChannelInfo(body.Info), nil return convertChannelInfo(body.Info), nil
@ -501,8 +504,8 @@ func (s *GuildService) GetTopicChannelFeeds(guildId, channelId uint64) ([]*topic
Count: proto.Uint32(12), Count: proto.Uint32(12),
From: proto.Uint32(0), From: proto.Uint32(0),
ChannelSign: &channel.StChannelSign{ ChannelSign: &channel.StChannelSign{
GuildId: &guildId, GuildId: proto.Some(guildId),
ChannelId: &channelId, ChannelId: proto.Some(channelId),
}, },
FeedAttchInfo: proto.String(""), // isLoadMore FeedAttchInfo: proto.String(""), // isLoadMore
}) })
@ -527,14 +530,14 @@ func (s *GuildService) GetTopicChannelFeeds(guildId, channelId uint64) ([]*topic
}, },
}, },
}) })
call := s.c.uniRequest("QChannelSvr.trpc.qchannel.commreader.ComReader.GetChannelTimelineFeeds", payload, nil) seq, packet := s.c.uniPacket("QChannelSvr.trpc.qchannel.commreader.ComReader.GetChannelTimelineFeeds", payload)
rsp, err := s.c.call(call) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.New("send packet error") return nil, errors.New("send packet error")
} }
pkg := new(qweb.QWebRsp) pkg := new(qweb.QWebRsp)
body := new(channel.StGetChannelFeedsRsp) body := new(channel.StGetChannelFeedsRsp)
if err = proto.Unmarshal(rsp.Body, pkg); err != nil { if err = proto.Unmarshal(rsp, pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil { if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil {
@ -597,53 +600,53 @@ func (s *GuildService) PostTopicChannelFeed(guildId, channelId uint64, feed *top
}, },
}, },
}) })
rsp, err := s.c.call(s.c.uniRequest("QChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed", payload, nil)) seq, packet := s.c.uniPacket("QChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return errors.New("send packet error") return errors.New("send packet error")
} }
pkg := new(qweb.QWebRsp) pkg := new(qweb.QWebRsp)
body := new(channel.StPublishFeedRsp) body := new(channel.StPublishFeedRsp)
if err = proto.Unmarshal(rsp.Body, pkg); err != nil { if err = proto.Unmarshal(rsp, pkg); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message") return errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil { if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message") return errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if body.Feed != nil && body.Feed.Id != nil { if body.Feed != nil && body.Feed.Id.IsNone() {
return nil return nil
} }
return errors.New("post feed error") return errors.New("post feed error")
} }
func (s *GuildService) fetchMemberRoles(guildId uint64, tinyId uint64) ([]*GuildRole, error) { func (s *GuildService) fetchMemberRoles(guildId uint64, tinyId uint64) ([]*GuildRole, error) {
u1 := uint32(1) seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1017_1", s.c.packOIDBPackageDynamically(4119, 1, proto.DynamicMessage{
req := s.c.uniRequest("OidbSvcTrpcTcp.0x1017_1", s.c.packOIDBPackageDynamically(4119, 1, binary.DynamicProtoMessage{
1: guildId, 1: guildId,
2: tinyId, 2: tinyId,
4: binary.DynamicProtoMessage{ 4: proto.DynamicMessage{
1: u1, 1: 1,
2: u1, 2: 1,
3: u1, 3: 1,
}, },
}), nil) }))
rsp, err := s.c.call(req) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.ChannelOidb0X1017Rsp) body := new(channel.ChannelOidb0X1017Rsp)
if err = unpackOIDBPackage(rsp.Body, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
p1 := body.GetP1() p1 := body.P1
if p1 == nil { if p1 == nil {
return nil, errors.New("packet OidbSvcTrpcTcp.0x1017_1: decode p1 error") return nil, errors.New("packet OidbSvcTrpcTcp.0x1017_1: decode p1 error")
} }
roles := make([]*GuildRole, 0, len(p1.GetRoles())) roles := make([]*GuildRole, 0, len(p1.Roles))
for _, role := range p1.GetRoles() { for _, role := range p1.Roles {
roles = append(roles, &GuildRole{ roles = append(roles, &GuildRole{
RoleId: role.GetRoleId(), RoleId: role.RoleId.Unwrap(),
RoleName: role.GetName(), RoleName: role.Name.Unwrap(),
ArgbColor: role.GetArgbColor(), ArgbColor: role.ArgbColor.Unwrap(),
}) })
} }
return roles, nil return roles, nil
@ -656,8 +659,8 @@ func (s *GuildService) fetchChannelListState(guildId uint64, channels []*Channel
for _, info := range channels { for _, info := range channels {
ids = append(ids, info.ChannelId) ids = append(ids, info.ChannelId)
} }
payload := s.c.packOIDBPackageDynamically(4104, 1, binary.DynamicProtoMessage{ payload := s.c.packOIDBPackageDynamically(4104, 1, binary.DynamicMessage{
1: binary.DynamicProtoMessage{ 1: binary.DynamicMessage{
1: guildId, 1: guildId,
2: ids, 2: ids,
}, },
@ -676,96 +679,96 @@ func (s *GuildService) fetchChannelListState(guildId uint64, channels []*Channel
func convertChannelInfo(info *channel.GuildChannelInfo) *ChannelInfo { func convertChannelInfo(info *channel.GuildChannelInfo) *ChannelInfo {
meta := &ChannelMeta{ meta := &ChannelMeta{
CreatorUin: info.GetCreatorUin(), CreatorUin: info.CreatorUin.Unwrap(),
CreatorTinyId: info.GetCreatorTinyId(), CreatorTinyId: info.CreatorTinyId.Unwrap(),
CreateTime: info.GetCreateTime(), CreateTime: info.CreateTime.Unwrap(),
GuildId: info.GetGuildId(), GuildId: info.GuildId.Unwrap(),
VisibleType: info.GetVisibleType(), VisibleType: info.VisibleType.Unwrap(),
CurrentSlowMode: info.GetCurrentSlowModeKey(), CurrentSlowMode: info.CurrentSlowModeKey.Unwrap(),
TalkPermission: info.GetTalkPermission(), TalkPermission: info.TalkPermission.Unwrap(),
} }
if info.TopMsg != nil { if info.TopMsg != nil {
meta.TopMessageSeq = info.TopMsg.GetTopMsgSeq() meta.TopMessageSeq = info.TopMsg.TopMsgSeq.Unwrap()
meta.TopMessageTime = info.TopMsg.GetTopMsgTime() meta.TopMessageTime = info.TopMsg.TopMsgTime.Unwrap()
meta.TopMessageOperatorId = info.TopMsg.GetTopMsgOperatorTinyId() meta.TopMessageOperatorId = info.TopMsg.TopMsgOperatorTinyId.Unwrap()
} }
for _, slow := range info.SlowModeInfos { for _, slow := range info.SlowModeInfos {
meta.SlowModes = append(meta.SlowModes, &ChannelSlowModeInfo{ meta.SlowModes = append(meta.SlowModes, &ChannelSlowModeInfo{
SlowModeKey: slow.GetSlowModeKey(), SlowModeKey: slow.SlowModeKey.Unwrap(),
SpeakFrequency: slow.GetSpeakFrequency(), SpeakFrequency: slow.SpeakFrequency.Unwrap(),
SlowModeCircle: slow.GetSlowModeCircle(), SlowModeCircle: slow.SlowModeCircle.Unwrap(),
SlowModeText: slow.GetSlowModeText(), SlowModeText: slow.SlowModeText.Unwrap(),
}) })
} }
return &ChannelInfo{ return &ChannelInfo{
ChannelId: info.GetChannelId(), ChannelId: info.ChannelId.Unwrap(),
ChannelName: info.GetChannelName(), ChannelName: info.ChannelName.Unwrap(),
NotifyType: uint32(info.GetFinalNotifyType()), NotifyType: uint32(info.FinalNotifyType.Unwrap()),
ChannelType: ChannelType(info.GetChannelType()), ChannelType: ChannelType(info.ChannelType.Unwrap()),
Meta: meta, Meta: meta,
fetchTime: time.Now().Unix(), fetchTime: time.Now().Unix(),
} }
} }
func (c *QQClient) syncChannelFirstView() { func (c *QQClient) syncChannelFirstView() {
rsp, err := c.call(c.buildSyncChannelFirstViewPacket()) rsp, err := c.sendAndWaitDynamic(c.buildSyncChannelFirstViewPacket())
if err != nil { if err != nil {
c.Error("sync channel error: %v", err) c.error("sync channel error: %v", err)
return return
} }
firstViewRsp := new(channel.FirstViewRsp) firstViewRsp := new(channel.FirstViewRsp)
if err = proto.Unmarshal(rsp.Body, firstViewRsp); err != nil { if err = proto.Unmarshal(rsp, firstViewRsp); err != nil {
return return
} }
c.GuildService.TinyId = firstViewRsp.GetSelfTinyid() c.GuildService.TinyId = firstViewRsp.SelfTinyid.Unwrap()
c.GuildService.GuildCount = firstViewRsp.GetGuildCount() c.GuildService.GuildCount = firstViewRsp.GuildCount.Unwrap()
if self, err := c.GuildService.GetUserProfile(c.GuildService.TinyId); err == nil { if self, err := c.GuildService.GetUserProfile(c.GuildService.TinyId); err == nil {
c.GuildService.Nickname = self.Nickname c.GuildService.Nickname = self.Nickname
c.GuildService.AvatarUrl = self.AvatarUrl c.GuildService.AvatarUrl = self.AvatarUrl
} else { } else {
c.Error("get self guild profile error: %v", err) c.error("get self guild profile error: %v", err)
} }
} }
func (c *QQClient) buildSyncChannelFirstViewPacket() *network.Request { func (c *QQClient) buildSyncChannelFirstViewPacket() (uint16, []byte) {
req := &channel.FirstViewReq{ req := &channel.FirstViewReq{
LastMsgTime: proto.Uint64(0), LastMsgTime: proto.Uint64(0),
Seq: proto.Uint32(0), Seq: proto.Uint32(0),
DirectMessageFlag: proto.Uint32(1), DirectMessageFlag: proto.Uint32(1),
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
return c.uniRequest("trpc.group_pro.synclogic.SyncLogic.SyncFirstView", payload, nil) return c.uniPacket("trpc.group_pro.synclogic.SyncLogic.SyncFirstView", payload)
} }
func decodeGuildPushFirstView(c *QQClient, resp *network.Response) (interface{}, error) { func decodeGuildPushFirstView(c *QQClient, pkt *network.Packet) (any, error) {
firstViewMsg := new(channel.FirstViewMsg) firstViewMsg := new(channel.FirstViewMsg)
if err := proto.Unmarshal(resp.Body, firstViewMsg); err != nil { if err := proto.Unmarshal(pkt.Payload, firstViewMsg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if len(firstViewMsg.GuildNodes) > 0 { if len(firstViewMsg.GuildNodes) > 0 {
c.GuildService.Guilds = []*GuildInfo{} c.GuildService.Guilds = []*GuildInfo{}
for _, guild := range firstViewMsg.GuildNodes { for _, guild := range firstViewMsg.GuildNodes {
info := &GuildInfo{ info := &GuildInfo{
GuildId: guild.GetGuildId(), GuildId: guild.GuildId.Unwrap(),
GuildCode: guild.GetGuildCode(), GuildCode: guild.GuildCode.Unwrap(),
GuildName: utils.B2S(guild.GuildName), GuildName: utils.B2S(guild.GuildName),
CoverUrl: fmt.Sprintf("https://groupprocover-76483.picgzc.qpic.cn/%v", guild.GetGuildId()), CoverUrl: fmt.Sprintf("https://groupprocover-76483.picgzc.qpic.cn/%v", guild.GuildId.Unwrap()),
AvatarUrl: fmt.Sprintf("https://groupprohead-76292.picgzc.qpic.cn/%v", guild.GetGuildId()), AvatarUrl: fmt.Sprintf("https://groupprohead-76292.picgzc.qpic.cn/%v", guild.GuildId.Unwrap()),
} }
channels, err := c.GuildService.FetchChannelList(info.GuildId) channels, err := c.GuildService.FetchChannelList(info.GuildId)
if err != nil { if err != nil {
c.Warning("waring: fetch guild %v channel error %v. will use sync node to fill channel list field", guild.GuildId, err) c.warning("warning: fetch guild %v channel error %v. will use sync node to fill channel list field", guild.GuildId, err)
for _, node := range guild.ChannelNodes { for _, node := range guild.ChannelNodes {
meta := new(channel.ChannelMsgMeta) meta := new(channel.ChannelMsgMeta)
_ = proto.Unmarshal(node.Meta, meta) _ = proto.Unmarshal(node.Meta, meta)
info.Channels = append(info.Channels, &ChannelInfo{ info.Channels = append(info.Channels, &ChannelInfo{
ChannelId: node.GetChannelId(), ChannelId: node.ChannelId.Unwrap(),
ChannelName: utils.B2S(node.ChannelName), ChannelName: utils.B2S(node.ChannelName),
Time: node.GetTime(), Time: node.Time.Unwrap(),
EventTime: node.GetEventTime(), EventTime: node.EventTime.Unwrap(),
NotifyType: node.GetNotifyType(), NotifyType: node.NotifyType.Unwrap(),
ChannelType: ChannelType(node.GetChannelType()), ChannelType: ChannelType(node.ChannelType.Unwrap()),
AtAllSeq: meta.GetAtAllSeq(), AtAllSeq: meta.AtAllSeq.Unwrap(),
}) })
} }
} else { } else {
@ -775,7 +778,7 @@ func decodeGuildPushFirstView(c *QQClient, resp *network.Response) (interface{},
c.GuildService.Guilds = append(c.GuildService.Guilds, info) c.GuildService.Guilds = append(c.GuildService.Guilds, info)
} }
} }
if len(firstViewMsg.ChannelMsgs) > 0 { // sync msg // if len(firstViewMsg.ChannelMsgs) > 0 { // sync msg
} // }
return nil, nil return nil, nil
} }

View File

@ -27,12 +27,12 @@ type tipsPushInfo struct {
ChannelId uint64 ChannelId uint64
} }
func decodeGuildEventFlowPacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodeGuildEventFlowPacket(c *QQClient, pkt *network.Packet) (any, error) {
push := new(channel.MsgOnlinePush) push := new(channel.MsgOnlinePush)
if err := proto.Unmarshal(resp.Body, push); err != nil { if err := proto.Unmarshal(pkt.Payload, push); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if push.GetCompressFlag() == 1 && len(push.CompressMsg) > 0 { if push.CompressFlag.Unwrap() == 1 && len(push.CompressMsg) > 0 {
press := new(channel.PressMsg) press := new(channel.PressMsg)
dst := make([]byte, len(push.CompressMsg)*2) dst := make([]byte, len(push.CompressMsg)*2)
i, err := lz4.UncompressBlock(push.CompressMsg, dst) i, err := lz4.UncompressBlock(push.CompressMsg, dst)
@ -49,10 +49,13 @@ func decodeGuildEventFlowPacket(c *QQClient, resp *network.Response) (interface{
push.Msgs = press.Msgs push.Msgs = press.Msgs
} }
for _, m := range push.Msgs { for _, m := range push.Msgs {
if m.Head.ContentHead.GetType() == 3841 { if m.Head == nil {
continue
}
if m.Head.ContentHead.Type.Unwrap() == 3841 {
// todo: 回头 event flow 的处理移出去重构下逻辑, 先暂时这样方便改 // todo: 回头 event flow 的处理移出去重构下逻辑, 先暂时这样方便改
var common *msg.CommonElem var common *msg.CommonElem
if m.Body != nil { if m.Body != nil && m.Body.RichText != nil {
for _, e := range m.Body.RichText.Elems { for _, e := range m.Body.RichText.Elems {
if e.CommonElem != nil { if e.CommonElem != nil {
common = e.CommonElem common = e.CommonElem
@ -60,13 +63,13 @@ func decodeGuildEventFlowPacket(c *QQClient, resp *network.Response) (interface{
} }
} }
} }
if m.Head.ContentHead.GetSubType() == 2 { // todo: tips? if m.Head.ContentHead.SubType.Unwrap() == 2 { // todo: tips?
if common == nil { // empty tips // if common == nil { // empty tips
} // }
tipsInfo := &tipsPushInfo{ tipsInfo := &tipsPushInfo{
TinyId: m.Head.RoutingHead.GetFromTinyid(), TinyId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GetGuildId(), GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: m.Head.RoutingHead.GetChannelId(), ChannelId: m.Head.RoutingHead.ChannelId.Unwrap(),
} }
/* /*
if len(m.CtrlHead.IncludeUin) > 0 { if len(m.CtrlHead.IncludeUin) > 0 {
@ -75,23 +78,23 @@ func decodeGuildEventFlowPacket(c *QQClient, resp *network.Response) (interface{
*/ */
return tipsInfo, nil return tipsInfo, nil
} }
if common == nil || common.GetServiceType() != 500 { if common == nil || common.ServiceType.Unwrap() != 500 {
continue continue
} }
eventBody := new(channel.EventBody) eventBody := new(channel.EventBody)
if err := proto.Unmarshal(common.PbElem, eventBody); err != nil { if err := proto.Unmarshal(common.PbElem, eventBody); err != nil {
c.Error("failed to unmarshal guild channel event body: %v", err) c.error("failed to unmarshal guild channel event body: %v", err)
continue continue
} }
c.processGuildEventBody(m, eventBody) c.processGuildEventBody(m, eventBody)
continue continue
} }
if m.Head.ContentHead.GetType() == 3840 { if m.Head.ContentHead.Type.Unwrap() == 3840 {
if m.Head.RoutingHead.GetDirectMessageFlag() == 1 { if m.Head.RoutingHead.DirectMessageFlag.Unwrap() == 1 {
// todo: direct message decode // todo: direct message decode
continue continue
} }
if m.Head.RoutingHead.GetFromTinyid() == c.GuildService.TinyId { if m.Head.RoutingHead.FromTinyid.Unwrap() == c.GuildService.TinyId {
continue continue
} }
if cm := c.GuildService.parseGuildChannelMessage(m); cm != nil { if cm := c.GuildService.parseGuildChannelMessage(m); cm != nil {
@ -104,39 +107,39 @@ func decodeGuildEventFlowPacket(c *QQClient, resp *network.Response) (interface{
func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody *channel.EventBody) { func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody *channel.EventBody) {
var guild *GuildInfo var guild *GuildInfo
if m.Head.RoutingHead.GetGuildId() != 0 { if m.Head.RoutingHead.GuildId.Unwrap() != 0 {
if guild = c.GuildService.FindGuild(m.Head.RoutingHead.GetGuildId()); guild == nil { if guild = c.GuildService.FindGuild(m.Head.RoutingHead.GuildId.Unwrap()); guild == nil {
c.Warning("process channel event error: guild not found.") c.warning("process channel event error: guild not found.")
return return
} }
} }
switch { switch {
case eventBody.CreateChan != nil: case eventBody.CreateChan != nil:
for _, chanId := range eventBody.CreateChan.CreateId { for _, chanId := range eventBody.CreateChan.CreateId {
if guild.FindChannel(chanId.GetChanId()) != nil { if guild.FindChannel(chanId.ChanId.Unwrap()) != nil {
continue continue
} }
channelInfo, err := c.GuildService.FetchChannelInfo(guild.GuildId, chanId.GetChanId()) channelInfo, err := c.GuildService.FetchChannelInfo(guild.GuildId, chanId.ChanId.Unwrap())
if err != nil { if err != nil {
c.Warning("process create channel event error: fetch channel info error: %v", err) c.warning("process create channel event error: fetch channel info error: %v", err)
continue continue
} }
guild.Channels = append(guild.Channels, channelInfo) guild.Channels = append(guild.Channels, channelInfo)
c.dispatchGuildChannelCreatedEvent(&GuildChannelOperationEvent{ c.dispatchGuildChannelCreatedEvent(&GuildChannelOperationEvent{
OperatorId: m.Head.RoutingHead.GetFromTinyid(), OperatorId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GetGuildId(), GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelInfo: channelInfo, ChannelInfo: channelInfo,
}) })
} }
case eventBody.DestroyChan != nil: case eventBody.DestroyChan != nil:
for _, chanId := range eventBody.DestroyChan.DeleteId { for _, chanId := range eventBody.DestroyChan.DeleteId {
channelInfo := guild.FindChannel(chanId.GetChanId()) channelInfo := guild.FindChannel(chanId.ChanId.Unwrap())
if channelInfo == nil { if channelInfo == nil {
continue continue
} }
guild.removeChannel(chanId.GetChanId()) guild.removeChannel(chanId.ChanId.Unwrap())
c.dispatchGuildChannelDestroyedEvent(&GuildChannelOperationEvent{ c.dispatchGuildChannelDestroyedEvent(&GuildChannelOperationEvent{
OperatorId: m.Head.RoutingHead.GetFromTinyid(), OperatorId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: guild.GuildId, GuildId: guild.GuildId,
ChannelInfo: channelInfo, ChannelInfo: channelInfo,
}) })
@ -144,11 +147,11 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody
case eventBody.ChangeChanInfo != nil: case eventBody.ChangeChanInfo != nil:
updateChanLock.Lock() updateChanLock.Lock()
defer updateChanLock.Unlock() defer updateChanLock.Unlock()
oldInfo := guild.FindChannel(eventBody.ChangeChanInfo.GetChanId()) oldInfo := guild.FindChannel(eventBody.ChangeChanInfo.ChanId.Unwrap())
if oldInfo == nil { if oldInfo == nil {
info, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GetGuildId(), eventBody.ChangeChanInfo.GetChanId()) info, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GuildId.Unwrap(), eventBody.ChangeChanInfo.ChanId.Unwrap())
if err != nil { if err != nil {
c.Error("error to decode channel info updated event: fetch channel info failed: %v", err) c.error("failed to decode channel info updated event: fetch channel info failed: %v", err)
return return
} }
guild.Channels = append(guild.Channels, info) guild.Channels = append(guild.Channels, info)
@ -157,9 +160,9 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody
if time.Now().Unix()-oldInfo.fetchTime <= 2 { if time.Now().Unix()-oldInfo.fetchTime <= 2 {
return return
} }
newInfo, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GetGuildId(), eventBody.ChangeChanInfo.GetChanId()) newInfo, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GuildId.Unwrap(), eventBody.ChangeChanInfo.ChanId.Unwrap())
if err != nil { if err != nil {
c.Error("error to decode channel info updated event: fetch channel info failed: %v", err) c.error("failed to decode channel info updated event: fetch channel info failed: %v", err)
return return
} }
for i := range guild.Channels { for i := range guild.Channels {
@ -169,22 +172,22 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody
} }
} }
c.dispatchGuildChannelUpdatedEvent(&GuildChannelUpdatedEvent{ c.dispatchGuildChannelUpdatedEvent(&GuildChannelUpdatedEvent{
OperatorId: m.Head.RoutingHead.GetFromTinyid(), OperatorId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GetGuildId(), GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: eventBody.ChangeChanInfo.GetChanId(), ChannelId: eventBody.ChangeChanInfo.ChanId.Unwrap(),
OldChannelInfo: oldInfo, OldChannelInfo: oldInfo,
NewChannelInfo: newInfo, NewChannelInfo: newInfo,
}) })
case eventBody.JoinGuild != nil: case eventBody.JoinGuild != nil:
/* 应该不会重复推送把, 不会吧不会吧 /* 应该不会重复推送把, 不会吧不会吧
if mem := guild.FindMember(eventBody.JoinGuild.GetMemberTinyid()); mem != nil { if mem := guild.FindMember(eventBody.JoinGuild.MemberTinyid.Unwrap()); mem != nil {
c.Info("ignore join guild event: member %v already exists", mem.TinyId) c.info("ignore join guild event: member %v already exists", mem.TinyId)
return return
} }
*/ */
profile, err := c.GuildService.FetchGuildMemberProfileInfo(guild.GuildId, eventBody.JoinGuild.GetMemberTinyid()) profile, err := c.GuildService.FetchGuildMemberProfileInfo(guild.GuildId, eventBody.JoinGuild.MemberTinyid.Unwrap())
if err != nil { if err != nil {
c.Error("error to decode member join guild event: get member profile error: %v", err) c.error("failed to decode member join guild event: get member profile error: %v", err)
return return
} }
info := &GuildMemberInfo{ info := &GuildMemberInfo{
@ -197,33 +200,33 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody
Member: info, Member: info,
}) })
case eventBody.UpdateMsg != nil: case eventBody.UpdateMsg != nil:
if eventBody.UpdateMsg.GetEventType() == 1 || eventBody.UpdateMsg.GetEventType() == 2 { if eventBody.UpdateMsg.EventType.Unwrap() == 1 || eventBody.UpdateMsg.EventType.Unwrap() == 2 {
c.dispatchGuildMessageRecalledEvent(&GuildMessageRecalledEvent{ c.dispatchGuildMessageRecalledEvent(&GuildMessageRecalledEvent{
OperatorId: eventBody.UpdateMsg.GetOperatorTinyid(), OperatorId: eventBody.UpdateMsg.OperatorTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GetGuildId(), GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: m.Head.RoutingHead.GetChannelId(), ChannelId: m.Head.RoutingHead.ChannelId.Unwrap(),
MessageId: eventBody.UpdateMsg.GetMsgSeq(), MessageId: eventBody.UpdateMsg.MsgSeq.Unwrap(),
RecallTime: int64(m.Head.ContentHead.GetTime()), RecallTime: int64(m.Head.ContentHead.Time.Unwrap()),
}) })
return return
} }
if eventBody.UpdateMsg.GetEventType() == 4 { // 消息贴表情更新 (包含添加或删除) if eventBody.UpdateMsg.EventType.Unwrap() == 4 { // 消息贴表情更新 (包含添加或删除)
t, err := c.GuildService.pullChannelMessages(m.Head.RoutingHead.GetGuildId(), m.Head.RoutingHead.GetChannelId(), eventBody.UpdateMsg.GetMsgSeq(), eventBody.UpdateMsg.GetMsgSeq(), eventBody.UpdateMsg.GetEventVersion()-1, false) t, err := c.GuildService.pullChannelMessages(m.Head.RoutingHead.GuildId.Unwrap(), m.Head.RoutingHead.ChannelId.Unwrap(), eventBody.UpdateMsg.MsgSeq.Unwrap(), eventBody.UpdateMsg.MsgSeq.Unwrap(), eventBody.UpdateMsg.EventVersion.Unwrap()-1, false)
if err != nil || len(t) == 0 { if err != nil || len(t) == 0 {
c.Error("process guild event flow error: pull eventMsg message error: %v", err) c.error("process guild event flow error: pull eventMsg message error: %v", err)
return return
} }
// 自己的消息被贴表情会单独推送一个tips, 这里不需要解析 // 自己的消息被贴表情会单独推送一个tips, 这里不需要解析
if t[0].Head.RoutingHead.GetFromTinyid() == c.GuildService.TinyId { if t[0].Head.RoutingHead.FromTinyid.Unwrap() == c.GuildService.TinyId {
return return
} }
updatedEvent := &GuildMessageReactionsUpdatedEvent{ updatedEvent := &GuildMessageReactionsUpdatedEvent{
GuildId: m.Head.RoutingHead.GetGuildId(), GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: m.Head.RoutingHead.GetChannelId(), ChannelId: m.Head.RoutingHead.ChannelId.Unwrap(),
MessageId: t[0].Head.ContentHead.GetSeq(), MessageId: t[0].Head.ContentHead.Seq.Unwrap(),
CurrentReactions: decodeGuildMessageEmojiReactions(t[0]), CurrentReactions: decodeGuildMessageEmojiReactions(t[0]),
} }
tipsInfo, err := c.waitPacketTimeoutSyncF("MsgPush.PushGroupProMsg", time.Second, func(i interface{}) bool { tipsInfo, err := c.waitPacketTimeoutSyncF("MsgPush.PushGroupProMsg", time.Second, func(i any) bool {
if i == nil { if i == nil {
return false return false
} }

View File

@ -1,38 +1,23 @@
package client package client
import ( import (
"bytes" "fmt"
"encoding/hex"
"image"
"io" "io"
"math/rand" "math/rand"
"strconv" "strconv"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/internal/highway"
"github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/channel" "github.com/Mrs4s/MiraiGo/client/pb/channel"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x388" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x388"
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/client/pb/pttcenter"
"github.com/Mrs4s/MiraiGo/internal/proto" "github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils"
) )
type guildImageUploadResponse struct { func init() {
UploadKey []byte decoders["ImgStore.QQMeetPicUp"] = decodeGuildImageStoreResponse
UploadIp []uint32
UploadPort []uint32
Width int32
Height int32
Message string
DownloadIndex string
FileId int64
ResultCode int32
IsExists bool
} }
func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *message.SendingMessage) (*message.GuildChannelMessage, error) { func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *message.SendingMessage) (*message.GuildChannelMessage, error) {
@ -48,8 +33,8 @@ func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *mes
req := &channel.DF62ReqBody{Msg: &channel.ChannelMsgContent{ req := &channel.DF62ReqBody{Msg: &channel.ChannelMsgContent{
Head: &channel.ChannelMsgHead{ Head: &channel.ChannelMsgHead{
RoutingHead: &channel.ChannelRoutingHead{ RoutingHead: &channel.ChannelRoutingHead{
GuildId: &guildId, GuildId: proto.Some(guildId),
ChannelId: &channelId, ChannelId: proto.Some(channelId),
FromUin: proto.Uint64(uint64(s.c.Uin)), FromUin: proto.Uint64(uint64(s.c.Uin)),
}, },
ContentHead: &channel.ChannelContentHead{ ContentHead: &channel.ChannelContentHead{
@ -64,29 +49,30 @@ func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *mes
}, },
}} }}
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
rsp, err := s.c.uniCall("MsgProxy.SendMsg", payload) seq, packet := s.c.uniPacket("MsgProxy.SendMsg", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := new(channel.DF62RspBody) body := new(channel.DF62RspBody)
if err = proto.Unmarshal(rsp.Body, body); err != nil { if err = proto.Unmarshal(rsp, body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if body.GetResult() != 0 { if body.Result.Unwrap() != 0 {
return nil, errors.Errorf("send channel message error: server response %v", body.GetResult()) return nil, errors.Errorf("send channel message error: server response %v", body.Result.Unwrap())
} }
elements := m.Elements elements := m.Elements
if body.Body != nil && body.Body.RichText != nil { if body.Body != nil && body.Body.RichText != nil {
elements = message.ParseMessageElems(body.Body.RichText.Elems) elements = message.ParseMessageElems(body.Body.RichText.Elems)
} }
return &message.GuildChannelMessage{ return &message.GuildChannelMessage{
Id: body.Head.ContentHead.GetSeq(), Id: body.Head.ContentHead.Seq.Unwrap(),
InternalId: body.Head.ContentHead.GetRandom(), InternalId: body.Head.ContentHead.Random.Unwrap(),
GuildId: guildId, GuildId: guildId,
ChannelId: channelId, ChannelId: channelId,
Time: int64(body.GetSendTime()), Time: int64(body.SendTime.Unwrap()),
Sender: &message.GuildSender{ Sender: &message.GuildSender{
TinyId: body.Head.RoutingHead.GetFromTinyid(), TinyId: body.Head.RoutingHead.FromTinyid.Unwrap(),
Nickname: s.Nickname, Nickname: s.Nickname,
}, },
Elements: elements, Elements: elements,
@ -94,15 +80,15 @@ func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *mes
} }
func (s *GuildService) QueryImage(guildId, channelId uint64, hash []byte, size uint64) (*message.GuildImageElement, error) { func (s *GuildService) QueryImage(guildId, channelId uint64, hash []byte, size uint64) (*message.GuildImageElement, error) {
rsp, err := s.c.callAndDecode(s.c.buildGuildImageStorePacket(guildId, channelId, hash, size)) rsp, err := s.c.sendAndWait(s.c.buildGuildImageStorePacket(guildId, channelId, hash, size))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
body := rsp.(*guildImageUploadResponse) body := rsp.(*imageUploadResponse)
if body.IsExists { if body.IsExists {
return &message.GuildImageElement{ return &message.GuildImageElement{
FileId: body.FileId, FileId: body.FileId,
FilePath: hex.EncodeToString(hash) + ".jpg", FilePath: fmt.Sprintf("%x.jpg", hash),
Size: int32(size), Size: int32(size),
DownloadIndex: body.DownloadIndex, DownloadIndex: body.DownloadIndex,
Width: body.Width, Width: body.Width,
@ -113,60 +99,18 @@ func (s *GuildService) QueryImage(guildId, channelId uint64, hash []byte, size u
return nil, errors.New("image is not exists") return nil, errors.New("image is not exists")
} }
// Deprecated: use QQClient.UploadImage instead
func (s *GuildService) UploadGuildImage(guildId, channelId uint64, img io.ReadSeeker) (*message.GuildImageElement, error) { func (s *GuildService) UploadGuildImage(guildId, channelId uint64, img io.ReadSeeker) (*message.GuildImageElement, error) {
_, _ = img.Seek(0, io.SeekStart) // safe source := message.Source{
fh, length := utils.ComputeMd5AndLength(img) SourceType: message.SourceGuildChannel,
_, _ = img.Seek(0, io.SeekStart) PrimaryID: int64(guildId),
rsp, err := s.c.callAndDecode(s.c.buildGuildImageStorePacket(guildId, channelId, fh, uint64(length))) SecondaryID: int64(channelId),
}
image, err := s.c.uploadGroupOrGuildImage(source, img)
if err != nil { if err != nil {
return nil, err return nil, err
} }
body := rsp.(*guildImageUploadResponse) return image.(*message.GuildImageElement), nil
if body.IsExists {
goto ok
}
if s.c.highwaySession.AddrLength() == 0 {
for i, addr := range body.UploadIp {
s.c.highwaySession.AppendAddr(addr, body.UploadPort[i])
}
}
if _, err = s.c.highwaySession.UploadBDH(highway.BdhInput{
CommandID: 83,
Body: img,
Ticket: body.UploadKey,
Ext: binary.DynamicProtoMessage{11: guildId, 12: channelId}.Encode(),
Encrypt: false,
}); err == nil {
goto ok
}
return nil, errors.Wrap(err, "highway upload error")
ok:
_, _ = img.Seek(0, io.SeekStart)
i, _, err := image.DecodeConfig(img)
var imageType int32 = 1000
_, _ = img.Seek(0, io.SeekStart)
tmp := make([]byte, 4)
_, _ = img.Read(tmp)
if bytes.Equal(tmp, []byte{0x47, 0x49, 0x46, 0x38}) {
imageType = 2000
}
width := int32(i.Width)
height := int32(i.Height)
if err != nil {
s.c.Warning("waring: decode image error: %v. this image will be displayed by wrong size in pc guild client", err)
width = 200
height = 200
}
return &message.GuildImageElement{
FileId: body.FileId,
FilePath: hex.EncodeToString(fh) + ".jpg",
Size: int32(length),
DownloadIndex: body.DownloadIndex,
Width: width,
Height: height,
ImageType: imageType,
Md5: fh,
}, nil
} }
func (s *GuildService) PullGuildChannelMessage(guildId, channelId, beginSeq, endSeq uint64) (r []*message.GuildChannelMessage, e error) { func (s *GuildService) PullGuildChannelMessage(guildId, channelId, beginSeq, endSeq uint64) (r []*message.GuildChannelMessage, e error) {
@ -188,10 +132,10 @@ func (s *GuildService) PullGuildChannelMessage(guildId, channelId, beginSeq, end
func (s *GuildService) pullChannelMessages(guildId, channelId, beginSeq, endSeq, eventVersion uint64, direct bool) ([]*channel.ChannelMsgContent, error) { func (s *GuildService) pullChannelMessages(guildId, channelId, beginSeq, endSeq, eventVersion uint64, direct bool) ([]*channel.ChannelMsgContent, error) {
param := &channel.ChannelParam{ param := &channel.ChannelParam{
GuildId: &guildId, GuildId: proto.Some(guildId),
ChannelId: &channelId, ChannelId: proto.Some(channelId),
BeginSeq: &beginSeq, BeginSeq: proto.Some(beginSeq),
EndSeq: &endSeq, EndSeq: proto.Some(endSeq),
} }
if eventVersion != 0 { if eventVersion != 0 {
param.Version = []uint64{eventVersion} param.Version = []uint64{eventVersion}
@ -207,32 +151,33 @@ func (s *GuildService) pullChannelMessages(guildId, channelId, beginSeq, endSeq,
} }
payload, _ := proto.Marshal(&channel.ChannelMsgReq{ payload, _ := proto.Marshal(&channel.ChannelMsgReq{
ChannelParam: param, ChannelParam: param,
WithVersionFlag: &withVersionFlag, WithVersionFlag: proto.Some(withVersionFlag),
DirectMessageFlag: &directFlag, DirectMessageFlag: proto.Some(directFlag),
}) })
rsp, err := s.c.uniCall("trpc.group_pro.synclogic.SyncLogic.GetChannelMsg", payload) seq, packet := s.c.uniPacket("trpc.group_pro.synclogic.SyncLogic.GetChannelMsg", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "send packet error") return nil, errors.Wrap(err, "send packet error")
} }
msgRsp := new(channel.ChannelMsgRsp) msgRsp := new(channel.ChannelMsgRsp)
if err = proto.Unmarshal(rsp.Body, msgRsp); err != nil { if err = proto.Unmarshal(rsp, msgRsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
return msgRsp.ChannelMsg.Msgs, nil return msgRsp.ChannelMsg.Msgs, nil
} }
func (c *QQClient) buildGuildImageStorePacket(guildId, channelId uint64, hash []byte, size uint64) *network.Request { func (c *QQClient) buildGuildImageStorePacket(guildId, channelId uint64, hash []byte, size uint64) (uint16, []byte) {
payload, _ := proto.Marshal(&cmd0x388.D388ReqBody{ payload, _ := proto.Marshal(&cmd0x388.D388ReqBody{
NetType: proto.Uint32(3), NetType: proto.Uint32(3),
Subcmd: proto.Uint32(1), Subcmd: proto.Uint32(1),
TryupImgReq: []*cmd0x388.TryUpImgReq{ TryupImgReq: []*cmd0x388.TryUpImgReq{
{ {
GroupCode: &channelId, GroupCode: proto.Some(channelId),
SrcUin: proto.Uint64(uint64(c.Uin)), SrcUin: proto.Uint64(uint64(c.Uin)),
FileId: proto.Uint64(0), FileId: proto.Uint64(0),
FileMd5: hash, FileMd5: hash,
FileSize: &size, FileSize: proto.Some(size),
FileName: []byte(hex.EncodeToString(hash) + ".jpg"), FileName: []byte(fmt.Sprintf("%x.jpg", hash)),
SrcTerm: proto.Uint32(5), SrcTerm: proto.Uint32(5),
PlatformType: proto.Uint32(9), PlatformType: proto.Uint32(9),
BuType: proto.Uint32(211), BuType: proto.Uint32(211),
@ -240,20 +185,20 @@ func (c *QQClient) buildGuildImageStorePacket(guildId, channelId uint64, hash []
BuildVer: []byte("8.8.38.2266"), BuildVer: []byte("8.8.38.2266"),
AppPicType: proto.Uint32(1052), AppPicType: proto.Uint32(1052),
SrvUpload: proto.Uint32(0), SrvUpload: proto.Uint32(0),
QqmeetGuildId: &guildId, QqmeetGuildId: proto.Some(guildId),
QqmeetChannelId: &channelId, QqmeetChannelId: proto.Some(channelId),
}, },
}, },
CommandId: proto.Uint32(83), CommandId: proto.Uint32(83),
}) })
return c.uniRequest("ImgStore.QQMeetPicUp", payload, decodeGuildImageStoreResponse) return c.uniPacket("ImgStore.QQMeetPicUp", payload)
} }
func decodeGuildMessageEmojiReactions(content *channel.ChannelMsgContent) (r []*message.GuildMessageEmojiReaction) { func decodeGuildMessageEmojiReactions(content *channel.ChannelMsgContent) (r []*message.GuildMessageEmojiReaction) {
r = []*message.GuildMessageEmojiReaction{} r = []*message.GuildMessageEmojiReaction{}
var common *msg.CommonElem var common *msg.CommonElem
for _, elem := range content.Body.RichText.Elems { for _, elem := range content.Body.RichText.Elems {
if elem.CommonElem != nil && elem.CommonElem.GetServiceType() == 38 { if elem.CommonElem != nil && elem.CommonElem.ServiceType.Unwrap() == 38 {
common = elem.CommonElem common = elem.CommonElem
break break
} }
@ -271,12 +216,12 @@ func decodeGuildMessageEmojiReactions(content *channel.ChannelMsgContent) (r []*
} }
for _, e := range cnt.EmojiReaction { for _, e := range cnt.EmojiReaction {
reaction := &message.GuildMessageEmojiReaction{ reaction := &message.GuildMessageEmojiReaction{
EmojiId: e.GetEmojiId(), EmojiId: e.EmojiId.Unwrap(),
EmojiType: e.GetEmojiType(), EmojiType: e.EmojiType.Unwrap(),
Count: int32(e.GetCnt()), Count: int32(e.Cnt.Unwrap()),
Clicked: e.GetIsClicked(), Clicked: e.IsClicked.Unwrap(),
} }
if index, err := strconv.ParseInt(e.GetEmojiId(), 10, 32); err == nil { if index, err := strconv.ParseInt(e.EmojiId.Unwrap(), 10, 32); err == nil {
reaction.Face = message.NewFace(int32(index)) reaction.Face = message.NewFace(int32(index))
} }
r = append(r, reaction) r = append(r, reaction)
@ -285,127 +230,65 @@ func decodeGuildMessageEmojiReactions(content *channel.ChannelMsgContent) (r []*
return return
} }
func decodeGuildImageStoreResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeGuildImageStoreResponse(_ *QQClient, pkt *network.Packet) (any, error) {
body := new(cmd0x388.D388RspBody) body := new(cmd0x388.D388RspBody)
if err := proto.Unmarshal(resp.Body, body); err != nil { if err := proto.Unmarshal(pkt.Payload, body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if len(body.TryupImgRsp) == 0 { if len(body.TryupImgRsp) == 0 {
return nil, errors.New("response is empty") return nil, errors.New("response is empty")
} }
rsp := body.TryupImgRsp[0] rsp := body.TryupImgRsp[0]
if rsp.GetResult() != 0 { if rsp.Result.Unwrap() != 0 {
return &guildImageUploadResponse{ return &imageUploadResponse{
ResultCode: int32(rsp.GetResult()), ResultCode: int32(rsp.Result.Unwrap()),
Message: utils.B2S(rsp.GetFailMsg()), Message: string(rsp.FailMsg),
}, nil }, nil
} }
if rsp.GetFileExit() { if rsp.FileExit.Unwrap() {
if rsp.ImgInfo != nil { resp := &imageUploadResponse{
return &guildImageUploadResponse{IsExists: true, FileId: int64(rsp.GetFileid()), DownloadIndex: string(rsp.GetDownloadIndex()), Width: int32(rsp.ImgInfo.GetFileWidth()), Height: int32(rsp.ImgInfo.GetFileHeight())}, nil IsExists: true,
FileId: int64(rsp.Fileid.Unwrap()),
DownloadIndex: string(rsp.DownloadIndex),
} }
return &guildImageUploadResponse{IsExists: true, FileId: int64(rsp.GetFileid()), DownloadIndex: string(rsp.GetDownloadIndex())}, nil if rsp.ImgInfo != nil {
resp.Width = int32(rsp.ImgInfo.FileWidth.Unwrap())
resp.Height = int32(rsp.ImgInfo.FileHeight.Unwrap())
}
return resp, nil
} }
return &guildImageUploadResponse{ return &imageUploadResponse{
FileId: int64(rsp.GetFileid()), FileId: int64(rsp.Fileid.Unwrap()),
UploadKey: rsp.UpUkey, UploadKey: rsp.UpUkey,
UploadIp: rsp.GetUpIp(), UploadIp: rsp.UpIp,
UploadPort: rsp.GetUpPort(), UploadPort: rsp.UpPort,
DownloadIndex: string(rsp.GetDownloadIndex()), DownloadIndex: string(rsp.DownloadIndex),
}, nil }, nil
} }
func (s *GuildService) parseGuildChannelMessage(msg *channel.ChannelMsgContent) *message.GuildChannelMessage { func (s *GuildService) parseGuildChannelMessage(msg *channel.ChannelMsgContent) *message.GuildChannelMessage {
guild := s.FindGuild(msg.Head.RoutingHead.GetGuildId()) guild := s.FindGuild(msg.Head.RoutingHead.GuildId.Unwrap())
if guild == nil { if guild == nil {
return nil // todo: sync guild info return nil // todo: sync guild info
} }
if msg.Body == nil || msg.Body.RichText == nil { if msg.Body == nil || msg.Body.RichText == nil {
return nil return nil
} }
// mem := guild.FindMember(msg.Head.RoutingHead.GetFromTinyid()) // mem := guild.FindMember(msg.Head.RoutingHead.FromTinyid.Unwrap())
memberName := msg.ExtInfo.GetMemberName() memberName := msg.ExtInfo.MemberName
if memberName == nil { if memberName == nil {
memberName = msg.ExtInfo.GetFromNick() memberName = msg.ExtInfo.FromNick
} }
return &message.GuildChannelMessage{ return &message.GuildChannelMessage{
Id: msg.Head.ContentHead.GetSeq(), Id: msg.Head.ContentHead.Seq.Unwrap(),
InternalId: msg.Head.ContentHead.GetRandom(), InternalId: msg.Head.ContentHead.Random.Unwrap(),
GuildId: msg.Head.RoutingHead.GetGuildId(), GuildId: msg.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: msg.Head.RoutingHead.GetChannelId(), ChannelId: msg.Head.RoutingHead.ChannelId.Unwrap(),
Time: int64(msg.Head.ContentHead.GetTime()), Time: int64(msg.Head.ContentHead.Time.Unwrap()),
Sender: &message.GuildSender{ Sender: &message.GuildSender{
TinyId: msg.Head.RoutingHead.GetFromTinyid(), TinyId: msg.Head.RoutingHead.FromTinyid.Unwrap(),
Nickname: string(memberName), Nickname: string(memberName),
}, },
Elements: message.ParseMessageElems(msg.Body.RichText.Elems), Elements: message.ParseMessageElems(msg.Body.RichText.Elems),
} }
} }
// PttCenterSvr.GroupShortVideoUpReq
func (c *QQClient) buildPttGuildVideoUpReq(videoHash, thumbHash []byte, guildId, channelId int64, videoSize, thumbSize int64) *network.Request {
pb := c.buildPttGroupShortVideoProto(videoHash, thumbHash, guildId, videoSize, thumbSize, 4)
pb.PttShortVideoUploadReq.BusinessType = 4601
pb.PttShortVideoUploadReq.ToUin = channelId
pb.ExtensionReq[0].SubBusiType = 4601
payload, _ := proto.Marshal(pb)
return c.uniRequest("PttCenterSvr.GroupShortVideoUpReq", payload, decodeGroupShortVideoUploadResponse)
}
func (c *QQClient) UploadGuildShortVideo(guildId, channelId uint64, video, thumb io.ReadSeeker) (*message.ShortVideoElement, error) {
// todo: combine with group short video upload
videoHash, videoLen := utils.ComputeMd5AndLength(video)
thumbHash, thumbLen := utils.ComputeMd5AndLength(thumb)
key := string(videoHash) + string(thumbHash)
pttWaiter.Wait(key)
defer pttWaiter.Done(key)
i, err := c.callAndDecode(c.buildPttGuildVideoUpReq(videoHash, thumbHash, int64(guildId), int64(channelId), videoLen, thumbLen))
if err != nil {
return nil, errors.Wrap(err, "upload req error")
}
rsp := i.(*pttcenter.ShortVideoUploadRsp)
if rsp.FileExists == 1 {
return &message.ShortVideoElement{
Uuid: []byte(rsp.FileId),
Size: int32(videoLen),
ThumbSize: int32(thumbLen),
Md5: videoHash,
ThumbMd5: thumbHash,
Guild: true,
}, nil
}
req := c.buildPttGroupShortVideoProto(videoHash, thumbHash, int64(guildId), videoLen, thumbLen, 4).PttShortVideoUploadReq
req.BusinessType = 4601
req.ToUin = int64(channelId)
ext, _ := proto.Marshal(req)
multi := utils.MultiReadSeeker(thumb, video)
hwRsp, err := c.highwaySession.UploadBDH(highway.BdhInput{
CommandID: 89,
Body: multi,
Ticket: c.highwaySession.SigSession,
Ext: ext,
Encrypt: true,
})
if err != nil {
return nil, errors.Wrap(err, "upload video file error")
}
if len(hwRsp) == 0 {
return nil, errors.New("resp is empty")
}
rsp = &pttcenter.ShortVideoUploadRsp{}
if err = proto.Unmarshal(hwRsp, rsp); err != nil {
return nil, errors.Wrap(err, "decode error")
}
return &message.ShortVideoElement{
Uuid: []byte(rsp.FileId),
Size: int32(videoLen),
ThumbSize: int32(thumbLen),
Md5: videoHash,
ThumbMd5: thumbHash,
Guild: true,
}, nil
}

View File

@ -5,13 +5,12 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"html" "html"
"io"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"net/textproto" "net/textproto"
"net/url" "net/url"
"regexp"
"strconv" "strconv"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -21,51 +20,6 @@ import (
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
/* -------- VipInfo -------- */
type VipInfo struct {
Uin int64
Name string
Level int
LevelSpeed float64
VipLevel string
VipGrowthSpeed int
VipGrowthTotal int
}
func (c *QQClient) GetVipInfo(target int64) (*VipInfo, error) {
b, err := utils.HttpGetBytes(fmt.Sprintf("https://h5.vip.qq.com/p/mc/cardv2/other?platform=1&qq=%d&adtag=geren&aid=mvip.pingtai.mobileqq.androidziliaoka.fromqita", target), c.getCookiesWithDomain("h5.vip.qq.com"))
if err != nil {
return nil, err
}
ret := VipInfo{Uin: target}
b = b[bytes.Index(b, []byte(`<span class="ui-nowrap">`))+24:]
t := b[:bytes.Index(b, []byte(`</span>`))]
ret.Name = string(t)
b = b[bytes.Index(b, []byte(`<small>LV</small>`))+17:]
t = b[:bytes.Index(b, []byte(`</p>`))]
ret.Level, _ = strconv.Atoi(string(t))
b = b[bytes.Index(b, []byte(`<div class="pk-line pk-line-guest">`))+35:]
b = b[bytes.Index(b, []byte(`<p>`))+3:]
t = b[:bytes.Index(b, []byte(`<small>倍`))]
ret.LevelSpeed, _ = strconv.ParseFloat(string(t), 64)
b = b[bytes.Index(b, []byte(`<div class="pk-line pk-line-guest">`))+35:]
b = b[bytes.Index(b, []byte(`<p>`))+3:]
st := string(b[:bytes.Index(b, []byte(`</p>`))])
st = strings.Replace(st, "<small>", "", 1)
st = strings.Replace(st, "</small>", "", 1)
ret.VipLevel = st
b = b[bytes.Index(b, []byte(`<div class="pk-line pk-line-guest">`))+35:]
b = b[bytes.Index(b, []byte(`<p>`))+3:]
t = b[:bytes.Index(b, []byte(`</p>`))]
ret.VipGrowthSpeed, _ = strconv.Atoi(string(t))
b = b[bytes.Index(b, []byte(`<div class="pk-line pk-line-guest">`))+35:]
b = b[bytes.Index(b, []byte(`<p>`))+3:]
t = b[:bytes.Index(b, []byte(`</p>`))]
ret.VipGrowthTotal, _ = strconv.Atoi(string(t))
return &ret, nil
}
/* -------- GroupHonorInfo -------- */ /* -------- GroupHonorInfo -------- */
type ( type (
@ -106,15 +60,20 @@ const (
Emotion HonorType = 6 // 快乐源泉 Emotion HonorType = 6 // 快乐源泉
) )
// 匹配 window.__INITIAL_STATE__ = 后的内容
var honorRe = regexp.MustCompile(`window\.__INITIAL_STATE__\s*?=\s*?(\{.*\})`)
func (c *QQClient) GetGroupHonorInfo(groupCode int64, honorType HonorType) (*GroupHonorInfo, error) { func (c *QQClient) GetGroupHonorInfo(groupCode int64, honorType HonorType) (*GroupHonorInfo, error) {
b, err := utils.HttpGetBytes(fmt.Sprintf("https://qun.qq.com/interactive/honorlist?gc=%d&type=%d", groupCode, honorType), c.getCookiesWithDomain("qun.qq.com")) b, err := utils.HttpGetBytes(fmt.Sprintf("https://qun.qq.com/interactive/honorlist?gc=%d&type=%d", groupCode, honorType), c.getCookiesWithDomain("qun.qq.com"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
b = b[bytes.Index(b, []byte(`window.__INITIAL_STATE__=`))+25:] matched := honorRe.FindSubmatch(b)
b = b[:bytes.Index(b, []byte("</script>"))] if len(matched) == 0 {
return nil, errors.New("无匹配结果")
}
ret := GroupHonorInfo{} ret := GroupHonorInfo{}
err = json.Unmarshal(b, &ret) err = json.NewDecoder(bytes.NewReader(matched[1])).Decode(&ret)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -165,6 +124,37 @@ func (c *QQClient) GetTts(text string) ([]byte, error) {
/* -------- GroupNotice -------- */ /* -------- GroupNotice -------- */
type groupNoticeRsp struct {
Feeds []*GroupNoticeFeed `json:"feeds"`
Inst []*GroupNoticeFeed `json:"inst"`
}
type GroupNoticeFeed struct {
NoticeId string `json:"fid"`
SenderId uint32 `json:"u"`
PublishTime uint64 `json:"pubt"`
Message struct {
Text string `json:"text"`
Images []noticeImage `json:"pics"`
} `json:"msg"`
}
type GroupNoticeMessage struct {
NoticeId string `json:"notice_id"`
SenderId uint32 `json:"sender_id"`
PublishTime uint64 `json:"publish_time"`
Message struct {
Text string `json:"text"`
Images []GroupNoticeImage `json:"images"`
} `json:"message"`
}
type GroupNoticeImage struct {
Height string `json:"height"`
Width string `json:"width"`
ID string `json:"id"`
}
type noticePicUpResponse struct { type noticePicUpResponse struct {
ErrorCode int `json:"ec"` ErrorCode int `json:"ec"`
ErrorMessage string `json:"em"` ErrorMessage string `json:"em"`
@ -177,37 +167,87 @@ type noticeImage struct {
ID string `json:"id"` ID string `json:"id"`
} }
type noticeSendResp struct {
NoticeId string `json:"new_fid"`
}
func (c *QQClient) GetGroupNotice(groupCode int64) (l []*GroupNoticeMessage, err error) {
v := url.Values{}
v.Set("bkn", strconv.Itoa(c.getCSRFToken()))
v.Set("qid", strconv.FormatInt(groupCode, 10))
v.Set("ft", "23")
v.Set("ni", "1")
v.Set("n", "1")
v.Set("i", "1")
v.Set("log_read", "1")
v.Set("platform", "1")
v.Set("s", "-1")
v.Set("n", "20")
req, _ := http.NewRequest(http.MethodGet, "https://web.qun.qq.com/cgi-bin/announce/get_t_list?"+v.Encode(), nil)
req.Header.Set("Cookie", c.getCookies())
rsp, err := utils.Client.Do(req)
if err != nil {
return
}
defer rsp.Body.Close()
r := groupNoticeRsp{}
err = json.NewDecoder(rsp.Body).Decode(&r)
if err != nil {
return
}
return c.parseGroupNoticeJson(&r), nil
}
func (c *QQClient) parseGroupNoticeJson(s *groupNoticeRsp) []*GroupNoticeMessage {
o := make([]*GroupNoticeMessage, 0, len(s.Feeds)+len(s.Inst))
parse := func(v *GroupNoticeFeed) {
ims := make([]GroupNoticeImage, 0, len(v.Message.Images))
for i := 0; i < len(v.Message.Images); i++ {
ims = append(ims, GroupNoticeImage{
Height: v.Message.Images[i].Height,
Width: v.Message.Images[i].Width,
ID: v.Message.Images[i].ID,
})
}
o = append(o, &GroupNoticeMessage{
NoticeId: v.NoticeId,
SenderId: v.SenderId,
PublishTime: v.PublishTime,
Message: struct {
Text string `json:"text"`
Images []GroupNoticeImage `json:"images"`
}{
Text: v.Message.Text,
Images: ims,
},
})
}
for _, v := range s.Feeds {
parse(v)
}
for _, v := range s.Inst {
parse(v)
}
return o
}
func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) { func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
w := multipart.NewWriter(buf) w := multipart.NewWriter(buf)
err := w.WriteField("bkn", strconv.Itoa(c.getCSRFToken())) _ = w.WriteField("bkn", strconv.Itoa(c.getCSRFToken()))
if err != nil { _ = w.WriteField("source", "troopNotice")
return nil, errors.Wrap(err, "write multipart<bkn> failed") _ = w.WriteField("m", "0")
}
err = w.WriteField("source", "troopNotice")
if err != nil {
return nil, errors.Wrap(err, "write multipart<source> failed")
}
err = w.WriteField("m", "0")
if err != nil {
return nil, errors.Wrap(err, "write multipart<m> failed")
}
h := make(textproto.MIMEHeader) h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", `form-data; name="pic_up"; filename="temp_uploadFile.png"`) h.Set("Content-Disposition", `form-data; name="pic_up"; filename="temp_uploadFile.png"`)
h.Set("Content-Type", "image/png") h.Set("Content-Type", "image/png")
fw, err := w.CreatePart(h) fw, _ := w.CreatePart(h)
if err != nil { _, _ = fw.Write(img)
return nil, errors.Wrap(err, "create multipart field<pic_up> failed") _ = w.Close()
} req, err := http.NewRequest(http.MethodPost, "https://web.qun.qq.com/cgi-bin/announce/upload_img", buf)
_, err = fw.Write(img)
if err != nil {
return nil, errors.Wrap(err, "write multipart<pic_up> failed")
}
err = w.Close()
if err != nil {
return nil, errors.Wrap(err, "close multipart failed")
}
req, err := http.NewRequest("POST", "https://web.qun.qq.com/cgi-bin/announce/upload_img", buf)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "new request error") return nil, errors.Wrap(err, "new request error")
} }
@ -218,12 +258,8 @@ func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) {
return nil, errors.Wrap(err, "post error") return nil, errors.Wrap(err, "post error")
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) var res noticePicUpResponse
if err != nil { err = json.NewDecoder(resp.Body).Decode(&res)
return nil, errors.Wrap(err, "read body error")
}
res := noticePicUpResponse{}
err = json.Unmarshal(body, &res)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal json") return nil, errors.Wrap(err, "failed to unmarshal json")
} }
@ -239,23 +275,42 @@ func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) {
} }
// AddGroupNoticeSimple 发群公告 // AddGroupNoticeSimple 发群公告
func (c *QQClient) AddGroupNoticeSimple(groupCode int64, text string) error { func (c *QQClient) AddGroupNoticeSimple(groupCode int64, text string) (noticeId string, err error) {
body := fmt.Sprintf(`qid=%v&bkn=%v&text=%v&pinned=0&type=1&settings={"is_show_edit_card":0,"tip_window_type":1,"confirm_required":1}`, groupCode, c.getCSRFToken(), url.QueryEscape(text)) body := fmt.Sprintf(`qid=%v&bkn=%v&text=%v&pinned=0&type=1&settings={"is_show_edit_card":0,"tip_window_type":1,"confirm_required":1}`, groupCode, c.getCSRFToken(), url.QueryEscape(text))
_, err := utils.HttpPostBytesWithCookie("https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn="+fmt.Sprint(c.getCSRFToken()), []byte(body), c.getCookiesWithDomain("qun.qq.com")) resp, err := utils.HttpPostBytesWithCookie("https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn="+fmt.Sprint(c.getCSRFToken()), []byte(body), c.getCookiesWithDomain("qun.qq.com"))
if err != nil { if err != nil {
return errors.Wrap(err, "request error") return "", errors.Wrap(err, "request error")
} }
return nil var res noticeSendResp
err = json.Unmarshal(resp, &res)
if err != nil {
return "", errors.Wrap(err, "json unmarshal error")
}
return res.NoticeId, nil
} }
// AddGroupNoticeWithPic 发群公告带图片 // AddGroupNoticeWithPic 发群公告带图片
func (c *QQClient) AddGroupNoticeWithPic(groupCode int64, text string, pic []byte) error { func (c *QQClient) AddGroupNoticeWithPic(groupCode int64, text string, pic []byte) (noticeId string, err error) {
img, err := c.uploadGroupNoticePic(pic) img, err := c.uploadGroupNoticePic(pic)
if err != nil { if err != nil {
return err return "", err
} }
body := fmt.Sprintf(`qid=%v&bkn=%v&text=%v&pinned=0&type=1&settings={"is_show_edit_card":0,"tip_window_type":1,"confirm_required":1}&pic=%v&imgWidth=%v&imgHeight=%v`, groupCode, c.getCSRFToken(), url.QueryEscape(text), img.ID, img.Width, img.Height) body := fmt.Sprintf(`qid=%v&bkn=%v&text=%v&pinned=0&type=1&settings={"is_show_edit_card":0,"tip_window_type":1,"confirm_required":1}&pic=%v&imgWidth=%v&imgHeight=%v`, groupCode, c.getCSRFToken(), url.QueryEscape(text), img.ID, img.Width, img.Height)
_, err = utils.HttpPostBytesWithCookie("https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn="+fmt.Sprint(c.getCSRFToken()), []byte(body), c.getCookiesWithDomain("qun.qq.com")) resp, err := utils.HttpPostBytesWithCookie("https://web.qun.qq.com/cgi-bin/announce/add_qun_notice?bkn="+fmt.Sprint(c.getCSRFToken()), []byte(body), c.getCookiesWithDomain("qun.qq.com"))
if err != nil {
return "", errors.Wrap(err, "request error")
}
var res noticeSendResp
err = json.Unmarshal(resp, &res)
if err != nil {
return "", errors.Wrap(err, "json unmarshal error")
}
return res.NoticeId, nil
}
func (c *QQClient) DelGroupNotice(groupCode int64, fid string) error {
body := fmt.Sprintf(`fid=%s&qid=%v&bkn=%v&ft=23&op=1`, fid, groupCode, c.getCSRFToken())
_, err := utils.HttpPostBytesWithCookie("https://web.qun.qq.com/cgi-bin/announce/del_feed", []byte(body), c.getCookiesWithDomain("qun.qq.com"))
if err != nil { if err != nil {
return errors.Wrap(err, "request error") return errors.Wrap(err, "request error")
} }

View File

@ -1,11 +1,9 @@
package client package client
import ( import (
"bytes" "crypto/rand"
"encoding/hex" "fmt"
"io" "io"
"math/rand"
"os"
"strings" "strings"
"time" "time"
@ -23,22 +21,40 @@ import (
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
func init() {
decoders["ImgStore.GroupPicUp"] = decodeGroupImageStoreResponse
decoders["ImgStore.GroupPicDown"] = decodeGroupImageDownloadResponse
decoders["OidbSvc.0xe07_0"] = decodeImageOcrResponse
}
var imgWaiter = utils.NewUploadWaiter() var imgWaiter = utils.NewUploadWaiter()
type imageUploadResponse struct { type imageUploadResponse struct {
UploadKey []byte UploadKey []byte
UploadIp []uint32 UploadIp []uint32
UploadPort []uint32 UploadPort []uint32
ResourceId string Width int32
Message string Height int32
FileId int64 Message string
Width int32 DownloadIndex string
Height int32 ResourceId string
ResultCode int32 FileId int64
IsExists bool ResultCode int32
IsExists bool
} }
func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*message.GroupImageElement, error) { func (c *QQClient) UploadImage(target message.Source, img io.ReadSeeker) (message.IMessageElement, error) {
switch target.SourceType {
case message.SourceGroup, message.SourceGuildChannel, message.SourceGuildDirect:
return c.uploadGroupOrGuildImage(target, img)
case message.SourcePrivate:
return c.uploadPrivateImage(target.PrimaryID, img, 0)
default:
return nil, errors.New("unsupported target type")
}
}
func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSeeker) (message.IMessageElement, error) {
_, _ = img.Seek(0, io.SeekStart) // safe _, _ = img.Seek(0, io.SeekStart) // safe
fh, length := utils.ComputeMd5AndLength(img) fh, length := utils.ComputeMd5AndLength(img)
_, _ = img.Seek(0, io.SeekStart) _, _ = img.Seek(0, io.SeekStart)
@ -47,8 +63,27 @@ func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*messag
imgWaiter.Wait(key) imgWaiter.Wait(key)
defer imgWaiter.Done(key) defer imgWaiter.Done(key)
req := c.buildGroupImageStoreRequest(groupCode, fh, int32(length)) cmd := int32(2)
r, err := c.callAndDecode(req) ext := EmptyBytes
if target.SourceType != message.SourceGroup { // guild
cmd = 83
ext = proto.DynamicMessage{
11: target.PrimaryID,
12: target.SecondaryID,
}.Encode()
}
var r any
var err error
var input highway.Transaction
switch target.SourceType {
case message.SourceGroup:
r, err = c.sendAndWait(c.buildGroupImageStorePacket(target.PrimaryID, fh, int32(length)))
case message.SourceGuildChannel, message.SourceGuildDirect:
r, err = c.sendAndWait(c.buildGuildImageStorePacket(uint64(target.PrimaryID), uint64(target.SecondaryID), fh, uint64(length)))
default:
return nil, errors.Errorf("unsupported target type %v", target.SourceType)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -64,16 +99,19 @@ func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*messag
c.highwaySession.AppendAddr(addr, rsp.UploadPort[i]) c.highwaySession.AppendAddr(addr, rsp.UploadPort[i])
} }
} }
if _, err = c.highwaySession.UploadBDH(highway.BdhInput{
CommandID: 2, input = highway.Transaction{
CommandID: cmd,
Body: img, Body: img,
Size: length,
Sum: fh,
Ticket: rsp.UploadKey, Ticket: rsp.UploadKey,
Ext: EmptyBytes, Ext: ext,
Encrypt: false, }
}); err == nil { _, err = c.highwaySession.Upload(input)
goto ok if err != nil {
return nil, errors.Wrap(err, "upload failed")
} }
return nil, errors.Wrap(err, "upload failed")
ok: ok:
_, _ = img.Seek(0, io.SeekStart) _, _ = img.Seek(0, io.SeekStart)
i, t, _ := imgsz.DecodeSize(img) i, t, _ := imgsz.DecodeSize(img)
@ -81,80 +119,57 @@ ok:
if t == "gif" { if t == "gif" {
imageType = 2000 imageType = 2000
} }
return message.NewGroupImage(binary.CalculateImageResourceId(fh), fh, rsp.FileId, int32(length), int32(i.Width), int32(i.Height), imageType), nil width := int32(i.Width)
} height := int32(i.Height)
if err != nil && target.SourceType != message.SourceGroup {
func (c *QQClient) UploadGroupImageByFile(groupCode int64, path string) (*message.GroupImageElement, error) { c.warning("warning: decode image error: %v. this image will be displayed by wrong size in pc guild client", err)
img, err := os.OpenFile(path, os.O_RDONLY, 0o666) width = 200
if err != nil { height = 200
return nil, err
} }
defer func() { _ = img.Close() }() if target.SourceType == message.SourceGroup {
fh, length := utils.ComputeMd5AndLength(img) return message.NewGroupImage(
binary.CalculateImageResourceId(fh),
key := hex.EncodeToString(fh) fh, rsp.FileId, int32(length),
imgWaiter.Wait(key) int32(i.Width), int32(i.Height), imageType,
defer imgWaiter.Done(key) ), nil
req := c.buildGroupImageStoreRequest(groupCode, fh, int32(length))
r, err := c.callAndDecode(req)
if err != nil {
return nil, err
} }
rsp := r.(*imageUploadResponse) return &message.GuildImageElement{
if rsp.ResultCode != 0 { FileId: rsp.FileId,
return nil, errors.New(rsp.Message) FilePath: fmt.Sprintf("%x.jpg", fh),
} Size: int32(length),
if rsp.IsExists { DownloadIndex: rsp.DownloadIndex,
goto ok Width: width,
} Height: height,
if c.highwaySession.AddrLength() == 0 { ImageType: imageType,
for i, addr := range rsp.UploadIp { Md5: fh,
c.highwaySession.AppendAddr(addr, rsp.UploadPort[i]) }, nil
}
}
if _, err = c.highwaySession.UploadBDHMultiThread(highway.BdhInput{
CommandID: 2,
File: path,
Ticket: rsp.UploadKey,
Ext: EmptyBytes,
Encrypt: false,
}, 1); err == nil {
goto ok
}
return nil, errors.Wrap(err, "upload failed")
ok:
_, _ = img.Seek(0, io.SeekStart)
i, t, _ := imgsz.DecodeSize(img)
var imageType int32 = 1000
if t == "gif" {
imageType = 2000
}
return message.NewGroupImage(binary.CalculateImageResourceId(fh), fh, rsp.FileId, int32(length), int32(i.Width), int32(i.Height), imageType), nil
}
func (c *QQClient) UploadPrivateImage(target int64, img io.ReadSeeker) (*message.FriendImageElement, error) {
return c.uploadPrivateImage(target, img, 0)
} }
func (c *QQClient) GetGroupImageDownloadUrl(fileId, groupCode int64, fileMd5 []byte) (string, error) { func (c *QQClient) GetGroupImageDownloadUrl(fileId, groupCode int64, fileMd5 []byte) (string, error) {
i, err := c.callAndDecode(c.buildGroupImageDownloadRequest(fileId, groupCode, fileMd5)) i, err := c.sendAndWait(c.buildGroupImageDownloadPacket(fileId, groupCode, fileMd5))
if err != nil { if err != nil {
return "", err return "", err
} }
return i.(string), nil return i.(string), nil
} }
func (c *QQClient) uploadPrivateImage(target int64, img io.ReadSeeker, count int) (*message.FriendImageElement, error) { func (c *QQClient) uploadPrivateImage(target int64, img io.ReadSeeker, count int) (message.IMessageElement, error) {
_, _ = img.Seek(0, io.SeekStart) _, _ = img.Seek(0, io.SeekStart)
count++ count++
fh, length := utils.ComputeMd5AndLength(img) fh, length := utils.ComputeMd5AndLength(img)
_, _ = img.Seek(0, io.SeekStart) _, _ = img.Seek(0, io.SeekStart)
i, _, _ := imgsz.DecodeSize(img)
_, _ = img.Seek(0, io.SeekStart)
width := int32(i.Width)
height := int32(i.Height)
e, err := c.QueryFriendImage(target, fh, int32(length)) e, err := c.QueryFriendImage(target, fh, int32(length))
if errors.Is(err, ErrNotExists) { if errors.Is(err, ErrNotExists) {
groupSource := message.Source{
SourceType: message.SourceGroup,
PrimaryID: target,
}
// use group highway upload and query again for image id. // use group highway upload and query again for image id.
if _, err = c.UploadGroupImage(target, img); err != nil { if _, err = c.uploadGroupOrGuildImage(groupSource, img); err != nil {
return nil, err return nil, err
} }
if count >= 5 { if count >= 5 {
@ -165,22 +180,23 @@ func (c *QQClient) uploadPrivateImage(target int64, img io.ReadSeeker, count int
if err != nil { if err != nil {
return nil, err return nil, err
} }
e.Height = height
e.Width = width
return e, nil return e, nil
} }
func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) { func (c *QQClient) ImageOcr(img any) (*OcrResponse, error) {
url := "" url := ""
switch e := img.(type) { switch e := img.(type) {
case *message.GroupImageElement: case *message.GroupImageElement:
url = e.Url url = e.Url
if b, err := utils.HTTPGetReadCloser(e.Url, ""); err == nil { if b, err := utils.HTTPGetReadCloser(e.Url, ""); err == nil {
if url, err = c.uploadOcrImage(b); err != nil { if url, err = c.uploadOcrImage(b, e.Size, e.Md5); err != nil {
url = e.Url url = e.Url
} }
_ = b.Close() _ = b.Close()
} }
call := c.buildImageOcrRequestPacket(url, strings.ToUpper(hex.EncodeToString(e.Md5)), e.Size, e.Width, e.Height) rsp, err := c.sendAndWait(c.buildImageOcrRequestPacket(url, fmt.Sprintf("%X", e.Md5), e.Size, e.Width, e.Height))
rsp, err := c.callAndDecode(call)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -190,7 +206,7 @@ func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) {
} }
func (c *QQClient) QueryGroupImage(groupCode int64, hash []byte, size int32) (*message.GroupImageElement, error) { func (c *QQClient) QueryGroupImage(groupCode int64, hash []byte, size int32) (*message.GroupImageElement, error) {
r, err := c.callAndDecode(c.buildGroupImageStoreRequest(groupCode, hash, size)) r, err := c.sendAndWait(c.buildGroupImageStorePacket(groupCode, hash, size))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -205,7 +221,7 @@ func (c *QQClient) QueryGroupImage(groupCode int64, hash []byte, size int32) (*m
} }
func (c *QQClient) QueryFriendImage(target int64, hash []byte, size int32) (*message.FriendImageElement, error) { func (c *QQClient) QueryFriendImage(target int64, hash []byte, size int32) (*message.FriendImageElement, error) {
i, err := c.callAndDecode(c.buildOffPicUpRequest(target, hash, size)) i, err := c.sendAndWait(c.buildOffPicUpPacket(target, hash, size))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -217,6 +233,7 @@ func (c *QQClient) QueryFriendImage(target int64, hash []byte, size int32) (*mes
return &message.FriendImageElement{ return &message.FriendImageElement{
ImageId: rsp.ResourceId, ImageId: rsp.ResourceId,
Md5: hash, Md5: hash,
Size: size,
Url: "https://c2cpicdw.qpic.cn/offpic_new/0/" + rsp.ResourceId + "/0?term=2", Url: "https://c2cpicdw.qpic.cn/offpic_new/0/" + rsp.ResourceId + "/0?term=2",
}, errors.WithStack(ErrNotExists) }, errors.WithStack(ErrNotExists)
} }
@ -224,11 +241,14 @@ func (c *QQClient) QueryFriendImage(target int64, hash []byte, size int32) (*mes
ImageId: rsp.ResourceId, ImageId: rsp.ResourceId,
Md5: hash, Md5: hash,
Url: "https://c2cpicdw.qpic.cn/offpic_new/0/" + rsp.ResourceId + "/0?term=2", Url: "https://c2cpicdw.qpic.cn/offpic_new/0/" + rsp.ResourceId + "/0?term=2",
Size: size,
Height: rsp.Height,
Width: rsp.Width,
}, nil }, nil
} }
// ImgStore.GroupPicUp // ImgStore.GroupPicUp
func (c *QQClient) buildGroupImageStoreRequest(groupCode int64, md5 []byte, size int32) *network.Request { func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 []byte, size int32) (uint16, []byte) {
name := utils.RandomString(16) + ".gif" name := utils.RandomString(16) + ".gif"
req := &cmd0x388.D388ReqBody{ req := &cmd0x388.D388ReqBody{
NetType: proto.Uint32(3), NetType: proto.Uint32(3),
@ -253,10 +273,10 @@ func (c *QQClient) buildGroupImageStoreRequest(groupCode int64, md5 []byte, size
Extension: EmptyBytes, Extension: EmptyBytes,
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
return c.uniRequest("ImgStore.GroupPicUp", payload, decodeGroupImageStoreResponse) return c.uniPacket("ImgStore.GroupPicUp", payload)
} }
func (c *QQClient) buildGroupImageDownloadRequest(fileId, groupCode int64, fileMd5 []byte) *network.Request { func (c *QQClient) buildGroupImageDownloadPacket(fileId, groupCode int64, fileMd5 []byte) (uint16, []byte) {
req := &cmd0x388.D388ReqBody{ req := &cmd0x388.D388ReqBody{
NetType: proto.Uint32(3), NetType: proto.Uint32(3),
Subcmd: proto.Uint32(2), Subcmd: proto.Uint32(2),
@ -277,10 +297,10 @@ func (c *QQClient) buildGroupImageDownloadRequest(fileId, groupCode int64, fileM
}, },
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
return c.uniRequest("ImgStore.GroupPicDown", payload, decodeGroupImageDownloadResponse) return c.uniPacket("ImgStore.GroupPicDown", payload)
} }
func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) { func (c *QQClient) uploadOcrImage(img io.Reader, size int32, sum []byte) (string, error) {
r := make([]byte, 16) r := make([]byte, 16)
rand.Read(r) rand.Read(r)
ext, _ := proto.Marshal(&highway2.CommFileExtReq{ ext, _ := proto.Marshal(&highway2.CommFileExtReq{
@ -288,13 +308,13 @@ func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) {
Uuid: binary.GenUUID(r), Uuid: binary.GenUUID(r),
}) })
buf, _ := io.ReadAll(img) rsp, err := c.highwaySession.Upload(highway.Transaction{
rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{
CommandID: 76, CommandID: 76,
Body: bytes.NewReader(buf), Body: img,
Size: int64(size),
Sum: sum,
Ticket: c.highwaySession.SigSession, Ticket: c.highwaySession.SigSession,
Ext: ext, Ext: ext,
Encrypt: false,
}) })
if err != nil { if err != nil {
return "", errors.Wrap(err, "upload ocr image error") return "", errors.Wrap(err, "upload ocr image error")
@ -303,11 +323,11 @@ func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) {
if err = proto.Unmarshal(rsp, &rspExt); err != nil { if err = proto.Unmarshal(rsp, &rspExt); err != nil {
return "", errors.Wrap(err, "error unmarshal highway resp") return "", errors.Wrap(err, "error unmarshal highway resp")
} }
return string(rspExt.GetDownloadUrl()), nil return string(rspExt.DownloadUrl), nil
} }
// OidbSvc.0xe07_0 // OidbSvc.0xe07_0
func (c *QQClient) buildImageOcrRequestPacket(url, md5 string, size, weight, height int32) *network.Request { func (c *QQClient) buildImageOcrRequestPacket(url, md5 string, size, weight, height int32) (uint16, []byte) {
body := &oidb.DE07ReqBody{ body := &oidb.DE07ReqBody{
Version: 1, Version: 1,
Entrance: 3, Entrance: 3,
@ -323,60 +343,57 @@ func (c *QQClient) buildImageOcrRequestPacket(url, md5 string, size, weight, hei
} }
b, _ := proto.Marshal(body) b, _ := proto.Marshal(body)
payload := c.packOIDBPackage(3591, 0, b) payload := c.packOIDBPackage(3591, 0, b)
return c.uniRequest("OidbSvc.0xe07_0", payload, decodeImageOcrResponse) return c.uniPacket("OidbSvc.0xe07_0", payload)
} }
// ImgStore.GroupPicUp // ImgStore.GroupPicUp
func decodeGroupImageStoreResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeGroupImageStoreResponse(_ *QQClient, packet *network.Packet) (any, error) {
pkt := cmd0x388.D388RspBody{} pkt := cmd0x388.D388RspBody{}
err := proto.Unmarshal(resp.Body, &pkt) err := proto.Unmarshal(packet.Payload, &pkt)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
rsp := pkt.TryupImgRsp[0] rsp := pkt.TryupImgRsp[0]
if rsp.GetResult() != 0 { if rsp.Result.Unwrap() != 0 {
return &imageUploadResponse{ return &imageUploadResponse{
ResultCode: int32(rsp.GetResult()), ResultCode: int32(rsp.Result.Unwrap()),
Message: utils.B2S(rsp.GetFailMsg()), Message: utils.B2S(rsp.FailMsg),
}, nil }, nil
} }
if rsp.GetFileExit() { if rsp.FileExit.Unwrap() {
if rsp.GetImgInfo() != nil { if rsp.ImgInfo != nil {
return &imageUploadResponse{IsExists: true, FileId: int64(rsp.GetFileid()), Width: int32(rsp.ImgInfo.GetFileWidth()), Height: int32(rsp.ImgInfo.GetFileHeight())}, nil return &imageUploadResponse{IsExists: true, FileId: int64(rsp.Fileid.Unwrap()), Width: int32(rsp.ImgInfo.FileWidth.Unwrap()), Height: int32(rsp.ImgInfo.FileHeight.Unwrap())}, nil
} }
return &imageUploadResponse{IsExists: true, FileId: int64(rsp.GetFileid())}, nil return &imageUploadResponse{IsExists: true, FileId: int64(rsp.Fileid.Unwrap())}, nil
} }
return &imageUploadResponse{ return &imageUploadResponse{
FileId: int64(rsp.GetFileid()), FileId: int64(rsp.Fileid.Unwrap()),
UploadKey: rsp.UpUkey, UploadKey: rsp.UpUkey,
UploadIp: rsp.GetUpIp(), UploadIp: rsp.UpIp,
UploadPort: rsp.GetUpPort(), UploadPort: rsp.UpPort,
}, nil }, nil
} }
func decodeGroupImageDownloadResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeGroupImageDownloadResponse(_ *QQClient, pkt *network.Packet) (any, error) {
pkt := cmd0x388.D388RspBody{} rsp := cmd0x388.D388RspBody{}
if err := proto.Unmarshal(resp.Body, &pkt); err != nil { if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
return nil, errors.Wrap(err, "unmarshal protobuf message error") return nil, errors.Wrap(err, "unmarshal protobuf message error")
} }
if len(pkt.GetimgUrlRsp) == 0 { if len(rsp.GetimgUrlRsp) == 0 {
return nil, errors.New("response not found") return nil, errors.New("response not found")
} }
if len(pkt.GetimgUrlRsp[0].FailMsg) != 0 { if len(rsp.GetimgUrlRsp[0].FailMsg) != 0 {
return nil, errors.New(utils.B2S(pkt.GetimgUrlRsp[0].FailMsg)) return nil, errors.New(utils.B2S(rsp.GetimgUrlRsp[0].FailMsg))
} }
return "https://" + utils.B2S(pkt.GetimgUrlRsp[0].DownDomain) + utils.B2S(pkt.GetimgUrlRsp[0].BigDownPara), nil return fmt.Sprintf("https://%s%s", rsp.GetimgUrlRsp[0].DownDomain, rsp.GetimgUrlRsp[0].BigDownPara), nil
} }
// OidbSvc.0xe07_0 // OidbSvc.0xe07_0
func decodeImageOcrResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeImageOcrResponse(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.DE07RspBody{} rsp := oidb.DE07RspBody{}
if err := proto.Unmarshal(resp.Body, &pkg); err != nil { err := unpackOIDBPackage(pkt.Payload, &rsp)
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") if err != nil {
} return nil, err
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if rsp.Wording != "" { if rsp.Wording != "" {
if strings.Contains(rsp.Wording, "服务忙") { if strings.Contains(rsp.Wording, "服务忙") {

View File

@ -1,15 +1,155 @@
package auth package auth
//go:generate stringer -type=Protocol -linecomment import (
type Protocol int "encoding/hex"
"encoding/json"
"fmt"
"github.com/pkg/errors"
)
//go:generate stringer -type=ProtocolType -linecomment
type ProtocolType int
const ( const (
Unset Protocol = iota Unset ProtocolType = iota
AndroidPhone // Android Phone AndroidPhone // Android Phone
AndroidWatch // Android Watch AndroidWatch // Android Watch
MacOS // MacOS MacOS // MacOS
QiDian // 企点 QiDian // 企点
IPad // iPad IPad // iPad
AndroidPad // Android Pad
)
var (
AppVersions = map[ProtocolType]*AppVersion{
AndroidPhone: {
ApkId: "com.tencent.mobileqq",
AppId: 537164840,
SubAppId: 537164840,
AppKey: "0S200MNJT807V3GE",
SortVersionName: "8.9.63.11390",
BuildTime: 1685069178,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2546",
SSOVersion: 20,
MiscBitmap: 150470524,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
QUA: "V1_AND_SQ_8.9.63_4194_YYB_D",
Protocol: AndroidPhone,
},
AndroidPad: {
ApkId: "com.tencent.mobileqq",
AppId: 537164888,
SubAppId: 537164888,
AppKey: "0S200MNJT807V3GE",
SortVersionName: "8.9.63.11390",
BuildTime: 1685069178,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2546",
SSOVersion: 20,
MiscBitmap: 150470524,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
QUA: "V1_AND_SQ_8.9.63_4194_YYB_D",
Protocol: AndroidPad,
},
AndroidWatch: {
ApkId: "com.tencent.qqlite",
AppId: 537065138,
SubAppId: 537065138,
SortVersionName: "2.0.8",
BuildTime: 1559564731,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2365",
SSOVersion: 5,
MiscBitmap: 16252796,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
Protocol: AndroidWatch,
},
IPad: {
ApkId: "com.tencent.minihd.qq",
AppId: 537151363,
SubAppId: 537151363,
SortVersionName: "8.9.33.614",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 19,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_VKEY | WLOGIN_D2 | WLOGIN_SID | WLOGIN_PSKEY, // 1970400
Protocol: IPad,
},
MacOS: {
ApkId: "com.tencent.minihd.qq",
AppId: 537128930,
SubAppId: 537128930,
SortVersionName: "5.8.9",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_VKEY | WLOGIN_D2 | WLOGIN_SID | WLOGIN_PSKEY, // 1970400
Protocol: MacOS,
},
QiDian: {
ApkId: "com.tencent.qidian",
AppId: 537096038,
SubAppId: 537036590,
SortVersionName: "5.0.0",
BuildTime: 1630062176,
ApkSign: []byte{160, 30, 236, 171, 133, 233, 227, 186, 43, 15, 106, 21, 140, 133, 92, 41},
SdkVersion: "6.0.0.2484",
SSOVersion: 18,
MiscBitmap: 184024956,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_D2 | WLOGIN_PSKEY | WLOGIN_DA2, // 34869472
Protocol: QiDian,
},
}
)
// see oicq/wlogin_sdk/request/WtloginHelper.java class SigType
const (
_ = 1 << iota
WLOGIN_A5
_
_
WLOGIN_RESERVED
WLOGIN_STWEB
WLOGIN_A2
WLOGIN_ST
_
WLOGIN_LSKEY
_
_
WLOGIN_SKEY
WLOGIN_SIG64
WLOGIN_OPENKEY
WLOGIN_TOKEN
_
WLOGIN_VKEY
WLOGIN_D2
WLOGIN_SID
WLOGIN_PSKEY
WLOGIN_AQSIG
WLOGIN_LHSIG
WLOGIN_PAYTOKEN
WLOGIN_PF
WLOGIN_DA2
WLOGIN_QRPUSH
WLOGIN_PT4Token
) )
type AppVersion struct { type AppVersion struct {
@ -19,95 +159,47 @@ type AppVersion struct {
SdkVersion string SdkVersion string
AppId uint32 AppId uint32
SubAppId uint32 SubAppId uint32
AppKey string
BuildTime uint32 BuildTime uint32
SSOVersion uint32 SSOVersion uint32
MiscBitmap uint32 MiscBitmap uint32
SubSigmap uint32 SubSigmap uint32
MainSigMap uint32 MainSigMap uint32
Protocol Protocol QUA string
Protocol ProtocolType
} }
func (i Protocol) Version() *AppVersion { func (v *AppVersion) String() string {
switch i { return fmt.Sprintf("%s %s", v.Protocol.String(), v.SortVersionName)
case AndroidPhone: // Dumped by mirai from qq android v8.8.38 }
return &AppVersion{
ApkId: "com.tencent.mobileqq", func (v *AppVersion) UpdateFromJson(d []byte) error {
AppId: 537100432, var f appVersionFile
SubAppId: 537100432, if err := json.Unmarshal(d, &f); err != nil {
SortVersionName: "8.8.38", return errors.Wrap(err, "failed to unmarshal json message")
BuildTime: 1634310940,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2487",
SSOVersion: 16,
MiscBitmap: 184024956,
SubSigmap: 0x10400,
MainSigMap: 34869472,
Protocol: i,
}
case AndroidWatch:
return &AppVersion{
ApkId: "com.tencent.qqlite",
AppId: 537064446,
SubAppId: 537064446,
SortVersionName: "2.0.5",
BuildTime: 1559564731,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.236",
SSOVersion: 5,
MiscBitmap: 16252796,
SubSigmap: 0x10400,
MainSigMap: 34869472,
Protocol: i,
}
case IPad:
return &AppVersion{
ApkId: "com.tencent.minihd.qq",
AppId: 537097188,
SubAppId: 537097188,
SortVersionName: "8.8.35",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: 1970400,
Protocol: i,
}
case MacOS:
return &AppVersion{
ApkId: "com.tencent.minihd.qq",
AppId: 537064315,
SubAppId: 537064315,
SortVersionName: "5.8.9",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: 1970400,
Protocol: i,
}
case QiDian:
return &AppVersion{
ApkId: "com.tencent.qidian",
AppId: 537061386,
SubAppId: 537036590,
SortVersionName: "3.8.6",
BuildTime: 1556628836,
ApkSign: []byte{160, 30, 236, 171, 133, 233, 227, 186, 43, 15, 106, 21, 140, 133, 92, 41},
SdkVersion: "6.0.0.2365",
SSOVersion: 5,
MiscBitmap: 49807228,
SubSigmap: 66560,
MainSigMap: 34869472,
Protocol: i,
}
} }
// 按 AppVersion 字段顺序赋值,以免遗漏
v.ApkSign, _ = hex.DecodeString(f.ApkSign)
v.ApkId = f.ApkId
v.SortVersionName = f.SortVersionName
v.SdkVersion = f.SdkVersion
v.AppId = f.AppId
v.SubAppId = f.SubAppId
v.AppKey = f.AppKey
v.BuildTime = f.BuildTime
v.SSOVersion = f.SSOVersion
v.MiscBitmap = f.MiscBitmap
v.SubSigmap = f.SubSigmap
v.MainSigMap = f.MainSigMap
v.QUA = f.QUA
v.Protocol = f.ProtocolType
return nil return nil
} }
func (i ProtocolType) Version() *AppVersion {
return AppVersions[i]
}
type SigInfo struct { type SigInfo struct {
LoginBitmap uint64 LoginBitmap uint64
TGT []byte TGT []byte
@ -137,6 +229,7 @@ type SigInfo struct {
G []byte G []byte
T402 []byte T402 []byte
RandSeed []byte // t403 RandSeed []byte // t403
T547 []byte
// rollbackSig []byte // rollbackSig []byte
// t149 []byte // t149 []byte
// t150 []byte // t150 []byte
@ -149,3 +242,20 @@ type SigInfo struct {
Ksid []byte Ksid []byte
// msgCtrlBuf []byte // msgCtrlBuf []byte
} }
type appVersionFile struct {
ApkId string `json:"apk_id"`
AppId uint32 `json:"app_id"`
SubAppId uint32 `json:"sub_app_id"`
AppKey string `json:"app_key"`
SortVersionName string `json:"sort_version_name"`
BuildTime uint32 `json:"build_time"`
ApkSign string `json:"apk_sign"` // hex encoded
SdkVersion string `json:"sdk_version"`
SSOVersion uint32 `json:"sso_version"`
MiscBitmap uint32 `json:"misc_bitmap"`
MainSigMap uint32 `json:"main_sig_map"`
SubSigmap uint32 `json:"sub_sig_map"`
QUA string `json:"qua"`
ProtocolType ProtocolType `json:"protocol_type"`
}

View File

@ -2,9 +2,9 @@ package auth
import ( import (
"crypto/md5" "crypto/md5"
"crypto/rand"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"math/rand"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -45,7 +45,9 @@ type Device struct {
VendorOSName []byte VendorOSName []byte
Guid []byte Guid []byte
TgtgtKey []byte TgtgtKey []byte
Protocol Protocol QImei16 string
QImei36 string
Protocol ProtocolType
Version *OSVersion Version *OSVersion
} }
@ -134,13 +136,22 @@ func (info *Device) ReadJson(d []byte) error {
} }
switch f.Protocol { switch f.Protocol {
case 1, 2, 3, 4, 5: case 1, 2, 3, 4, 5, 6:
info.Protocol = Protocol(f.Protocol) info.Protocol = ProtocolType(f.Protocol)
default: default:
info.Protocol = IPad info.Protocol = AndroidPad
} }
v := new(OSVersion)
v.SDK = f.Version.Sdk
v.Release = []byte(f.Version.Release)
v.CodeName = []byte(f.Version.Codename)
v.Incremental = []byte(f.Version.Incremental)
info.Version = v
info.GenNewGuid() info.GenNewGuid()
info.GenNewTgtgtKey() info.GenNewTgtgtKey()
info.RequestQImei() // 应该可以缓存, 理论上同一设备每次请求都是一样的
return nil return nil
} }

View File

@ -0,0 +1,57 @@
package auth
import (
"bytes"
"crypto/sha256"
"math/big"
"time"
"github.com/Mrs4s/MiraiGo/binary"
)
func CalcPow(data []byte) []byte {
r := binary.NewReader(data)
a := r.ReadByte()
typ := r.ReadByte()
c := r.ReadByte()
ok := r.ReadByte() != 0
e := r.ReadUInt16()
f := r.ReadUInt16()
src := r.ReadBytesShort()
tgt := r.ReadBytesShort()
cpy := r.ReadBytesShort()
var dst []byte
var elp, cnt uint32
if typ == 2 && len(tgt) == 32 {
start := time.Now()
tmp := new(big.Int).SetBytes(src)
hash := sha256.Sum256(tmp.Bytes())
one := big.NewInt(1)
for !bytes.Equal(hash[:], tgt) {
tmp = tmp.Add(tmp, one)
hash = sha256.Sum256(tmp.Bytes())
cnt++
}
ok = true
dst = tmp.Bytes()
elp = uint32(time.Since(start).Milliseconds())
}
w := binary.SelectWriter()
w.WriteByte(a)
w.WriteByte(typ)
w.WriteByte(c)
w.WriteBool(ok)
w.WriteUInt16(e)
w.WriteUInt16(f)
w.WriteBytesShort(src)
w.WriteBytesShort(tgt)
w.WriteBytesShort(cpy)
if ok {
w.WriteBytesShort(dst)
w.WriteUInt32(elp)
w.WriteUInt32(cnt)
}
return w.Bytes()
}

View File

@ -1,28 +0,0 @@
// Code generated by "stringer -type=Protocol -linecomment"; DO NOT EDIT.
package auth
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[Unset-0]
_ = x[AndroidPhone-1]
_ = x[AndroidWatch-2]
_ = x[MacOS-3]
_ = x[QiDian-4]
_ = x[IPad-5]
}
const _Protocol_name = "UnsetAndroid PhoneAndroid WatchMacOS企点iPad"
var _Protocol_index = [...]uint8{0, 5, 18, 31, 36, 42, 46}
func (i Protocol) String() string {
if i < 0 || i >= Protocol(len(_Protocol_index)-1) {
return "Protocol(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Protocol_name[_Protocol_index[i]:_Protocol_index[i+1]]
}

View File

@ -0,0 +1,29 @@
// Code generated by "stringer -type=ProtocolType -linecomment"; DO NOT EDIT.
package auth
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[Unset-0]
_ = x[AndroidPhone-1]
_ = x[AndroidWatch-2]
_ = x[MacOS-3]
_ = x[QiDian-4]
_ = x[IPad-5]
_ = x[AndroidPad-6]
}
const _ProtocolType_name = "UnsetAndroid PhoneAndroid WatchMacOS企点iPadAndroid Pad"
var _ProtocolType_index = [...]uint8{0, 5, 18, 31, 36, 42, 46, 57}
func (i ProtocolType) String() string {
if i < 0 || i >= ProtocolType(len(_ProtocolType_index)-1) {
return "ProtocolType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ProtocolType_name[_ProtocolType_index[i]:_ProtocolType_index[i+1]]
}

View File

@ -0,0 +1,189 @@
package auth
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
crand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"fmt"
"math/rand"
"time"
"github.com/Mrs4s/MiraiGo/utils"
"github.com/tidwall/gjson"
)
const (
secret = "ZdJqM15EeO2zWc08"
rsaKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEIxgwoutfwoJxcGQeedgP7FG9
qaIuS0qzfR8gWkrkTZKM2iWHn2ajQpBRZjMSoSf6+KJGvar2ORhBfpDXyVtZCKpq
LQ+FLkpncClKVIrBwv6PHyUvuCb0rIarmgDnzkfQAqVufEtR64iazGDKatvJ9y6B
9NMbHddGSAUmRTCrHQIDAQAB
-----END PUBLIC KEY-----`
)
func (info *Device) RequestQImei() {
if info.Protocol.Version().AppKey == "" {
return
}
// init params
payload, _ := json.Marshal(genRandomPayloadByDevice(info))
cryptKey := utils.RandomStringRange(16, "abcdef1234567890")
ts := time.Now().Unix() * 1000
nonce := utils.RandomStringRange(16, "abcdef1234567890")
// init rsa key and aes key
publicKey := initPublicKey()
encryptedAesKey, _ := rsa.EncryptPKCS1v15(crand.Reader, publicKey, []byte(cryptKey))
encryptedPayload := aesEncrypt(payload, []byte(cryptKey))
key := base64.StdEncoding.EncodeToString(encryptedAesKey)
params := base64.StdEncoding.EncodeToString(encryptedPayload)
postData, _ := json.Marshal(map[string]any{
"key": key,
"params": params,
"time": ts,
"nonce": nonce,
"sign": sign(key, params, fmt.Sprint(ts), nonce),
"extra": "",
})
resp, _ := utils.HttpPostBytesWithCookie("https://snowflake.qq.com/ola/android", postData, "", "application/json")
if gjson.GetBytes(resp, "code").Int() != 0 {
return
}
encryptedResponse, _ := base64.StdEncoding.DecodeString(gjson.GetBytes(resp, "data").String())
if len(encryptedResponse) == 0 {
return
}
decryptedResponse := aesDecrypt(encryptedResponse, []byte(cryptKey))
info.QImei16 = gjson.GetBytes(decryptedResponse, "q16").String()
info.QImei36 = gjson.GetBytes(decryptedResponse, "q36").String()
}
func initPublicKey() *rsa.PublicKey {
blockPub, _ := pem.Decode([]byte(rsaKey))
pub, _ := x509.ParsePKIXPublicKey(blockPub.Bytes)
return pub.(*rsa.PublicKey)
}
func sign(key, params, ts, nonce string) string {
h := md5.Sum([]byte(key + params + ts + nonce + secret))
return hex.EncodeToString(h[:])
}
func aesEncrypt(src []byte, key []byte) []byte {
block, _ := aes.NewCipher(key)
ecb := cipher.NewCBCEncrypter(block, key)
content := src
content = pkcs5Padding(content, block.BlockSize())
crypted := make([]byte, len(content))
ecb.CryptBlocks(crypted, content)
return crypted
}
func aesDecrypt(crypt []byte, key []byte) []byte {
block, _ := aes.NewCipher(key)
ecb := cipher.NewCBCDecrypter(block, key)
decrypted := make([]byte, len(crypt))
ecb.CryptBlocks(decrypted, crypt)
return pkcs5Trimming(decrypted)
}
func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func pkcs5Trimming(encrypt []byte) []byte {
padding := encrypt[len(encrypt)-1]
return encrypt[:len(encrypt)-int(padding)]
}
func genRandomPayloadByDevice(info *Device) map[string]any {
now := time.Now()
seed := int64(0x6F4)
for _, b := range info.Guid {
seed += int64(b)
}
fixedRand := rand.New(rand.NewSource(seed))
reserved := map[string]string{
"harmony": "0",
"clone": "0",
"containe": "",
"oz": "UhYmelwouA+V2nPWbOvLTgN2/m8jwGB+yUB5v9tysQg=",
"oo": "Xecjt+9S1+f8Pz2VLSxgpw==",
"kelong": "0",
"uptimes": time.Unix(now.Unix()-fixedRand.Int63n(14400), 0).Format(time.DateTime),
"multiUser": "0",
"bod": string(info.Board),
"brd": string(info.Brand),
"dv": string(info.Device),
"firstLevel": "",
"manufact": string(info.Brand),
"name": string(info.Model),
"host": "se.infra",
"kernel": string(info.ProcVersion),
}
reservedBytes, _ := json.Marshal(reserved)
deviceType := "Phone"
if info.Protocol == AndroidPad {
deviceType = "Pad"
}
beaconId := ""
timeMonth := time.Now().Format("2006-01-") + "01"
rand1 := fixedRand.Intn(899999) + 100000
rand2 := fixedRand.Intn(899999999) + 100000000
for i := 1; i <= 40; i++ {
switch i {
case 1, 2, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30, 33, 34, 37, 38:
beaconId += fmt.Sprintf("k%v:%v%v.%v", i, timeMonth, rand1, rand2)
case 3:
beaconId += "k3:0000000000000000"
case 4:
beaconId += "k4:" + utils.RandomStringRange(16, "123456789abcdef")
default:
beaconId += fmt.Sprintf("k%v:%v", i, fixedRand.Intn(10000))
}
beaconId += ";"
}
return map[string]any{
"androidId": string(info.AndroidId),
"platformId": 1,
"appKey": info.Protocol.Version().AppKey,
"appVersion": info.Protocol.Version().SortVersionName,
"beaconIdSrc": beaconId,
"brand": string(info.Brand),
"channelId": "2017",
"cid": "",
"imei": info.IMEI,
"imsi": "",
"mac": "",
"model": string(info.Model),
"networkType": "unknown",
"oaid": "",
"osVersion": "Android " + string(info.Version.Release) + ",level " + fmt.Sprint(info.Version.SDK),
"qimei": "",
"qimei36": "",
"sdkVersion": "1.2.13.6",
"audit": "",
"userId": "{}",
"packageId": info.Protocol.Version().ApkId,
"deviceType": deviceType,
"sdkName": "",
"reserved": string(reservedBytes),
}
}

View File

@ -1,31 +0,0 @@
package highway
import (
binary2 "encoding/binary"
"fmt"
"net"
"github.com/Mrs4s/MiraiGo/binary"
)
type Addr struct {
IP uint32
Port int
}
func (a Addr) asTcpAddr() *net.TCPAddr {
addr := &net.TCPAddr{
IP: make([]byte, 4),
Port: a.Port,
}
binary2.LittleEndian.PutUint32(addr.IP, a.IP)
return addr
}
func (a Addr) AsNetIP() net.IP {
return net.IPv4(byte(a.IP>>24), byte(a.IP>>16), byte(a.IP>>8), byte(a.IP))
}
func (a Addr) String() string {
return fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(a.IP), a.Port)
}

View File

@ -3,11 +3,8 @@ package highway
import ( import (
"crypto/md5" "crypto/md5"
"io" "io"
"net"
"os"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@ -15,60 +12,45 @@ import (
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/internal/proto" "github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/utils"
) )
type BdhInput struct { type Transaction struct {
CommandID int32 CommandID int32
File string // upload multi-thread required Body io.Reader
Body io.ReadSeeker Sum []byte // md5 sum of body
Size int64 // body size
Ticket []byte Ticket []byte
Ext []byte Ext []byte
Encrypt bool Encrypt bool
} }
func (bdh *BdhInput) encrypt(key []byte) error { func (bdh *Transaction) encrypt(key []byte) error {
if bdh.Encrypt { if !bdh.Encrypt {
if len(key) == 0 { return nil
return errors.New("session key not found. maybe miss some packet?")
}
bdh.Ext = binary.NewTeaCipher(key).Encrypt(bdh.Ext)
} }
if len(key) == 0 {
return errors.New("session key not found. maybe miss some packet?")
}
bdh.Ext = binary.NewTeaCipher(key).Encrypt(bdh.Ext)
return nil return nil
} }
func (s *Session) UploadBDH(input BdhInput) ([]byte, error) { func (s *Session) uploadSingle(trans Transaction) ([]byte, error) {
if len(s.SsoAddr) == 0 { pc, err := s.selectConn()
return nil, errors.New("srv addrs not found. maybe miss some packet?")
}
addr := s.SsoAddr[0].String()
sum, length := utils.ComputeMd5AndLength(input.Body)
_, _ = input.Body.Seek(0, io.SeekStart)
if err := input.encrypt(s.SessionKey); err != nil {
return nil, errors.Wrap(err, "encrypt error")
}
conn, err := net.DialTimeout("tcp", addr, time.Second*20)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "connect error")
}
defer conn.Close()
reader := binary.NewNetworkReader(conn)
if err = s.sendEcho(conn); err != nil {
return nil, err return nil, err
} }
defer s.putIdleConn(pc)
const chunkSize = 256 * 1024 reader := binary.NewNetworkReader(pc.conn)
const chunkSize = 128 * 1024
var rspExt []byte var rspExt []byte
offset := 0 offset := 0
chunk := make([]byte, chunkSize) chunk := make([]byte, chunkSize)
w := binary.SelectWriter()
defer binary.PutWriter(w)
for { for {
chunk = chunk[:chunkSize] chunk = chunk[:cap(chunk)]
rl, err := io.ReadFull(input.Body, chunk) rl, err := io.ReadFull(trans.Body, chunk)
if errors.Is(err, io.EOF) { if rl == 0 {
break break
} }
if errors.Is(err, io.ErrUnexpectedEOF) { if errors.Is(err, io.ErrUnexpectedEOF) {
@ -79,31 +61,30 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) {
MsgBasehead: &pb.DataHighwayHead{ MsgBasehead: &pb.DataHighwayHead{
Version: 1, Version: 1,
Uin: s.Uin, Uin: s.Uin,
Command: "PicUp.DataUp", Command: _REQ_CMD_DATA,
Seq: s.nextSeq(), Seq: s.nextSeq(),
Appid: s.AppID, Appid: s.AppID,
Dataflag: 4096, Dataflag: 4096,
CommandId: input.CommandID, CommandId: trans.CommandID,
LocaleId: 2052, LocaleId: 2052,
}, },
MsgSeghead: &pb.SegHead{ MsgSeghead: &pb.SegHead{
Filesize: length, Filesize: trans.Size,
Dataoffset: int64(offset), Dataoffset: int64(offset),
Datalength: int32(rl), Datalength: int32(rl),
Serviceticket: input.Ticket, Serviceticket: trans.Ticket,
Md5: ch[:], Md5: ch[:],
FileMd5: sum, FileMd5: trans.Sum,
}, },
ReqExtendinfo: input.Ext, ReqExtendinfo: trans.Ext,
}) })
offset += rl offset += rl
w.Reset() buffers := frame(head, chunk)
writeHeadBody(w, head, chunk) _, err = buffers.WriteTo(pc.conn)
_, err = conn.Write(w.Bytes())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "write conn error") return nil, errors.Wrap(err, "write conn error")
} }
rspHead, _, err := readResponse(reader) rspHead, err := readResponse(reader)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "highway upload error") return nil, errors.Wrap(err, "highway upload error")
} }
@ -114,155 +95,123 @@ func (s *Session) UploadBDH(input BdhInput) ([]byte, error) {
rspExt = rspHead.RspExtendinfo rspExt = rspHead.RspExtendinfo
} }
if rspHead.MsgSeghead != nil && rspHead.MsgSeghead.Serviceticket != nil { if rspHead.MsgSeghead != nil && rspHead.MsgSeghead.Serviceticket != nil {
input.Ticket = rspHead.MsgSeghead.Serviceticket trans.Ticket = rspHead.MsgSeghead.Serviceticket
} }
} }
return rspExt, nil return rspExt, nil
} }
func (s *Session) UploadBDHMultiThread(input BdhInput, threadCount int) ([]byte, error) { func (s *Session) Upload(trans Transaction) ([]byte, error) {
if len(s.SsoAddr) == 0 { // encrypt ext data
return nil, errors.New("srv addrs not found. maybe miss some packet?") if err := trans.encrypt(s.SessionKey); err != nil {
return nil, err
} }
addr := s.SsoAddr[0].String()
stat, err := os.Stat(input.File) const maxThreadCount = 4
threadCount := int(trans.Size) / (3 * 512 * 1024) // 1 thread upload 1.5 MB
if threadCount > maxThreadCount {
threadCount = maxThreadCount
}
if threadCount < 2 {
// single thread upload
return s.uploadSingle(trans)
}
// pick a address
// TODO: pick smarter
pc, err := s.selectConn()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "get stat error") return nil, err
} }
file, err := os.OpenFile(input.File, os.O_RDONLY, 0o666) addr := pc.addr
if err != nil { s.putIdleConn(pc)
return nil, errors.Wrap(err, "open file error")
}
sum, length := utils.ComputeMd5AndLength(file)
_, _ = file.Seek(0, io.SeekStart)
if err := input.encrypt(s.SessionKey); err != nil { const blockSize int64 = 256 * 1024
return nil, errors.Wrap(err, "encrypt error")
}
// for small file and small thread count,
// use UploadBDH instead of UploadBDHMultiThread
if length < 1024*1024*3 || threadCount < 2 {
input.Body = file
return s.UploadBDH(input)
}
type BlockMetaData struct {
Id int
BeginOffset int64
EndOffset int64
}
const blockSize int64 = 1024 * 512
var ( var (
blocks []*BlockMetaData rspExt []byte
rspExt []byte completedThread uint32
BlockId = ^uint32(0) // -1 cond = sync.NewCond(&sync.Mutex{})
uploadedCount uint32 offset = int64(0)
cond = sync.NewCond(&sync.Mutex{}) count = (trans.Size + blockSize - 1) / blockSize
id = 0
) )
// Init Blocks
{
var temp int64 = 0
for temp+blockSize < stat.Size() {
blocks = append(blocks, &BlockMetaData{
Id: len(blocks),
BeginOffset: temp,
EndOffset: temp + blockSize,
})
temp += blockSize
}
blocks = append(blocks, &BlockMetaData{
Id: len(blocks),
BeginOffset: temp,
EndOffset: stat.Size(),
})
}
doUpload := func() error { doUpload := func() error {
// send signal complete uploading // send signal complete uploading
defer cond.Signal() defer func() {
atomic.AddUint32(&completedThread, 1)
cond.Signal()
}()
conn, err := net.DialTimeout("tcp", addr, time.Second*20) // todo: get from pool?
pc, err := s.connect(addr)
if err != nil { if err != nil {
return errors.Wrap(err, "connect error")
}
defer conn.Close()
chunk, _ := os.OpenFile(input.File, os.O_RDONLY, 0o666)
defer chunk.Close()
reader := binary.NewNetworkReader(conn)
if err = s.sendEcho(conn); err != nil {
return err return err
} }
defer s.putIdleConn(pc)
buffer := make([]byte, blockSize) reader := binary.NewNetworkReader(pc.conn)
w := binary.SelectWriter() chunk := make([]byte, blockSize)
w.Reset()
w.Grow(600 * 1024) // 复用,600k 不要放回池中
for { for {
nextId := atomic.AddUint32(&BlockId, 1) cond.L.Lock() // lock protect reading
if nextId >= uint32(len(blocks)) { off := offset
break offset += blockSize
} id++
block := blocks[nextId] last := int64(id) == count
if block.Id == len(blocks)-1 { if last { // last
cond.L.Lock() for atomic.LoadUint32(&completedThread) != uint32(threadCount-1) {
for atomic.LoadUint32(&uploadedCount) != uint32(len(blocks))-1 {
cond.Wait() cond.Wait()
} }
} else if int64(id) > count {
cond.L.Unlock() cond.L.Unlock()
break
} }
buffer = buffer[:blockSize] chunk = chunk[:blockSize]
_, _ = chunk.Seek(block.BeginOffset, io.SeekStart) n, err := io.ReadFull(trans.Body, chunk)
ri, err := io.ReadFull(chunk, buffer) cond.L.Unlock()
if err != nil {
if err == io.EOF { if n == 0 {
break break
}
if err == io.ErrUnexpectedEOF {
buffer = buffer[:ri]
} else {
return err
}
} }
ch := md5.Sum(buffer) if errors.Is(err, io.ErrUnexpectedEOF) {
chunk = chunk[:n]
}
ch := md5.Sum(chunk)
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{ head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{ MsgBasehead: &pb.DataHighwayHead{
Version: 1, Version: 1,
Uin: s.Uin, Uin: s.Uin,
Command: "PicUp.DataUp", Command: _REQ_CMD_DATA,
Seq: s.nextSeq(), Seq: s.nextSeq(),
Appid: s.AppID, Appid: s.AppID,
Dataflag: 4096, Dataflag: 4096,
CommandId: input.CommandID, CommandId: trans.CommandID,
LocaleId: 2052, LocaleId: 2052,
}, },
MsgSeghead: &pb.SegHead{ MsgSeghead: &pb.SegHead{
Filesize: stat.Size(), Filesize: trans.Size,
Dataoffset: block.BeginOffset, Dataoffset: off,
Datalength: int32(ri), Datalength: int32(n),
Serviceticket: input.Ticket, Serviceticket: trans.Ticket,
Md5: ch[:], Md5: ch[:],
FileMd5: sum, FileMd5: trans.Sum,
}, },
ReqExtendinfo: input.Ext, ReqExtendinfo: trans.Ext,
}) })
w.Reset() buffers := frame(head, chunk)
writeHeadBody(w, head, buffer) _, err = buffers.WriteTo(pc.conn)
_, err = conn.Write(w.Bytes())
if err != nil { if err != nil {
return errors.Wrap(err, "write conn error") return errors.Wrap(err, "write conn error")
} }
rspHead, _, err := readResponse(reader) rspHead, err := readResponse(reader)
if err != nil { if err != nil {
return errors.Wrap(err, "highway upload error") return errors.Wrap(err, "highway upload error")
} }
if rspHead.ErrorCode != 0 { if rspHead.ErrorCode != 0 {
return errors.Errorf("upload failed: %d", rspHead.ErrorCode) return errors.Errorf("upload failed: %d", rspHead.ErrorCode)
} }
if rspHead.RspExtendinfo != nil { if last && rspHead.RspExtendinfo != nil {
rspExt = rspHead.RspExtendinfo rspExt = rspHead.RspExtendinfo
} }
atomic.AddUint32(&uploadedCount, 1)
} }
return nil return nil
} }
@ -271,6 +220,5 @@ func (s *Session) UploadBDHMultiThread(input BdhInput, threadCount int) ([]byte,
for i := 0; i < threadCount; i++ { for i := 0; i < threadCount; i++ {
group.Go(doUpload) group.Go(doUpload)
} }
err = group.Wait() return rspExt, group.Wait()
return rspExt, err
} }

View File

@ -0,0 +1,35 @@
package highway
import (
"encoding/binary"
"net"
)
var etx = []byte{0x29}
// frame 包格式
//
// - STX: 0x28(40)
// - head length
// - body length
// - head data
// - body data
// - ETX: 0x29(41)
//
// 节省内存, 可被go runtime优化为writev操作
func frame(head []byte, body []byte) net.Buffers {
buffers := make(net.Buffers, 4)
// buffer0 format:
// - STX
// - head length
// - body length
buffer0 := make([]byte, 9)
buffer0[0] = 0x28
binary.BigEndian.PutUint32(buffer0[1:], uint32(len(head)))
binary.BigEndian.PutUint32(buffer0[5:], uint32(len(body)))
buffers[0] = buffer0
buffers[1] = head
buffers[2] = body
buffers[3] = etx
return buffers
}

View File

@ -1,12 +1,10 @@
package highway package highway
import ( import (
"bytes"
"crypto/md5"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "runtime"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -15,26 +13,59 @@ import (
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/internal/proto" "github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/utils"
) )
// see com/tencent/mobileqq/highway/utils/BaseConstants.java#L120-L121
const (
_REQ_CMD_DATA = "PicUp.DataUp"
_REQ_CMD_HEART_BREAK = "PicUp.Echo"
)
type Addr struct {
IP uint32
Port int
}
func (a Addr) AsNetIP() net.IP {
return net.IPv4(byte(a.IP>>24), byte(a.IP>>16), byte(a.IP>>8), byte(a.IP))
}
func (a Addr) String() string {
return fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(a.IP), a.Port)
}
func (a Addr) empty() bool {
return a.IP == 0 || a.Port == 0
}
type Session struct { type Session struct {
Uin string Uin string
AppID int32 AppID int32
SigSession []byte SigSession []byte
SessionKey []byte SessionKey []byte
SsoAddr []Addr
seq int32 seq int32
addrMu sync.Mutex
idx int
SsoAddr []Addr
idleMu sync.Mutex
idleCount int
idle *idle
} }
const highwayMaxResponseSize int32 = 1024 * 100 // 100k const highwayMaxResponseSize int32 = 1024 * 100 // 100k
func (s *Session) AddrLength() int { func (s *Session) AddrLength() int {
s.addrMu.Lock()
defer s.addrMu.Unlock()
return len(s.SsoAddr) return len(s.SsoAddr)
} }
func (s *Session) AppendAddr(ip, port uint32) { func (s *Session) AppendAddr(ip, port uint32) {
s.addrMu.Lock()
defer s.addrMu.Unlock()
addr := Addr{ addr := Addr{
IP: ip, IP: ip,
Port: int(port), Port: int(port),
@ -42,162 +73,6 @@ func (s *Session) AppendAddr(ip, port uint32) {
s.SsoAddr = append(s.SsoAddr, addr) s.SsoAddr = append(s.SsoAddr, addr)
} }
type Input struct {
CommandID int32
Key []byte
Body io.ReadSeeker
}
func (s *Session) Upload(addr Addr, input Input) error {
fh, length := utils.ComputeMd5AndLength(input.Body)
_, _ = input.Body.Seek(0, io.SeekStart)
conn, err := net.DialTimeout("tcp", addr.String(), time.Second*3)
if err != nil {
return errors.Wrap(err, "connect error")
}
defer conn.Close()
const chunkSize = 8192 * 8
chunk := make([]byte, chunkSize)
offset := 0
reader := binary.NewNetworkReader(conn)
w := binary.SelectWriter()
defer binary.PutWriter(w)
for {
chunk = chunk[:chunkSize]
rl, err := io.ReadFull(input.Body, chunk)
if errors.Is(err, io.EOF) {
break
}
if errors.Is(err, io.ErrUnexpectedEOF) {
chunk = chunk[:rl]
}
ch := md5.Sum(chunk)
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: s.Uin,
Command: "PicUp.DataUp",
Seq: s.nextSeq(),
Appid: s.AppID,
Dataflag: 4096,
CommandId: input.CommandID,
LocaleId: 2052,
},
MsgSeghead: &pb.SegHead{
Filesize: length,
Dataoffset: int64(offset),
Datalength: int32(rl),
Serviceticket: input.Key,
Md5: ch[:],
FileMd5: fh,
},
ReqExtendinfo: []byte{},
})
offset += rl
w.Reset()
writeHeadBody(w, head, chunk)
_, err = conn.Write(w.Bytes())
if err != nil {
return errors.Wrap(err, "write conn error")
}
rspHead, _, err := readResponse(reader)
if err != nil {
return errors.Wrap(err, "highway upload error")
}
if rspHead.ErrorCode != 0 {
return errors.New("upload failed")
}
}
return nil
}
type ExcitingInput struct {
CommandID int32
Body io.ReadSeeker
Ticket []byte
Ext []byte
}
func (s *Session) UploadExciting(input ExcitingInput) ([]byte, error) {
fileMd5, fileLength := utils.ComputeMd5AndLength(input.Body)
_, _ = input.Body.Seek(0, io.SeekStart)
addr := s.SsoAddr[0]
url := fmt.Sprintf("http://%v/cgi-bin/httpconn?htcmd=0x6FF0087&Uin=%v", addr, s.Uin)
var (
rspExt []byte
offset int64 = 0
chunkSize = 524288
)
chunk := make([]byte, chunkSize)
w := binary.SelectWriter()
w.Reset()
w.Grow(600 * 1024) // 复用,600k 不要放回池中
for {
chunk = chunk[:chunkSize]
rl, err := io.ReadFull(input.Body, chunk)
if err == io.EOF {
break
}
if err == io.ErrUnexpectedEOF {
chunk = chunk[:rl]
}
ch := md5.Sum(chunk)
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: s.Uin,
Command: "PicUp.DataUp",
Seq: s.nextSeq(),
Appid: s.AppID,
Dataflag: 0,
CommandId: input.CommandID,
LocaleId: 0,
},
MsgSeghead: &pb.SegHead{
Filesize: fileLength,
Dataoffset: offset,
Datalength: int32(rl),
Serviceticket: input.Ticket,
Md5: ch[:],
FileMd5: fileMd5,
},
ReqExtendinfo: input.Ext,
})
offset += int64(rl)
w.Reset()
writeHeadBody(w, head, chunk)
req, _ := http.NewRequest("POST", url, bytes.NewReader(w.Bytes()))
req.Header.Set("Accept", "*/*")
req.Header.Set("Connection", "Keep-Alive")
req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)")
req.Header.Set("Pragma", "no-cache")
rsp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, errors.Wrap(err, "request error")
}
body, _ := io.ReadAll(rsp.Body)
_ = rsp.Body.Close()
r := binary.NewReader(body)
r.ReadByte()
hl := r.ReadInt32()
a2 := r.ReadInt32()
h := r.ReadBytes(int(hl))
r.ReadBytes(int(a2))
rspHead := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(h, rspHead); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if rspHead.ErrorCode != 0 {
return nil, errors.Errorf("upload failed: %d", rspHead.ErrorCode)
}
if rspHead.RspExtendinfo != nil {
rspExt = rspHead.RspExtendinfo
}
}
return rspExt, nil
}
func (s *Session) nextSeq() int32 { func (s *Session) nextSeq() int32 {
return atomic.AddInt32(&s.seq, 2) return atomic.AddInt32(&s.seq, 2)
} }
@ -207,7 +82,7 @@ func (s *Session) sendHeartbreak(conn net.Conn) error {
MsgBasehead: &pb.DataHighwayHead{ MsgBasehead: &pb.DataHighwayHead{
Version: 1, Version: 1,
Uin: s.Uin, Uin: s.Uin,
Command: "PicUp.Echo", Command: _REQ_CMD_HEART_BREAK,
Seq: s.nextSeq(), Seq: s.nextSeq(),
Appid: s.AppID, Appid: s.AppID,
Dataflag: 4096, Dataflag: 4096,
@ -215,49 +90,170 @@ func (s *Session) sendHeartbreak(conn net.Conn) error {
LocaleId: 2052, LocaleId: 2052,
}, },
}) })
w := binary.SelectWriter() buffers := frame(head, nil)
writeHeadBody(w, head, nil) _, err := buffers.WriteTo(conn)
_, err := conn.Write(w.Bytes())
binary.PutWriter(w)
return err return err
} }
func (s *Session) sendEcho(conn net.Conn) error { func (s *Session) ping(pc *persistConn) error {
err := s.sendHeartbreak(conn) start := time.Now()
err := s.sendHeartbreak(pc.conn)
if err != nil { if err != nil {
return errors.Wrap(err, "echo error") return errors.Wrap(err, "echo error")
} }
if _, _, err = readResponse(binary.NewNetworkReader(conn)); err != nil { if _, err = readResponse(binary.NewNetworkReader(pc.conn)); err != nil {
return errors.Wrap(err, "echo error") return errors.Wrap(err, "echo error")
} }
// update delay
pc.ping = time.Since(start).Milliseconds()
return nil return nil
} }
func writeHeadBody(w *binary.Writer, head []byte, body []byte) { func readResponse(r *binary.NetworkReader) (*pb.RspDataHighwayHead, error) {
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(uint32(len(body)))
w.Write(head)
w.Write(body)
w.WriteByte(41)
}
func readResponse(r *binary.NetworkReader) (*pb.RspDataHighwayHead, []byte, error) {
_, err := r.ReadByte() _, err := r.ReadByte()
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "failed to read byte") return nil, errors.Wrap(err, "failed to read byte")
} }
hl, _ := r.ReadInt32() hl, _ := r.ReadInt32()
a2, _ := r.ReadInt32() a2, _ := r.ReadInt32()
if hl > highwayMaxResponseSize || a2 > highwayMaxResponseSize { if hl > highwayMaxResponseSize || a2 > highwayMaxResponseSize {
return nil, nil, errors.Errorf("highway response invild. head size: %v body size: %v", hl, a2) return nil, errors.Errorf("highway response invild. head size: %v body size: %v", hl, a2)
} }
head, _ := r.ReadBytes(int(hl)) head, _ := r.ReadBytes(int(hl))
payload, _ := r.ReadBytes(int(a2)) _, _ = r.ReadBytes(int(a2)) // skip payload
_, _ = r.ReadByte() _, _ = r.ReadByte()
rsp := new(pb.RspDataHighwayHead) rsp := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(head, rsp); err != nil { if err = proto.Unmarshal(head, rsp); err != nil {
return nil, nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
return rsp, payload, nil return rsp, nil
}
type persistConn struct {
conn net.Conn
addr Addr
ping int64 // echo ping
}
const maxIdleConn = 7
type idle struct {
pc persistConn
next *idle
}
// getIdleConn ...
func (s *Session) getIdleConn() persistConn {
s.idleMu.Lock()
defer s.idleMu.Unlock()
// no idle
if s.idle == nil {
return persistConn{}
}
// switch the fastest idle conn
conn := s.idle.pc
s.idle = s.idle.next
s.idleCount--
if s.idleCount < 0 {
panic("idle count underflow")
}
return conn
}
func (s *Session) putIdleConn(pc persistConn) {
s.idleMu.Lock()
defer s.idleMu.Unlock()
// check persistConn
if pc.conn == nil || pc.addr.empty() {
panic("put bad idle conn")
}
cur := &idle{pc: pc}
s.idleCount++
if s.idle == nil { // quick path
s.idle = cur
return
}
// insert between pre and succ
var pre, succ *idle
succ = s.idle
for succ != nil && succ.pc.ping < pc.ping { // keep idle list sorted by delay incremental
pre = succ
succ = succ.next
}
if pre != nil {
pre.next = cur
}
cur.next = succ
// remove the slowest idle conn if idle count greater than maxIdleConn
if s.idleCount > maxIdleConn {
for cur.next != nil {
pre = cur
cur = cur.next
}
pre.next = nil
s.idleCount--
}
}
func (s *Session) connect(addr Addr) (persistConn, error) {
conn, err := net.DialTimeout("tcp", addr.String(), time.Second*3)
if err != nil {
return persistConn{}, err
}
_ = conn.(*net.TCPConn).SetKeepAlive(true)
// close conn
runtime.SetFinalizer(conn, func(conn net.Conn) {
_ = conn.Close()
})
pc := persistConn{conn: conn, addr: addr}
if err = s.ping(&pc); err != nil {
return persistConn{}, err
}
return pc, nil
}
func (s *Session) nextAddr() Addr {
s.addrMu.Lock()
defer s.addrMu.Unlock()
addr := s.SsoAddr[s.idx]
s.idx = (s.idx + 1) % len(s.SsoAddr)
return addr
}
func (s *Session) selectConn() (pc persistConn, err error) {
for { // select from idle pc
pc = s.getIdleConn()
if pc.conn == nil {
// no idle connection
break
}
err = s.ping(&pc) // ping
if err == nil {
return
}
}
try := 0
for {
addr := s.nextAddr()
pc, err = s.connect(addr)
if err == nil {
break
}
try++
if try > 5 {
break
}
}
return
} }

View File

@ -0,0 +1,32 @@
package intern
import (
"sync"
)
// String Interning is a technique for reducing the memory footprint of large
// strings. It can re-use strings that are already in memory.
type StringInterner struct {
mu sync.RWMutex
strings map[string]string
}
func NewStringInterner() *StringInterner {
return &StringInterner{
strings: make(map[string]string),
}
}
func (i *StringInterner) Intern(s string) string {
i.mu.RLock()
if v, ok := i.strings[s]; ok {
i.mu.RUnlock()
return v
}
i.mu.RUnlock()
i.mu.Lock()
i.strings[s] = s
i.mu.Unlock()
return s
}

View File

@ -5,119 +5,77 @@ import (
"io" "io"
"net" "net"
"sync" "sync"
"sync/atomic"
"unsafe"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type TCPListener struct { type TCPClient struct {
//lock sync.RWMutex lock sync.RWMutex
conn *net.TCPConn conn net.Conn
//connected bool connected bool
plannedDisconnect func(*TCPClient)
// PlannedDisconnect 预料中的断开连接 unexpectedDisconnect func(*TCPClient, error)
// 如调用 Close() Connect()
PlannedDisconnect func(*TCPListener)
// UnexpectedDisconnect 未预料的断开连接
UnexpectedDisconnect func(*TCPListener, error)
} }
func (t *TCPListener) getConn() *net.TCPConn { var ErrConnectionClosed = errors.New("connection closed")
return (*net.TCPConn)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&t.conn))))
// PlannedDisconnect 预料中的断开连接
// 如调用 Close() Connect()
func (t *TCPClient) PlannedDisconnect(f func(*TCPClient)) {
t.lock.Lock()
defer t.lock.Unlock()
t.plannedDisconnect = f
} }
func (t *TCPListener) setConn(conn *net.TCPConn) (swapped bool) { // UnexpectedDisconnect 未预料的断开连接
return atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&t.conn)), unsafe.Pointer(nil), unsafe.Pointer(conn)) func (t *TCPClient) UnexpectedDisconnect(f func(*TCPClient, error)) {
t.lock.Lock()
defer t.lock.Unlock()
t.unexpectedDisconnect = f
} }
func (t *TCPListener) closeConn() *net.TCPConn { func (t *TCPClient) Connect(addr string) error {
return (*net.TCPConn)(atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(&t.conn)), unsafe.Pointer(nil)))
}
func (t *TCPListener) Connected() bool {
// 等同于 t.getConn() != nil (? copilot写的)
return t.getConn() != nil
}
func (t *TCPListener) Connect(addr *net.TCPAddr) error {
t.Close() t.Close()
conn, err := net.DialTCP("tcp", nil, addr) conn, err := net.Dial("tcp", addr)
if err != nil { if err != nil {
return errors.Wrap(err, "dial tcp error") return errors.Wrap(err, "dial tcp error")
} }
t.setConn(conn) t.lock.Lock()
//t.lock.Lock() defer t.lock.Unlock()
//defer t.lock.Unlock() t.conn = conn
//t.conn = conn t.connected = true
return nil return nil
} }
// ConnectFastest 连接到最快的服务器 func (t *TCPClient) Write(buf []byte) error {
// TODO 禁用不可用服务器
func (t *TCPListener) ConnectFastest(addr []*net.TCPAddr) (*net.TCPAddr, error) {
ch := make(chan error)
wg := sync.WaitGroup{}
wg.Add(len(addr))
for _, remote := range addr {
go func(remote *net.TCPAddr) {
defer wg.Done()
conn, err := net.DialTCP("tcp", nil, remote)
if err != nil {
return
}
//addrs = append(addrs, remote)
if !t.setConn(conn) {
_ = conn.Close()
return
}
ch <- nil
}(remote)
}
go func() {
wg.Wait()
if t.getConn() == nil {
ch <- errors.New("All addr are unreachable")
}
}()
err := <-ch
if err != nil {
return nil, err
}
conn := t.getConn()
return conn.RemoteAddr().(*net.TCPAddr), nil
}
func (t *TCPListener) Write(buf []byte) error {
if conn := t.getConn(); conn != nil { if conn := t.getConn(); conn != nil {
_, err := conn.Write(buf) _, err := conn.Write(buf)
if err != nil { if err != nil {
t.unexpectedClose(err) t.unexpectedClose(err)
return ErrConnectionBroken return ErrConnectionClosed
} }
return nil return nil
} }
return ErrConnectionBroken return ErrConnectionClosed
} }
func (t *TCPListener) ReadBytes(len int) ([]byte, error) { func (t *TCPClient) ReadBytes(len int) ([]byte, error) {
buf := make([]byte, len) buf := make([]byte, len)
if conn := t.getConn(); conn != nil { if conn := t.getConn(); conn != nil {
_, err := io.ReadFull(conn, buf) _, err := io.ReadFull(conn, buf)
if err != nil { if err != nil {
// time.Sleep(time.Millisecond * 100) // 服务器会发送offline包后立即断开连接, 此时还没解析, 可能还是得加锁 // time.Sleep(time.Millisecond * 100) // 服务器会发送offline包后立即断开连接, 此时还没解析, 可能还是得加锁
t.unexpectedClose(err) t.unexpectedClose(err)
return nil, ErrConnectionBroken return nil, ErrConnectionClosed
} }
return buf, nil return buf, nil
} }
return nil, ErrConnectionBroken return nil, ErrConnectionClosed
} }
func (t *TCPListener) ReadInt32() (int32, error) { func (t *TCPClient) ReadInt32() (int32, error) {
b, err := t.ReadBytes(4) b, err := t.ReadBytes(4)
if err != nil { if err != nil {
return 0, err return 0, err
@ -125,48 +83,45 @@ func (t *TCPListener) ReadInt32() (int32, error) {
return int32(binary.BigEndian.Uint32(b)), nil return int32(binary.BigEndian.Uint32(b)), nil
} }
func (t *TCPListener) Close() { func (t *TCPClient) Close() {
t.close() t.close()
t.invokePlannedDisconnect() t.invokePlannedDisconnect()
} }
func (t *TCPListener) unexpectedClose(err error) { func (t *TCPClient) unexpectedClose(err error) {
t.close() t.close()
t.invokeUnexpectedDisconnect(err) t.invokeUnexpectedDisconnect(err)
} }
func (t *TCPListener) close() { func (t *TCPClient) close() {
if conn := t.closeConn(); conn != nil { t.lock.Lock()
_ = conn.Close() defer t.lock.Unlock()
if t.conn != nil {
_ = t.conn.Close()
t.conn = nil
} }
} }
func (t *TCPListener) invokePlannedDisconnect() { func (t *TCPClient) invokePlannedDisconnect() {
//if t.Connected() { t.lock.RLock()
t.PlannedDisconnect(t) defer t.lock.RUnlock()
//} if t.plannedDisconnect != nil && t.connected {
//t.lock.RLock() go t.plannedDisconnect(t)
//defer t.lock.RUnlock() t.connected = false
//if t.plannedDisconnect != nil && t.connected { }
// go t.plannedDisconnect(t)
// t.connected = false
//}
} }
func (t *TCPListener) invokeUnexpectedDisconnect(err error) { func (t *TCPClient) invokeUnexpectedDisconnect(err error) {
//if t.Connected() { t.lock.RLock()
t.UnexpectedDisconnect(t, err) defer t.lock.RUnlock()
//} if t.unexpectedDisconnect != nil && t.connected {
//t.lock.RLock() go t.unexpectedDisconnect(t, err)
//defer t.lock.RUnlock() t.connected = false
//if t.unexpectedDisconnect != nil && t.connected { }
// go t.unexpectedDisconnect(t, err)
// t.connected = false
//}
} }
//func (t *TCPListener) getConn() net.Conn { func (t *TCPClient) getConn() net.Conn {
// t.lock.RLock() t.lock.RLock()
// defer t.lock.RUnlock() defer t.lock.RUnlock()
// return t.conn return t.conn
//} }

View File

@ -0,0 +1,32 @@
package network
type Packet struct {
SequenceId uint16
CommandName string
Payload []byte
Params RequestParams
}
type RequestParams map[string]any
func (p RequestParams) Bool(k string) bool {
if p == nil {
return false
}
i, ok := p[k]
if !ok {
return false
}
return i.(bool)
}
func (p RequestParams) Int32(k string) int32 {
if p == nil {
return 0
}
i, ok := p[k]
if !ok {
return 0
}
return i.(int32)
}

View File

@ -7,8 +7,6 @@ const (
RequestTypeSimple = 0x0B RequestTypeSimple = 0x0B
) )
var emptyKey = make([]byte, 16)
type EncryptType uint32 type EncryptType uint32
const ( const (
@ -24,7 +22,4 @@ type Request struct {
Uin int64 Uin int64
CommandName string CommandName string
Body []byte Body []byte
Params Params
Decode func(*Response) (interface{}, error) // callAndDecode use this function to decode response
} }

View File

@ -9,73 +9,70 @@ import (
) )
type Response struct { type Response struct {
Type RequestType
EncryptType EncryptType
SequenceID int32 SequenceID int32
Uin int64
CommandName string CommandName string
Body []byte Body []byte
// Request is the original request that obtained this response. Message string
Request *Request
}
func (r *Response) Params() Params { // Request is the original request that obtained this response.
if r.Request == nil { // Request *Request
return nil
}
return r.Request.Params
} }
var ( var (
ErrSessionExpired = errors.New("session expired") ErrSessionExpired = errors.New("session expired")
ErrPacketDropped = errors.New("packet dropped") ErrPacketDropped = errors.New("packet dropped")
ErrInvalidPacketType = errors.New("invalid packet type") ErrInvalidPacketType = errors.New("invalid packet type")
ErrConnectionBroken = errors.New("connection broken")
) )
func (t *Transport) ReadRequest(head []byte) (*Request, error) { func (t *Transport) ReadResponse(head []byte) (*Response, error) {
req := new(Request) resp := new(Response)
r := binary.NewReader(head) r := binary.NewReader(head)
req.Type = RequestType(r.ReadInt32()) resp.Type = RequestType(r.ReadInt32())
if req.Type != RequestTypeLogin && req.Type != RequestTypeSimple { if resp.Type != RequestTypeLogin && resp.Type != RequestTypeSimple {
return req, ErrInvalidPacketType return resp, ErrInvalidPacketType
} }
req.EncryptType = EncryptType(r.ReadByte()) resp.EncryptType = EncryptType(r.ReadByte())
_ = r.ReadByte() // 0x00? _ = r.ReadByte() // 0x00?
req.Uin, _ = strconv.ParseInt(r.ReadString(), 10, 64) resp.Uin, _ = strconv.ParseInt(r.ReadString(), 10, 64)
body := r.ReadAvailable() body := r.ReadAvailable()
switch req.EncryptType { switch resp.EncryptType {
case EncryptTypeNoEncrypt: case EncryptTypeNoEncrypt:
// nothing to do // nothing to do
case EncryptTypeD2Key: case EncryptTypeD2Key:
body = binary.NewTeaCipher(t.Sig.D2Key).Decrypt(body) body = binary.NewTeaCipher(t.Sig.D2Key).Decrypt(body)
case EncryptTypeEmptyKey: case EncryptTypeEmptyKey:
emptyKey := make([]byte, 16)
body = binary.NewTeaCipher(emptyKey).Decrypt(body) body = binary.NewTeaCipher(emptyKey).Decrypt(body)
} }
err := t.readSSOFrame(req, body) err := t.readSSOFrame(resp, body)
return req, err return resp, err
} }
func (t *Transport) readSSOFrame(req *Request, payload []byte) error { func (t *Transport) readSSOFrame(resp *Response, payload []byte) error {
reader := binary.NewReader(payload) reader := binary.NewReader(payload)
headLen := reader.ReadInt32() headLen := reader.ReadInt32()
if headLen-4 > int32(reader.Len()) { if headLen < 4 || headLen-4 > int32(reader.Len()) {
return errors.WithStack(ErrPacketDropped) return errors.WithStack(ErrPacketDropped)
} }
head := binary.NewReader(reader.ReadBytes(int(headLen) - 4)) head := binary.NewReader(reader.ReadBytes(int(headLen) - 4))
req.SequenceID = head.ReadInt32() resp.SequenceID = head.ReadInt32()
retCode := head.ReadInt32() switch retCode := head.ReadInt32(); retCode {
message := head.ReadString()
switch retCode {
case 0: case 0:
// ok // ok
case -10008: case -10008:
return errors.WithMessage(ErrSessionExpired, message) return errors.WithStack(ErrSessionExpired)
default: default:
return errors.Errorf("return code unsuccessful: %d message: %s", retCode, message) return errors.Errorf("return code unsuccessful: %d", retCode)
} }
req.CommandName = head.ReadString() resp.Message = head.ReadString()
if req.CommandName == "Heartbeat.Alive" { resp.CommandName = head.ReadString()
if resp.CommandName == "Heartbeat.Alive" {
return nil return nil
} }
_ = head.ReadInt32Bytes() // session id _ = head.ReadInt32Bytes() // session id
@ -91,6 +88,6 @@ func (t *Transport) readSSOFrame(req *Request, payload []byte) error {
case 1: case 1:
body = binary.ZlibUncompress(body) body = binary.ZlibUncompress(body)
} }
req.Body = body resp.Body = body
return nil return nil
} }

View File

@ -1,34 +0,0 @@
package network
// Call is a client-side RPC call.
// refer to `net/rpc`
type Call struct {
Request *Request
Response *Response
Err error
Done chan *Call
}
type Params map[string]interface{}
func (p Params) Bool(k string) bool {
if p == nil {
return false
}
i, ok := p[k]
if !ok {
return false
}
return i.(bool)
}
func (p Params) Int32(k string) int32 {
if p == nil {
return 0
}
i, ok := p[k]
if !ok {
return 0
}
return i.(int32)
}

View File

@ -1,74 +1,116 @@
package network package network
import ( import (
goBinary "encoding/binary"
"fmt"
"io"
"net"
"strconv" "strconv"
"sync" "strings"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/internal/auth" "github.com/Mrs4s/MiraiGo/client/internal/auth"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/wrapper"
"github.com/pkg/errors"
) )
// Transport is a network transport. // Transport is a network transport.
type Transport struct { type Transport struct {
// sessionMu sync.Mutex
Sig *auth.SigInfo Sig *auth.SigInfo
Version *auth.AppVersion Version *auth.AppVersion
Device *auth.Device Device *auth.Device
// connection // connection
connMu sync.Mutex // conn *TCPClient
servers []*net.TCPAddr
curServerAddr *net.TCPAddr
conn TCPListener
} }
func (t *Transport) AddServerAddr(addr *net.TCPAddr) { var WhiteListCommands = `
t.connMu.Lock() ConnAuthSvr.fast_qq_login
defer t.connMu.Unlock() ConnAuthSvr.sdk_auth_api
t.servers = append(t.servers, addr) ConnAuthSvr.sdk_auth_api_emp
} FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoBarrage
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoComment
func (t *Transport) GetServerAddr() *net.TCPAddr { FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoFollow
t.connMu.Lock() FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoLike
defer t.connMu.Unlock() FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoPush
return t.curServerAddr FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoReply
} FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.PublishFeed
FeedCloudSvr.trpc.videocircle.circleprofile.CircleProfile.SetProfile
func (t *Transport) ServerCount() int { friendlist.addFriend
t.connMu.Lock() friendlist.AddFriendReq
defer t.connMu.Unlock() friendlist.ModifyGroupInfoReq
return len(t.servers) MessageSvc.PbSendMsg
} MsgProxy.SendMsg
OidbSvc.0x4ff_9
func (t *Transport) PlannedDisconnect(fun func(*TCPListener)) { OidbSvc.0x4ff_9_IMCore
t.conn.PlannedDisconnect = fun OidbSvc.0x56c_6
} OidbSvc.0x6d9_4
OidbSvc.0x758
func (t *Transport) UnexpectedDisconnect(fun func(*TCPListener, error)) { OidbSvc.0x758_0
t.conn.UnexpectedDisconnect = fun OidbSvc.0x758_1
} OidbSvc.0x88d_0
OidbSvc.0x89a_0
func (t *Transport) ConnectFastest() (chosen *net.TCPAddr, err error) { OidbSvc.0x89b_1
t.connMu.Lock() OidbSvc.0x8a1_0
defer t.connMu.Unlock() OidbSvc.0x8a1_7
chosen, err = t.conn.ConnectFastest(t.servers) OidbSvc.0x8ba
t.curServerAddr = chosen OidbSvc.0x9fa
return OidbSvc.oidb_0x758
} OidbSvcTrpcTcp.0x101e_1
OidbSvcTrpcTcp.0x101e_2
func (t *Transport) Close() { OidbSvcTrpcTcp.0x1100_1
t.conn.Close() OidbSvcTrpcTcp.0x1105_1
} OidbSvcTrpcTcp.0x1107_1
OidbSvcTrpcTcp.0x55f_0
func (t *Transport) Write(data []byte) error { OidbSvcTrpcTcp.0x6d9_4
return t.conn.Write(data) OidbSvcTrpcTcp.0xf55_1
} OidbSvcTrpcTcp.0xf57_1
OidbSvcTrpcTcp.0xf57_106
OidbSvcTrpcTcp.0xf57_9
OidbSvcTrpcTcp.0xf65_1
OidbSvcTrpcTcp.0xf65_10
OidbSvcTrpcTcp.0xf67_1
OidbSvcTrpcTcp.0xf67_5
OidbSvcTrpcTcp.0xf6e_1
OidbSvcTrpcTcp.0xf88_1
OidbSvcTrpcTcp.0xf89_1
OidbSvcTrpcTcp.0xfa5_1
ProfileService.getGroupInfoReq
ProfileService.GroupMngReq
QChannelSvr.trpc.qchannel.commwriter.ComWriter.DoComment
QChannelSvr.trpc.qchannel.commwriter.ComWriter.DoReply
QChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed
qidianservice.135
qidianservice.207
qidianservice.269
qidianservice.290
SQQzoneSvc.addComment
SQQzoneSvc.addReply
SQQzoneSvc.forward
SQQzoneSvc.like
SQQzoneSvc.publishmood
SQQzoneSvc.shuoshuo
trpc.group_pro.msgproxy.sendmsg
trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLoginUnusualDevice
trpc.o3.ecdh_access.EcdhAccess.SsoEstablishShareKey
trpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Access
trpc.o3.ecdh_access.EcdhAccess.SsoSecureA2Establish
trpc.o3.ecdh_access.EcdhAccess.SsoSecureAccess
trpc.o3.report.Report.SsoReport
trpc.passwd.manager.PasswdManager.SetPasswd
trpc.passwd.manager.PasswdManager.VerifyPasswd
trpc.qlive.relationchain_svr.RelationchainSvr.Follow
trpc.qlive.word_svr.WordSvr.NewPublicChat
trpc.qqhb.qqhb_proxy.Handler.sso_handle
trpc.springfestival.redpacket.LuckyBag.SsoSubmitGrade
wtlogin.device_lock
wtlogin.exchange_emp
wtlogin.login
wtlogin.name2uin
wtlogin.qrlogin
wtlogin.register
wtlogin.trans_emp
wtlogin_device.login
wtlogin_device.tran_sim_emp
`
func (t *Transport) packBody(req *Request, w *binary.Writer) { func (t *Transport) packBody(req *Request, w *binary.Writer) {
pos := w.FillUInt32() pos := w.FillUInt32()
@ -89,19 +131,58 @@ func (t *Transport) packBody(req *Request, w *binary.Writer) {
w.WriteUInt32(uint32(len(t.Sig.OutPacketSessionID) + 4)) w.WriteUInt32(uint32(len(t.Sig.OutPacketSessionID) + 4))
w.Write(t.Sig.OutPacketSessionID) w.Write(t.Sig.OutPacketSessionID)
if req.Type == RequestTypeLogin { if req.Type == RequestTypeLogin {
w.WriteString(t.Device.IMEI) w.WriteString((*t.Device).IMEI)
w.WriteUInt32(0x04) w.WriteUInt32(0x04)
w.WriteUInt16(uint16(len(t.Sig.Ksid)) + 2) w.WriteUInt16(uint16(len(t.Sig.Ksid)) + 2)
w.Write(t.Sig.Ksid) w.Write(t.Sig.Ksid)
} }
w.WriteUInt32(0x04) if strings.Contains(WhiteListCommands, req.CommandName) {
secSign := t.PackSecSign(req)
w.WriteUInt32(uint32(len(secSign) + 4))
w.Write(secSign)
}
w.WriteUInt32(0x04 + uint32(len(t.Device.QImei16)))
w.Write([]byte(t.Device.QImei16))
w.WriteUInt32At(pos, uint32(w.Len()-pos)) w.WriteUInt32At(pos, uint32(w.Len()-pos))
w.WriteUInt32(uint32(len(req.Body) + 4)) w.WriteUInt32(uint32(len(req.Body) + 4))
w.Write(req.Body) w.Write(req.Body)
} }
func (t *Transport) PackSecSign(req *Request) []byte {
if wrapper.FekitGetSign == nil {
return []byte{}
}
sign, extra, token, err := wrapper.FekitGetSign(uint64(req.SequenceID), strconv.FormatInt(req.Uin, 10), req.CommandName, t.Version.QUA, req.Body)
if err != nil {
return []byte{}
}
m := &pb.SSOReserveField{
Flag: 0,
Qimei: t.Device.QImei16,
NewconnFlag: 0,
Uid: strconv.FormatInt(req.Uin, 10),
Imsi: 0,
NetworkType: 1,
IpStackType: 1,
MessageType: 0,
SecInfo: &pb.SsoSecureInfo{
SecSig: sign,
SecDeviceToken: token,
SecExtra: extra,
},
SsoIpOrigin: 0,
}
data, err := proto.Marshal(m)
if err != nil {
panic(errors.Wrap(err, "failed to unmarshal protobuf SSOReserveField"))
}
return data
}
// PackPacket packs a packet. // PackPacket packs a packet.
func (t *Transport) PackPacket(req *Request) []byte { func (t *Transport) PackPacket(req *Request) []byte {
// todo(wdvxdr): combine pack packet, send packet and return the response // todo(wdvxdr): combine pack packet, send packet and return the response
@ -140,6 +221,7 @@ func (t *Transport) PackPacket(req *Request) []byte {
case EncryptTypeD2Key: case EncryptTypeD2Key:
body = binary.NewTeaCipher(t.Sig.D2Key).Encrypt(body) body = binary.NewTeaCipher(t.Sig.D2Key).Encrypt(body)
case EncryptTypeEmptyKey: case EncryptTypeEmptyKey:
emptyKey := make([]byte, 16)
body = binary.NewTeaCipher(emptyKey).Encrypt(body) body = binary.NewTeaCipher(emptyKey).Encrypt(body)
} }
w.Write(body) w.Write(body)
@ -148,59 +230,3 @@ func (t *Transport) PackPacket(req *Request) []byte {
w.WriteUInt32At(pos, uint32(w.Len())) w.WriteUInt32At(pos, uint32(w.Len()))
return append([]byte(nil), w.Bytes()...) return append([]byte(nil), w.Bytes()...)
} }
type PktHandler func(pkt *Request, netErr error)
type RequestHandler func(head []byte) (*Request, error)
func (t *Transport) NetLoop(pktHandler PktHandler, respHandler RequestHandler) {
go t.netLoop(pktHandler, respHandler)
}
// readPacket 帮助函数(Helper function)
func readPacket(conn *net.TCPConn, minSize, maxSize uint32) ([]byte, error) {
lBuf := make([]byte, 4)
_, err := io.ReadFull(conn, lBuf)
if err != nil {
return nil, err
}
l := goBinary.BigEndian.Uint32(lBuf)
if l < minSize || l > maxSize {
return nil, fmt.Errorf("parse incoming packet error: invalid packet length %v", l)
}
data := make([]byte, l-4)
_, err = io.ReadFull(conn, data)
return data, err
}
// netLoop 整个函数周期使用同一个连接,确保不会发生串线这种奇怪的事情
func (t *Transport) netLoop(pktHandler PktHandler, respHandler RequestHandler) {
conn := t.conn.getConn()
defer func() {
if r := recover(); r != nil {
pktHandler(nil, fmt.Errorf("panic: %v", r))
}
t.conn.Close()
}()
errCount := 0
for {
data, err := readPacket(conn, 4, 10<<20) // max 10MB
if err != nil {
// 在且仅在没有新连接建立时断线才被认为是意外的
if t.conn.getConn() == conn {
pktHandler(nil, errors.Wrap(ErrConnectionBroken, err.Error()))
}
return
}
req, err := respHandler(data)
if err == nil {
errCount = 0
goto ok
}
errCount++
if errCount > 2 {
err = errors.Wrap(ErrConnectionBroken, err.Error())
}
ok:
go pktHandler(req, err)
}
}

View File

@ -1,7 +1,7 @@
package crypto package oicq
import ( import (
"crypto/elliptic" "crypto/ecdh"
"crypto/md5" "crypto/md5"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
@ -10,37 +10,24 @@ import (
"strconv" "strconv"
) )
type ECDH struct { // session is ecdh session in oicq.
type session struct {
SvrPublicKeyVer uint16 SvrPublicKeyVer uint16
PublicKey []byte PublicKey []byte
ShareKey []byte ShareKey []byte
} }
type EncryptSession struct {
T133 []byte
}
const serverPublicKey = "04EBCA94D733E399B2DB96EACDD3F69A8BB0F74224E2B44E3357812211D2E62EFBC91BB553098E25E33A799ADC7F76FEB208DA7C6522CDB0719A305180CC54A82E" const serverPublicKey = "04EBCA94D733E399B2DB96EACDD3F69A8BB0F74224E2B44E3357812211D2E62EFBC91BB553098E25E33A799ADC7F76FEB208DA7C6522CDB0719A305180CC54A82E"
func NewECDH() *ECDH { func newSession() *session {
e := &ECDH{ e := &session{
SvrPublicKeyVer: 1, SvrPublicKeyVer: 1,
} }
e.generateKey(serverPublicKey) key, _ := hex.DecodeString(serverPublicKey)
e.init(key)
return e return e
} }
func (e *ECDH) generateKey(sPubKey string) {
pub, _ := hex.DecodeString(sPubKey)
p256 := elliptic.P256()
key, sx, sy, _ := elliptic.GenerateKey(p256, rand.Reader)
tx, ty := elliptic.Unmarshal(p256, pub)
x, _ := p256.ScalarMult(tx, ty, key)
hash := md5.Sum(x.Bytes()[:16])
e.ShareKey = hash[:]
e.PublicKey = elliptic.Marshal(p256, sx, sy)
}
type pubKeyResp struct { type pubKeyResp struct {
Meta struct { Meta struct {
PubKeyVer uint16 `json:"KeyVer"` PubKeyVer uint16 `json:"KeyVer"`
@ -48,8 +35,8 @@ type pubKeyResp struct {
} `json:"PubKeyMeta"` } `json:"PubKeyMeta"`
} }
// FetchPubKey 从服务器获取PubKey // fetchPubKey 从服务器获取PubKey
func (e *ECDH) FetchPubKey(uin int64) { func (e *session) fetchPubKey(uin int64) {
resp, err := http.Get("https://keyrotate.qq.com/rotate_key?cipher_suite_ver=305&uin=" + strconv.FormatInt(uin, 10)) resp, err := http.Get("https://keyrotate.qq.com/rotate_key?cipher_suite_ver=305&uin=" + strconv.FormatInt(uin, 10))
if err != nil { if err != nil {
return return
@ -61,5 +48,18 @@ func (e *ECDH) FetchPubKey(uin int64) {
return return
} }
e.SvrPublicKeyVer = pubKey.Meta.PubKeyVer e.SvrPublicKeyVer = pubKey.Meta.PubKeyVer
e.generateKey(pubKey.Meta.PubKey) // todo check key sign key, _ := hex.DecodeString(pubKey.Meta.PubKey)
e.init(key) // todo check key sign
}
func (e *session) init(svrPubKey []byte) {
p256 := ecdh.P256()
local, _ := p256.GenerateKey(rand.Reader)
remote, _ := p256.NewPublicKey(svrPubKey)
share, _ := local.ECDH(remote)
hash := md5.New()
hash.Write(share[:16])
e.ShareKey = hash.Sum(nil)
e.PublicKey = local.PublicKey().Bytes()
} }

View File

@ -1,17 +1,16 @@
package oicq package oicq
import ( import (
"crypto/rand"
goBinary "encoding/binary" goBinary "encoding/binary"
"math/rand"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/internal/crypto"
) )
type Codec struct { type Codec struct {
ecdh *crypto.ECDH ecdh *session
randomKey []byte randomKey []byte
WtSessionTicketKey []byte WtSessionTicketKey []byte
@ -19,11 +18,11 @@ type Codec struct {
func NewCodec(uin int64) *Codec { func NewCodec(uin int64) *Codec {
c := &Codec{ c := &Codec{
ecdh: crypto.NewECDH(), ecdh: newSession(),
randomKey: make([]byte, 16), randomKey: make([]byte, 16),
} }
rand.Read(c.randomKey) rand.Read(c.randomKey)
c.ecdh.FetchPubKey(uin) c.ecdh.fetchPubKey(uin)
return c return c
} }
@ -84,7 +83,8 @@ func (c *Codec) Marshal(m *Message) []byte {
} }
w.WriteByte(0x03) w.WriteByte(0x03)
buf := append([]byte(nil), w.Bytes()...) buf := make([]byte, len(w.Bytes()))
copy(buf, w.Bytes())
goBinary.BigEndian.PutUint16(buf[1:3], uint16(len(buf))) goBinary.BigEndian.PutUint16(buf[1:3], uint16(len(buf)))
return buf return buf
} }
@ -110,16 +110,13 @@ func (c *Codec) Unmarshal(data []byte) (*Message, error) {
reader.ReadByte() reader.ReadByte()
switch encryptType { switch encryptType {
case 0: case 0:
m.Body = func() (decrypted []byte) { d := reader.ReadBytes(reader.Len() - 1)
d := reader.ReadBytes(reader.Len() - 1) defer func() {
defer func() { if pan := recover(); pan != nil {
if pan := recover(); pan != nil { m.Body = binary.NewTeaCipher(c.randomKey).Decrypt(d)
tea := binary.NewTeaCipher(c.randomKey) }
decrypted = tea.Decrypt(d)
}
}()
return binary.NewTeaCipher(c.ecdh.ShareKey).Decrypt(d)
}() }()
m.Body = binary.NewTeaCipher(c.ecdh.ShareKey).Decrypt(d)
case 3: case 3:
d := reader.ReadBytes(reader.Len() - 1) d := reader.ReadBytes(reader.Len() - 1)
m.Body = binary.NewTeaCipher(c.WtSessionTicketKey).Decrypt(d) m.Body = binary.NewTeaCipher(c.WtSessionTicketKey).Decrypt(d)
@ -128,3 +125,25 @@ func (c *Codec) Unmarshal(data []byte) (*Message, error) {
} }
return m, nil return m, nil
} }
type TLV struct {
Command uint16
List [][]byte
}
func (t *TLV) Marshal() []byte {
w := binary.SelectWriter()
defer binary.PutWriter(w)
w.WriteUInt16(t.Command)
w.WriteUInt16(uint16(len(t.List)))
for _, elem := range t.List {
w.Write(elem)
}
return append([]byte(nil), w.Bytes()...)
}
func (t *TLV) Append(b ...[]byte) {
t.List = append(t.List, b...)
}

View File

@ -0,0 +1,105 @@
package tlv
import (
"encoding/binary"
"github.com/pkg/errors"
)
// Record represents a Tag-Length-Value record.
type Record struct {
Tag int
Length int
Value []byte
}
type RecordMap map[int][]byte
func (rm RecordMap) Exists(key int) bool {
_, ok := rm[key]
return ok
}
var ErrMessageTooShort = errors.New("tlv: message too short")
// Decoder is a configurable TLV decoder.
type Decoder struct {
tagSize uint8
lenSize uint8
headSize uint8
}
func NewDecoder(tagSize, lenSize uint8) *Decoder {
check := func(t string, s uint8) {
switch s {
case 1, 2, 4:
// ok
default:
panic("invalid " + t)
}
}
check("tag size", tagSize)
check("len size", lenSize)
return &Decoder{tagSize: tagSize, lenSize: lenSize, headSize: tagSize + lenSize}
}
func (d *Decoder) decodeRecord(data []byte) (r Record, err error) {
tagSize := d.tagSize
lenSize := d.lenSize
headSize := int(tagSize + lenSize)
if len(data) < headSize {
err = ErrMessageTooShort
return
}
r.Tag = d.read(tagSize, data)
r.Length = d.read(lenSize, data[tagSize:])
if len(data) < headSize+r.Length {
err = ErrMessageTooShort
return
}
r.Value = data[headSize : headSize+r.Length : headSize+r.Length]
return
}
func (d *Decoder) read(size uint8, data []byte) int {
switch size {
case 1:
return int(data[0])
case 2:
return int(binary.BigEndian.Uint16(data))
case 4:
return int(binary.BigEndian.Uint32(data))
default:
panic("invalid size")
}
}
func (d *Decoder) Decode(data []byte) ([]Record, error) {
var records []Record
for len(data) > 0 {
r, err := d.decodeRecord(data)
if err != nil {
return nil, err
}
records = append(records, r)
data = data[int(d.headSize)+r.Length:]
}
return records, nil
}
func (d *Decoder) DecodeRecordMap(data []byte) (RecordMap, error) {
records, err := d.Decode(data)
if err != nil {
return nil, err
}
rm := make(RecordMap, len(records))
for _, record := range records {
rm[record.Tag] = record.Value
}
return rm, nil
}

43
client/log.go Normal file
View File

@ -0,0 +1,43 @@
package client
type Logger interface {
Info(format string, args ...any)
Warning(format string, args ...any)
Error(format string, args ...any)
Debug(format string, args ...any)
Dump(dumped []byte, format string, args ...any)
}
func (c *QQClient) SetLogger(logger Logger) {
c.logger = logger
}
func (c *QQClient) info(msg string, args ...any) {
if c.logger != nil {
c.logger.Info(msg, args...)
}
}
func (c *QQClient) warning(msg string, args ...any) {
if c.logger != nil {
c.logger.Warning(msg, args...)
}
}
func (c *QQClient) error(msg string, args ...any) {
if c.logger != nil {
c.logger.Error(msg, args...)
}
}
func (c *QQClient) debug(msg string, args ...any) {
if c.logger != nil {
c.logger.Debug(msg, args...)
}
}
func (c *QQClient) dump(msg string, data []byte, args ...any) {
if c.logger != nil {
c.logger.Dump(data, msg, args...)
}
}

View File

@ -0,0 +1,43 @@
// Code generated by "stringer -type=LoginError"; DO NOT EDIT.
package client
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[NeedCaptcha-1]
_ = x[OtherLoginError-3]
_ = x[UnsafeDeviceError-4]
_ = x[SMSNeededError-5]
_ = x[TooManySMSRequestError-6]
_ = x[SMSOrVerifyNeededError-7]
_ = x[SliderNeededError-8]
_ = x[UnknownLoginError - -1]
}
const (
_LoginError_name_0 = "UnknownLoginError"
_LoginError_name_1 = "NeedCaptcha"
_LoginError_name_2 = "OtherLoginErrorUnsafeDeviceErrorSMSNeededErrorTooManySMSRequestErrorSMSOrVerifyNeededErrorSliderNeededError"
)
var (
_LoginError_index_2 = [...]uint8{0, 15, 32, 46, 68, 90, 107}
)
func (i LoginError) String() string {
switch {
case i == -1:
return _LoginError_name_0
case i == 1:
return _LoginError_name_1
case 3 <= i && i <= 8:
i -= 3
return _LoginError_name_2[_LoginError_index_2[i]:_LoginError_index_2[i+1]]
default:
return "LoginError(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

View File

@ -48,9 +48,8 @@ func (c *QQClient) getGtk(domain string) int {
accu = accu + (accu << 5) + int(b) accu = accu + (accu << 5) + int(b)
} }
return 2147483647 & accu return 2147483647 & accu
} else {
return 0
} }
return 0
} }
func (c *QQClient) GetModelShow(modelName string) ([]*ModelVariant, error) { func (c *QQClient) GetModelShow(modelName string) ([]*ModelVariant, error) {
@ -60,7 +59,7 @@ func (c *QQClient) GetModelShow(modelName string) ([]*ModelVariant, error) {
Uin: c.Uin, Uin: c.Uin,
Model: strings.ReplaceAll(url.QueryEscape(modelName), "+", "%20"), Model: strings.ReplaceAll(url.QueryEscape(modelName), "+", "%20"),
AppType: 0, AppType: 0,
IMei: c.deviceInfo.IMEI, IMei: c.Device().IMEI,
ShowInfo: true, ShowInfo: true,
ModelShow: "", ModelShow: "",
RecoverDefault: false, RecoverDefault: false,
@ -97,7 +96,7 @@ func (c *QQClient) SetModelShow(modelName string, modelShow string) error {
Uin: c.Uin, Uin: c.Uin,
Model: strings.ReplaceAll(url.QueryEscape(modelName), "+", "%20"), Model: strings.ReplaceAll(url.QueryEscape(modelName), "+", "%20"),
AppType: 0, AppType: 0,
IMei: c.deviceInfo.IMEI, IMei: c.Device().IMEI,
ShowInfo: true, ShowInfo: true,
ModelShow: strings.ReplaceAll(url.QueryEscape(modelShow), "+", "%20"), ModelShow: strings.ReplaceAll(url.QueryEscape(modelShow), "+", "%20"),
RecoverDefault: modelShow == "", RecoverDefault: modelShow == "",

View File

@ -1,13 +1,19 @@
package client package client
import ( import (
"bytes"
"crypto/md5"
"fmt" "fmt"
"math" "math"
"math/rand"
"strconv"
"strings"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/internal/highway"
"github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/longmsg" "github.com/Mrs4s/MiraiGo/client/pb/longmsg"
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
@ -17,8 +23,13 @@ import (
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
func init() {
decoders["MultiMsg.ApplyUp"] = decodeMultiApplyUpResponse
decoders["MultiMsg.ApplyDown"] = decodeMultiApplyDownResponse
}
// MultiMsg.ApplyUp // MultiMsg.ApplyUp
func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) *network.Request { func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) (uint16, []byte) {
req := &multimsg.MultiReqBody{ req := &multimsg.MultiReqBody{
Subcmd: 1, Subcmd: 1,
TermType: 5, TermType: 5,
@ -36,13 +47,13 @@ func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, grou
BuType: buType, BuType: buType,
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
return c.uniRequest("MultiMsg.ApplyUp", payload, decodeMultiApplyUpResponse) return c.uniPacket("MultiMsg.ApplyUp", payload)
} }
// MultiMsg.ApplyUp // MultiMsg.ApplyUp
func decodeMultiApplyUpResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeMultiApplyUpResponse(_ *QQClient, pkt *network.Packet) (any, error) {
body := multimsg.MultiRspBody{} body := multimsg.MultiRspBody{}
if err := proto.Unmarshal(resp.Body, &body); err != nil { if err := proto.Unmarshal(pkt.Payload, &body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if len(body.MultimsgApplyupRsp) == 0 { if len(body.MultimsgApplyupRsp) == 0 {
@ -59,7 +70,7 @@ func decodeMultiApplyUpResponse(_ *QQClient, resp *network.Response) (interface{
} }
// MultiMsg.ApplyDown // MultiMsg.ApplyDown
func (c *QQClient) buildMultiApplyDownPacket(resID string) *network.Request { func (c *QQClient) buildMultiApplyDownPacket(resID string) (uint16, []byte) {
req := &multimsg.MultiReqBody{ req := &multimsg.MultiReqBody{
Subcmd: 2, Subcmd: 2,
TermType: 5, TermType: 5,
@ -76,25 +87,33 @@ func (c *QQClient) buildMultiApplyDownPacket(resID string) *network.Request {
ReqChannelType: 2, ReqChannelType: 2,
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
return c.uniRequest("MultiMsg.ApplyDown", payload, decodeMultiApplyDownResponse) return c.uniPacket("MultiMsg.ApplyDown", payload)
} }
// MultiMsg.ApplyDown // MultiMsg.ApplyDown
func decodeMultiApplyDownResponse(_ *QQClient, resp *network.Response) (interface{}, error) { func decodeMultiApplyDownResponse(_ *QQClient, pkt *network.Packet) (any, error) {
body := multimsg.MultiRspBody{} body := multimsg.MultiRspBody{}
if err := proto.Unmarshal(resp.Body, &body); err != nil { if err := proto.Unmarshal(pkt.Payload, &body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
} }
if len(body.MultimsgApplydownRsp) == 0 { if len(body.MultimsgApplydownRsp) == 0 {
return nil, errors.New("not found") return nil, errors.New("message not found")
} }
rsp := body.MultimsgApplydownRsp[0] rsp := body.MultimsgApplydownRsp[0]
if rsp.ThumbDownPara == nil {
return nil, errors.New("message not found")
}
var prefix string var prefix string
if rsp.MsgExternInfo != nil && rsp.MsgExternInfo.ChannelType == 2 { if rsp.MsgExternInfo != nil && rsp.MsgExternInfo.ChannelType == 2 {
prefix = "https://ssl.htdata.qq.com" prefix = "https://ssl.htdata.qq.com"
} else { } else {
prefix = fmt.Sprintf("http://%s:%d", binary.UInt32ToIPV4Address(uint32(rsp.Uint32DownIp[0])), body.MultimsgApplydownRsp[0].Uint32DownPort[0]) ma := body.MultimsgApplydownRsp[0]
if len(rsp.Uint32DownIp) == 0 || len(ma.Uint32DownPort) == 0 {
return nil, errors.New("message not found")
}
prefix = fmt.Sprintf("http://%s:%d", binary.UInt32ToIPV4Address(uint32(rsp.Uint32DownIp[0])), ma.Uint32DownPort[0])
} }
b, err := utils.HttpGetBytes(fmt.Sprintf("%s%s", prefix, string(rsp.ThumbDownPara)), "") b, err := utils.HttpGetBytes(fmt.Sprintf("%s%s", prefix, string(rsp.ThumbDownPara)), "")
if err != nil { if err != nil {
@ -136,11 +155,11 @@ func (l *forwardMsgLinker) link(name string) *message.ForwardMessage {
if item == nil { if item == nil {
return nil return nil
} }
nodes := make([]*message.ForwardNode, 0, len(item.GetBuffer().GetMsg())) nodes := make([]*message.ForwardNode, 0, len(item.Buffer.Msg))
for _, m := range item.GetBuffer().GetMsg() { for _, m := range item.Buffer.Msg {
name := m.Head.GetFromNick() name := m.Head.FromNick.Unwrap()
if m.Head.GetMsgType() == 82 && m.Head.GroupInfo != nil { if m.Head.MsgType.Unwrap() == 82 && m.Head.GroupInfo != nil {
name = m.Head.GroupInfo.GetGroupCard() name = m.Head.GroupInfo.GroupCard.Unwrap()
} }
msgElems := message.ParseMessageElems(m.Body.RichText.Elems) msgElems := message.ParseMessageElems(m.Body.RichText.Elems)
@ -152,10 +171,15 @@ func (l *forwardMsgLinker) link(name string) *message.ForwardMessage {
} }
} }
gid := int64(0) // 给群号一个缺省值0防止在读合并转发的私聊内容时候会报错
if m.Head.GroupInfo != nil {
gid = m.Head.GroupInfo.GroupCode.Unwrap()
}
nodes = append(nodes, &message.ForwardNode{ nodes = append(nodes, &message.ForwardNode{
SenderId: m.Head.GetFromUin(), GroupId: gid,
SenderId: m.Head.FromUin.Unwrap(),
SenderName: name, SenderName: name,
Time: m.Head.GetMsgTime(), Time: m.Head.MsgTime.Unwrap(),
Message: msgElems, Message: msgElems,
}) })
} }
@ -171,39 +195,131 @@ func (c *QQClient) GetForwardMessage(resID string) *message.ForwardMessage {
items: make(map[string]*msg.PbMultiMsgItem), items: make(map[string]*msg.PbMultiMsgItem),
} }
for _, item := range m.Items { for _, item := range m.Items {
linker.items[item.GetFileName()] = item linker.items[item.FileName.Unwrap()] = item
} }
return linker.link(m.FileName) return linker.link("MultiMsg")
} }
func (c *QQClient) DownloadForwardMessage(resId string) *message.ForwardElement { func (c *QQClient) DownloadForwardMessage(resId string) *message.ForwardElement {
i, err := c.callAndDecode(c.buildMultiApplyDownPacket(resId)) i, err := c.sendAndWait(c.buildMultiApplyDownPacket(resId))
if err != nil { if err != nil {
return nil return nil
} }
multiMsg := i.(*msg.PbMultiMsgTransmit) multiMsg := i.(*msg.PbMultiMsgTransmit)
if multiMsg.GetPbItemList() == nil { if multiMsg.PbItemList == nil {
return nil return nil
} }
var pv string var pv bytes.Buffer
for i := 0; i < int(math.Min(4, float64(len(multiMsg.GetMsg())))); i++ { for i := 0; i < int(math.Min(4, float64(len(multiMsg.Msg)))); i++ {
m := multiMsg.Msg[i] m := multiMsg.Msg[i]
pv += fmt.Sprintf(`<title size="26" color="#777777">%s: %s</title>`, sender := m.Head.FromNick.Unwrap()
func() string { if m.Head.MsgType.Unwrap() == 82 && m.Head.GroupInfo != nil {
if m.Head.GetMsgType() == 82 && m.Head.GroupInfo != nil { sender = m.Head.GroupInfo.GroupCard.Unwrap()
return m.Head.GroupInfo.GetGroupCard() }
} brief := message.ToReadableString(message.ParseMessageElems(multiMsg.Msg[i].Body.RichText.Elems))
return m.Head.GetFromNick() fmt.Fprintf(&pv, `<title size="26" color="#777777">%s: %s</title>`, sender, brief)
}(),
message.ToReadableString(
message.ParseMessageElems(multiMsg.Msg[i].GetBody().GetRichText().Elems),
),
)
} }
return genForwardTemplate( return genForwardTemplate(
resId, pv, "群聊的聊天记录", "[聊天记录]", "聊天记录", resId, pv.String(),
fmt.Sprintf("查看 %d 条转发消息", len(multiMsg.GetMsg())), fmt.Sprintf("查看 %d 条转发消息", len(multiMsg.Msg)),
time.Now().UnixNano(), time.Now().UnixNano(),
multiMsg.GetPbItemList(), multiMsg.PbItemList,
) )
} }
func forwardDisplay(resID, fileName, preview, summary string) string {
sb := strings.Builder{}
sb.WriteString(`<?xml version='1.0' encoding='UTF-8'?><msg serviceID="35" templateID="1" action="viewMultiMsg" brief="[聊天记录]" `)
if resID != "" {
sb.WriteString(`m_resid="`)
sb.WriteString(resID)
sb.WriteString("\" ")
}
sb.WriteString(`m_fileName="`)
sb.WriteString(fileName)
sb.WriteString(`" tSum="3" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0"><item layout="1"><title color="#000000" size="34">群聊的聊天记录</title> `)
sb.WriteString(preview)
sb.WriteString(`<hr></hr><summary size="26" color="#808080">`)
sb.WriteString(summary)
// todo: 私聊的聊天记录?
sb.WriteString(`</summary></item><source name="聊天记录"></source></msg>`)
return sb.String()
}
func (c *QQClient) NewForwardMessageBuilder(groupCode int64) *ForwardMessageBuilder {
return &ForwardMessageBuilder{
c: c,
groupCode: groupCode,
}
}
type ForwardMessageBuilder struct {
c *QQClient
groupCode int64
objs []*msg.PbMultiMsgItem
}
// NestedNode 返回一个嵌套转发节点,其内容将会被 Builder 重定位
func (builder *ForwardMessageBuilder) NestedNode() *message.ForwardElement {
filename := strconv.FormatInt(time.Now().UnixNano(), 10) // 大概率不会重复
return &message.ForwardElement{FileName: filename}
}
// Link 将真实的消息内容填充 reloc
func (builder *ForwardMessageBuilder) Link(reloc *message.ForwardElement, fmsg *message.ForwardMessage) {
seq := builder.c.nextGroupSeq()
m := fmsg.PackForwardMessage(seq, rand.Int31(), builder.groupCode)
builder.objs = append(builder.objs, &msg.PbMultiMsgItem{
FileName: proto.String(reloc.FileName),
Buffer: &msg.PbMultiMsgNew{
Msg: m,
},
})
reloc.Content = forwardDisplay("", reloc.FileName, fmsg.Preview(), fmt.Sprintf("查看 %d 条转发消息", fmsg.Length()))
}
// Main 最外层的转发消息, 调用该方法后即上传消息
func (builder *ForwardMessageBuilder) Main(m *message.ForwardMessage) *message.ForwardElement {
if m.Length() > 200 {
return nil
}
c := builder.c
seq := c.nextGroupSeq()
fm := m.PackForwardMessage(seq, rand.Int31(), builder.groupCode)
const filename = "MultiMsg"
builder.objs = append(builder.objs, &msg.PbMultiMsgItem{
FileName: proto.String(filename),
Buffer: &msg.PbMultiMsgNew{
Msg: fm,
},
})
trans := &msg.PbMultiMsgTransmit{
Msg: fm,
PbItemList: builder.objs,
}
b, _ := proto.Marshal(trans)
data := binary.GZipCompress(b)
hash := md5.Sum(data)
rsp, body, err := c.multiMsgApplyUp(builder.groupCode, data, hash[:], 2)
if err != nil {
return nil
}
content := forwardDisplay(rsp.MsgResid, utils.RandomString(32), m.Preview(), fmt.Sprintf("查看 %d 条转发消息", m.Length()))
bodyHash := md5.Sum(body)
input := highway.Transaction{
CommandID: 27,
Ticket: rsp.MsgSig,
Body: bytes.NewReader(body),
Sum: bodyHash[:],
Size: int64(len(body)),
}
_, err = c.highwaySession.Upload(input)
if err != nil {
return nil
}
return &message.ForwardElement{
FileName: filename,
Content: content,
ResId: rsp.MsgResid,
}
}

View File

@ -2,16 +2,17 @@ package client
import ( import (
"net" "net"
"net/netip"
"runtime/debug" "runtime/debug"
"sort"
"sync" "sync"
"time" "time"
"github.com/Mrs4s/MiraiGo/message"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq" "github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
@ -40,34 +41,36 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo {
r := &ConnectionQualityInfo{} r := &ConnectionQualityInfo{}
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(2) wg.Add(2)
currentServerAddr := c.servers[c.currServerIndex].String()
go func() { go func() {
defer wg.Done() defer wg.Done()
var err error var err error
if r.ChatServerLatency, err = qualityTest(c.transport.GetServerAddr().String()); err != nil { if r.ChatServerLatency, err = qualityTest(currentServerAddr); err != nil {
c.Error("test chat server latency error: %v", err) c.error("test chat server latency error: %v", err)
r.ChatServerLatency = 9999 r.ChatServerLatency = 9999
} }
if addr, err := net.ResolveIPAddr("ip", "ssl.htdata.qq.com"); err == nil { if addr, err := net.ResolveIPAddr("ip", "ssl.htdata.qq.com"); err == nil {
if r.LongMessageServerLatency, err = qualityTest((&net.TCPAddr{IP: addr.IP, Port: 443}).String()); err != nil { if r.LongMessageServerLatency, err = qualityTest((&net.TCPAddr{IP: addr.IP, Port: 443}).String()); err != nil {
c.Error("test long message server latency error: %v", err) c.error("test long message server latency error: %v", err)
r.LongMessageServerLatency = 9999 r.LongMessageServerLatency = 9999
} }
} else { } else {
c.Error("resolve long message server error: %v", err) c.error("resolve long message server error: %v", err)
r.LongMessageServerLatency = 9999 r.LongMessageServerLatency = 9999
} }
if c.highwaySession.AddrLength() > 0 { if c.highwaySession.AddrLength() > 0 {
if r.SrvServerLatency, err = qualityTest(c.highwaySession.SsoAddr[0].String()); err != nil { if r.SrvServerLatency, err = qualityTest(c.highwaySession.SsoAddr[0].String()); err != nil {
c.Error("test srv server latency error: %v", err) c.error("test srv server latency error: %v", err)
r.SrvServerLatency = 9999 r.SrvServerLatency = 9999
} }
} }
}() }()
go func() { go func() {
defer wg.Done() defer wg.Done()
res := utils.RunTCPPingLoop(c.transport.GetServerAddr().String(), 10) res := utils.RunTCPPingLoop(currentServerAddr, 10)
r.ChatServerPacketLoss = res.PacketsLoss r.ChatServerPacketLoss = res.PacketsLoss
if c.highwaySession.AddrLength() > 0 { if c.highwaySession.AddrLength() > 0 {
res = utils.RunTCPPingLoop(c.highwaySession.SsoAddr[0].String(), 10) res = utils.RunTCPPingLoop(c.highwaySession.SsoAddr[0].String(), 10)
@ -78,49 +81,75 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo {
if _, err := utils.HttpGetBytes("https://ssl.htdata.qq.com", ""); err == nil { if _, err := utils.HttpGetBytes("https://ssl.htdata.qq.com", ""); err == nil {
r.LongMessageServerResponseLatency = time.Since(start).Milliseconds() r.LongMessageServerResponseLatency = time.Since(start).Milliseconds()
} else { } else {
c.Error("test long message server response latency error: %v", err) c.error("test long message server response latency error: %v", err)
r.LongMessageServerResponseLatency = 9999 r.LongMessageServerResponseLatency = 9999
} }
wg.Wait() wg.Wait()
return r return r
} }
func (c *QQClient) connectFastest() error { func (c *QQClient) initServers() {
c.Disconnect() if c.Device() == nil {
addr, err := c.transport.ConnectFastest() // must have device. Use c.UseDevice to set it!
if err != nil { panic("client device is nil")
c.Disconnect() }
return err
sso, err := getSSOAddress(c.Device())
if err == nil && len(sso) > 0 {
c.servers = append(sso, c.servers...)
}
adds, err := net.LookupIP("msfwifi.3g.qq.com") // host servers
if err == nil && len(adds) > 0 {
var hostAddrs []netip.AddrPort
for _, addr := range adds {
ip, ok := netip.AddrFromSlice(addr.To4())
if ok {
hostAddrs = append(hostAddrs, netip.AddrPortFrom(ip, 8080))
}
}
c.servers = append(hostAddrs, c.servers...)
}
if len(c.servers) == 0 {
c.servers = []netip.AddrPort{ // default servers
netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 81}), 80),
netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 148, 59}), 14000),
netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 147}), 443),
netip.AddrPortFrom(netip.AddrFrom4([4]byte{125, 94, 60, 146}), 80),
netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 144, 215}), 80),
netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 22}), 80),
}
}
pings := make([]int64, len(c.servers))
wg := sync.WaitGroup{}
wg.Add(len(c.servers))
for i := range c.servers {
go func(index int) {
defer wg.Done()
p, err := qualityTest(c.servers[index].String())
if err != nil {
pings[index] = 9999
return
}
pings[index] = p
}(i)
}
wg.Wait()
sort.Slice(c.servers, func(i, j int) bool {
return pings[i] < pings[j]
})
if len(c.servers) > 3 {
c.servers = c.servers[0 : len(c.servers)/2] // 保留ping值中位数以上的server
} }
c.Debug("connected to server: %v [fastest]", addr.String())
c.transport.NetLoop(c.pktProc, c.transport.ReadRequest)
c.ConnectTime = time.Now()
return nil
} }
// connect 连接到 QQClient.servers 中的服务器 // connect 连接到 QQClient.servers 中的服务器
func (c *QQClient) connect() error { func (c *QQClient) connect() error {
c.once.Do(func() { // init qq servers
c.OnGroupMessage(func(_ *QQClient, _ *message.GroupMessage) { c.initServerOnce.Do(c.initServers)
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix()) addr := c.servers[c.currServerIndex].String()
}) c.info("connect to server: %v", addr)
c.OnPrivateMessage(func(_ *QQClient, _ *message.PrivateMessage) { err := c.TCP.Connect(addr)
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.OnTempMessage(func(_ *QQClient, _ *TempMessageEvent) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.onGroupMessageReceipt("internal", func(_ *QQClient, _ *groupMessageReceiptEvent) {
c.stat.MessageSent.Add(1)
})
// go c.netLoop()
})
return c.connectFastest() // 暂时?
/*c.Info("connect to server: %v", c.servers[c.currServerIndex].String())
err := c.TCP.Connect(c.servers[c.currServerIndex])
c.currServerIndex++ c.currServerIndex++
if c.currServerIndex == len(c.servers) { if c.currServerIndex == len(c.servers) {
c.currServerIndex = 0 c.currServerIndex = 0
@ -130,16 +159,30 @@ func (c *QQClient) connect() error {
if c.retryTimes > len(c.servers) { if c.retryTimes > len(c.servers) {
return errors.New("All servers are unreachable") return errors.New("All servers are unreachable")
} }
c.Error("connect server error: %v", err) c.error("connect server error: %v", err)
return err return err
} }
c.once.Do(func() {
c.GroupMessageEvent.Subscribe(func(_ *QQClient, _ *message.GroupMessage) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.PrivateMessageEvent.Subscribe(func(_ *QQClient, _ *message.PrivateMessage) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.TempMessageEvent.Subscribe(func(_ *QQClient, _ *TempMessageEvent) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.onGroupMessageReceipt("internal", func(_ *QQClient, _ *groupMessageReceiptEvent) {
c.stat.MessageSent.Add(1)
})
go c.netLoop()
})
c.retryTimes = 0 c.retryTimes = 0
c.ConnectTime = time.Now() c.ConnectTime = time.Now()
return nil*/ return nil
}
func (c *QQClient) QuickReconnect() {
c.quickReconnect() // TODO "用户请求快速重连"
} }
// quickReconnect 快速重连 // quickReconnect 快速重连
@ -147,14 +190,14 @@ func (c *QQClient) quickReconnect() {
c.Disconnect() c.Disconnect()
time.Sleep(time.Millisecond * 200) time.Sleep(time.Millisecond * 200)
if err := c.connect(); err != nil { if err := c.connect(); err != nil {
c.Error("connect server error: %v", err) c.error("connect server error: %v", err)
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "快速重连失败"}) c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "quick reconnect failed"})
return return
} }
if err := c.registerClient(); err != nil { if err := c.registerClient(); err != nil {
c.Error("register client failed: %v", err) c.error("register client failed: %v", err)
c.Disconnect() c.Disconnect()
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "register error"}) c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"})
return return
} }
} }
@ -162,59 +205,55 @@ func (c *QQClient) quickReconnect() {
// Disconnect 中断连接, 不释放资源 // Disconnect 中断连接, 不释放资源
func (c *QQClient) Disconnect() { func (c *QQClient) Disconnect() {
c.Online.Store(false) c.Online.Store(false)
c.transport.Close() c.TCP.Close()
} }
func (c *QQClient) send(call *network.Call) { // sendAndWait 向服务器发送一个数据包, 并等待返回
if call.Done == nil { func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.RequestParams) (any, error) {
call.Done = make(chan *network.Call, 3) // use buffered channel type T struct {
Response any
Error error
} }
seq := call.Request.SequenceID ch := make(chan T, 1)
c.pendingMu.Lock() var p network.RequestParams
c.pending[seq] = call
c.pendingMu.Unlock()
err := c.sendPacket(c.transport.PackPacket(call.Request)) if len(params) != 0 {
c.Debug("send pkt: %v seq: %d", call.Request.CommandName, call.Request.SequenceID) p = params[0]
if err != nil { }
c.pendingMu.Lock()
call = c.pending[seq] c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) {
delete(c.pending, seq) ch <- T{
c.pendingMu.Unlock() Response: i,
call.Err = err Error: err,
call.Done <- call }
} }, params: p, dynamic: false})
}
err := c.sendPacket(pkt)
func (c *QQClient) sendReq(req *network.Request) {
c.send(&network.Call{Request: req})
}
func (c *QQClient) call(req *network.Request) (*network.Response, error) {
call := &network.Call{
Request: req,
Done: make(chan *network.Call, 3),
}
c.send(call)
select {
case <-call.Done:
return call.Response, call.Err
case <-time.After(time.Second * 15):
return nil, errors.New("Packet timed out")
}
}
func (c *QQClient) callAndDecode(req *network.Request) (interface{}, error) {
resp, err := c.call(req)
if err != nil { if err != nil {
c.handlers.Delete(seq)
return nil, err return nil, err
} }
return req.Decode(resp)
retry := 0
for {
select {
case rsp := <-ch:
return rsp.Response, rsp.Error
case <-time.After(time.Second * 15):
retry++
if retry < 2 {
_ = c.sendPacket(pkt)
continue
}
c.handlers.Delete(seq)
return nil, errors.New("Packet timed out")
}
}
} }
// sendPacket 向服务器发送一个数据包 // sendPacket 向服务器发送一个数据包
func (c *QQClient) sendPacket(pkt []byte) error { func (c *QQClient) sendPacket(pkt []byte) error {
err := c.transport.Write(pkt) err := c.TCP.Write(pkt)
if err != nil { if err != nil {
c.stat.PacketLost.Add(1) c.stat.PacketLost.Add(1)
} else { } else {
@ -226,7 +265,7 @@ func (c *QQClient) sendPacket(pkt []byte) error {
// waitPacket // waitPacket
// 等待一个或多个数据包解析, 优先级低于 sendAndWait // 等待一个或多个数据包解析, 优先级低于 sendAndWait
// 返回终止解析函数 // 返回终止解析函数
func (c *QQClient) waitPacket(cmd string, f func(interface{}, error)) func() { func (c *QQClient) waitPacket(cmd string, f func(any, error)) func() {
c.waiters.Store(cmd, f) c.waiters.Store(cmd, f)
return func() { return func() {
c.waiters.Delete(cmd) c.waiters.Delete(cmd)
@ -235,9 +274,9 @@ func (c *QQClient) waitPacket(cmd string, f func(interface{}, error)) func() {
// waitPacketTimeoutSyncF // waitPacketTimeoutSyncF
// 等待一个数据包解析, 优先级低于 sendAndWait // 等待一个数据包解析, 优先级低于 sendAndWait
func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, filter func(interface{}) bool) (r interface{}, e error) { func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, filter func(any) bool) (r any, e error) {
notifyChan := make(chan bool) notifyChan := make(chan bool, 4)
defer c.waitPacket(cmd, func(i interface{}, err error) { defer c.waitPacket(cmd, func(i any, err error) {
if filter(i) { if filter(i) {
r = i r = i
e = err e = err
@ -252,104 +291,140 @@ func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, fil
} }
} }
// sendAndWaitDynamic
// 发送数据包并返回需要解析的 response
func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) {
ch := make(chan []byte, 1)
c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) { ch <- i.([]byte) }, dynamic: true})
err := c.sendPacket(pkt)
if err != nil {
c.handlers.Delete(seq)
return nil, err
}
select {
case rsp := <-ch:
return rsp, nil
case <-time.After(time.Second * 15):
c.handlers.Delete(seq)
return nil, errors.New("Packet timed out")
}
}
// SendSsoPacket
// 发送签名回调包给服务器并获取返回结果供提交
func (c *QQClient) SendSsoPacket(cmd string, body []byte) ([]byte, error) {
seq, data := c.uniPacket(cmd, body)
return c.sendAndWaitDynamic(seq, data)
}
// plannedDisconnect 计划中断线事件 // plannedDisconnect 计划中断线事件
func (c *QQClient) plannedDisconnect(_ *network.TCPListener) { func (c *QQClient) plannedDisconnect(_ *network.TCPClient) {
c.Debug("planned disconnect.") c.debug("planned disconnect.")
c.stat.DisconnectTimes.Add(1) c.stat.DisconnectTimes.Add(1)
c.Online.Store(false) c.Online.Store(false)
} }
// unexpectedDisconnect 非预期断线事件 // unexpectedDisconnect 非预期断线事件
func (c *QQClient) unexpectedDisconnect(_ *network.TCPListener, e error) { func (c *QQClient) unexpectedDisconnect(_ *network.TCPClient, e error) {
c.Error("unexpected disconnect: %v", e) c.error("unexpected disconnect: %v", e)
c.stat.DisconnectTimes.Add(1) c.stat.DisconnectTimes.Add(1)
c.Online.Store(false) c.Online.Store(false)
if err := c.connect(); err != nil { if err := c.connect(); err != nil {
c.Error("connect server error: %v", err) c.error("connect server error: %v", err)
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "connection dropped by server."}) c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "connection dropped by server."})
return return
} }
if err := c.registerClient(); err != nil { if err := c.registerClient(); err != nil {
c.Error("register client failed: %v", err) c.error("register client failed: %v", err)
c.Disconnect() c.Disconnect()
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "register error"}) c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"})
return return
} }
} }
func (c *QQClient) pktProc(req *network.Request, netErr error) { // netLoop 通过循环来不停接收数据包
if netErr != nil { func (c *QQClient) netLoop() {
switch true { errCount := 0
case errors.Is(netErr, network.ErrConnectionBroken): for c.alive {
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: netErr.Error()}) l, err := c.TCP.ReadInt32()
c.QuickReconnect()
case errors.Is(netErr, network.ErrSessionExpired) || errors.Is(netErr, network.ErrPacketDropped):
c.Disconnect()
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "session expired"})
}
c.Error("parse incoming packet error: %v", netErr)
return
}
if req.EncryptType == network.EncryptTypeEmptyKey {
m, err := c.oicq.Unmarshal(req.Body)
if err != nil { if err != nil {
c.Error("decrypt payload error: %v", err) time.Sleep(time.Millisecond * 500)
if errors.Is(err, oicq.ErrUnknownFlag) { continue
go c.quickReconnect() // TODO "服务器发送未知响应" }
if l < 4 || l > 1024*1024*10 { // max 10MB
c.error("parse incoming packet error: invalid packet length %v", l)
errCount++
if errCount > 2 {
go c.quickReconnect()
} }
continue
} }
req.Body = m.Body data, _ := c.TCP.ReadBytes(int(l) - 4)
} resp, err := c.transport.ReadResponse(data)
// pkt, err := packets.ParseIncomingPacket(data, c.sig.D2Key)
defer func() {
if pan := recover(); pan != nil {
c.Error("panic on decoder %v : %v\n%s", req.CommandName, pan, debug.Stack())
c.Dump("packet decode error: %v - %v", req.Body, req.CommandName, pan)
}
}()
c.Debug("recv pkt: %v seq: %v", req.CommandName, req.SequenceID)
c.stat.PacketReceived.Add(1)
// snapshot of read call
c.pendingMu.Lock()
call := c.pending[req.SequenceID]
if call != nil {
call.Response = &network.Response{
SequenceID: req.SequenceID,
CommandName: req.CommandName,
Body: req.Body,
Request: call.Request,
}
delete(c.pending, req.SequenceID)
}
c.pendingMu.Unlock()
if call != nil && call.Request.CommandName == req.CommandName {
select {
case call.Done <- call:
default:
// we don't want blocking
}
return
}
if decoder, ok := decoders[req.CommandName]; ok {
// found predefined decoder
resp := network.Response{
SequenceID: req.SequenceID,
CommandName: req.CommandName,
Body: req.Body,
// Request: nil,
}
decoded, err := decoder(c, &resp)
if err != nil { if err != nil {
c.Debug("decode req %v error: %+v", req.CommandName, err) c.error("parse incoming packet error: %v", err)
if errors.Is(err, network.ErrSessionExpired) || errors.Is(err, network.ErrPacketDropped) {
c.Disconnect()
go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "session expired"})
continue
}
errCount++
if errCount > 2 {
go c.quickReconnect()
}
continue
} }
if f, ok := c.waiters.Load(req.CommandName); ok { // 在不存在handler的情况下触发wait if resp.EncryptType == network.EncryptTypeEmptyKey {
f.(func(interface{}, error))(decoded, err) m, err := c.oicq.Unmarshal(resp.Body)
if err != nil {
c.error("decrypt payload error: %v", err)
if errors.Is(err, oicq.ErrUnknownFlag) {
go c.quickReconnect()
}
continue
}
resp.Body = m.Body
} }
} else { errCount = 0
c.Debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", req.CommandName, req.SequenceID) c.debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID)
c.stat.PacketReceived.Add(1)
pkt := &network.Packet{
SequenceId: uint16(resp.SequenceID),
CommandName: resp.CommandName,
Payload: resp.Body,
}
go func(pkt *network.Packet) {
defer func() {
if pan := recover(); pan != nil {
c.error("panic on decoder %v : %v\n%s", pkt.CommandName, pan, debug.Stack())
c.dump("packet decode error: %v - %v", pkt.Payload, pkt.CommandName, pan)
}
}()
if decoder, ok := decoders[pkt.CommandName]; ok {
// found predefined decoder
info, ok := c.handlers.LoadAndDelete(pkt.SequenceId)
var decoded any
decoded = pkt.Payload
if info == nil || !info.dynamic {
pkt.Params = info.getParams()
decoded, err = decoder(c, pkt)
if err != nil {
c.debug("decode pkt %v error: %+v", pkt.CommandName, err)
}
}
if ok {
info.fun(decoded, err)
} else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait
f(decoded, err)
}
} else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok {
// does not need decoder
f.fun(pkt.Payload, nil)
} else {
c.debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId)
}
}(pkt)
} }
} }

View File

@ -62,7 +62,7 @@ func (c *QQClient) grayTipProcessor(groupCode int64, tipInfo *notify.GeneralGray
} }
} }
if sender != 0 { if sender != 0 {
c.dispatchGroupNotifyEvent(&GroupPokeNotifyEvent{ c.GroupNotifyEvent.dispatch(c, &GroupPokeNotifyEvent{
GroupCode: groupCode, GroupCode: groupCode,
Sender: sender, Sender: sender,
Receiver: receiver, Receiver: receiver,
@ -81,7 +81,7 @@ func (c *QQClient) grayTipProcessor(groupCode int64, tipInfo *notify.GeneralGray
uin, _ = strconv.ParseInt(templ.Value, 10, 64) uin, _ = strconv.ParseInt(templ.Value, 10, 64)
} }
} }
c.dispatchGroupNotifyEvent(&MemberHonorChangedNotifyEvent{ c.GroupNotifyEvent.dispatch(c, &MemberHonorChangedNotifyEvent{
GroupCode: groupCode, GroupCode: groupCode,
Honor: func() HonorType { Honor: func() HonorType {
switch tipInfo.TemplId { switch tipInfo.TemplId {
@ -127,8 +127,7 @@ func (c *QQClient) msgGrayTipProcessor(groupCode int64, tipInfo *notify.AIOGrayT
} }
} }
// 好像只能这么判断 // 好像只能这么判断
switch { if strings.Contains(content, "头衔") {
case strings.Contains(content, "头衔"):
event := &MemberSpecialTitleUpdatedEvent{GroupCode: groupCode} event := &MemberSpecialTitleUpdatedEvent{GroupCode: groupCode}
for _, cmd := range tipCmds { for _, cmd := range tipCmds {
if cmd.Command == 5 { if cmd.Command == 5 {
@ -139,13 +138,13 @@ func (c *QQClient) msgGrayTipProcessor(groupCode int64, tipInfo *notify.AIOGrayT
} }
} }
if event.Uin == 0 { if event.Uin == 0 {
c.Error("process special title updated tips error: missing cmd") c.error("process special title updated tips error: missing cmd")
return return
} }
if mem := c.FindGroup(groupCode).FindMember(event.Uin); mem != nil { if mem := c.FindGroup(groupCode).FindMember(event.Uin); mem != nil {
mem.SpecialTitle = event.NewTitle mem.SpecialTitle = event.NewTitle
} }
c.dispatchMemberSpecialTitleUpdateEvent(event) c.MemberSpecialTitleUpdatedEvent.dispatch(c, event)
} }
} }
@ -185,6 +184,7 @@ func (e *MemberHonorChangedNotifyEvent) Content() string {
return fmt.Sprintf("%s(%d) 在群 %d 里连续发消息超过7天, 获得 群聊之火 标识。", e.Nick, e.Uin, e.GroupCode) return fmt.Sprintf("%s(%d) 在群 %d 里连续发消息超过7天, 获得 群聊之火 标识。", e.Nick, e.Uin, e.GroupCode)
case Emotion: case Emotion:
return fmt.Sprintf("%s(%d) 在群聊 %d 中连续发表情包超过3天且累计数量超过20条获得 快乐源泉 标识。", e.Nick, e.Uin, e.GroupCode) return fmt.Sprintf("%s(%d) 在群聊 %d 中连续发表情包超过3天且累计数量超过20条获得 快乐源泉 标识。", e.Nick, e.Uin, e.GroupCode)
default:
return "ERROR"
} }
return "ERROR"
} }

View File

@ -8,7 +8,11 @@ import (
"github.com/Mrs4s/MiraiGo/internal/proto" "github.com/Mrs4s/MiraiGo/internal/proto"
) )
func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) *network.Request { func init() {
decoders["OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200"] = decodeOfflineFileDownloadResponse
}
func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, []byte) {
seq := c.nextSeq() seq := c.nextSeq()
req := &cmd0x346.C346ReqBody{ req := &cmd0x346.C346ReqBody{
Cmd: 1200, Cmd: 1200,
@ -25,21 +29,22 @@ func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) *network.R
}, },
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
return c.uniPacketWithSeq(seq, "OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200", payload, decodeOfflineFileDownloadResponse) packet := c.uniPacketWithSeq(seq, "OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200", payload)
return seq, packet
} }
func decodeOfflineFileDownloadResponse(c *QQClient, resp *network.Response) (interface{}, error) { func decodeOfflineFileDownloadResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := cmd0x346.C346RspBody{} rsp := cmd0x346.C346RspBody{}
if err := proto.Unmarshal(resp.Body, &rsp); err != nil { if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
c.Error("unmarshal cmd0x346 rsp body error: %v", err) c.error("unmarshal cmd0x346 rsp body error: %v", err)
return nil, errors.Wrap(err, "unmarshal cmd0x346 rsp body error") return nil, errors.Wrap(err, "unmarshal cmd0x346 rsp body error")
} }
if rsp.ApplyDownloadRsp == nil { if rsp.ApplyDownloadRsp == nil {
c.Error("decode apply download 1200 error: apply rsp is nil.") c.error("decode apply download 1200 error: apply rsp is nil.")
return nil, errors.New("apply rsp is nil") return nil, errors.New("apply rsp is nil")
} }
if rsp.ApplyDownloadRsp.RetCode != 0 { if rsp.ApplyDownloadRsp.RetCode != 0 {
c.Error("decode apply download 1200 error: %v", rsp.ApplyDownloadRsp.RetCode) c.error("decode apply download 1200 error: %v", rsp.ApplyDownloadRsp.RetCode)
return nil, errors.Errorf("apply download rsp error: %d", rsp.ApplyDownloadRsp.RetCode) return nil, errors.Errorf("apply download rsp error: %d", rsp.ApplyDownloadRsp.RetCode)
} }
return "http://" + rsp.ApplyDownloadRsp.DownloadInfo.DownloadDomain + rsp.ApplyDownloadRsp.DownloadInfo.DownloadUrl, nil return "http://" + rsp.ApplyDownloadRsp.DownloadInfo.DownloadDomain + rsp.ApplyDownloadRsp.DownloadInfo.DownloadUrl, nil

View File

@ -23,21 +23,21 @@ var msg0x210Decoders = map[int64]func(*QQClient, []byte) error{
} }
// OnlinePush.ReqPush // OnlinePush.ReqPush
func decodeOnlinePushReqPacket(c *QQClient, resp *network.Response) (interface{}, error) { func decodeOnlinePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(resp.Body)) request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{} data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer)) data.ReadFrom(jce.NewJceReader(request.SBuffer))
jr := jce.NewJceReader(data.Map["req"]["OnlinePushPack.SvcReqPushMsg"][1:]) jr := jce.NewJceReader(data.Map["req"]["OnlinePushPack.SvcReqPushMsg"][1:])
uin := jr.ReadInt64(0) uin := jr.ReadInt64(0)
msgInfos := jr.ReadPushMessageInfos(2) msgInfos := jr.ReadPushMessageInfos(2)
_ = c.sendPacket(c.buildDeleteOnlinePushPacket(uin, 0, nil, uint16(resp.SequenceID), msgInfos)) _ = c.sendPacket(c.buildDeleteOnlinePushPacket(uin, 0, nil, pkt.SequenceId, msgInfos))
for _, m := range msgInfos { for _, m := range msgInfos {
k := fmt.Sprintf("%v%v%v", m.MsgSeq, m.MsgTime, m.MsgUid) k := fmt.Sprintf("%v%v%v", m.MsgSeq, m.MsgTime, m.MsgUid)
if _, ok := c.onlinePushCache.Get(k); ok { if _, ok := c.onlinePushCache.Get(k); ok {
continue continue
} }
c.onlinePushCache.Add(k, "", time.Second*30) c.onlinePushCache.Add(k, unit{}, time.Second*30)
// 0x2dc // 0x2dc
if m.MsgType == 732 { if m.MsgType == 732 {
r := binary.NewReader(m.VMsg) r := binary.NewReader(m.VMsg)
@ -53,7 +53,17 @@ func decodeOnlinePushReqPacket(c *QQClient, resp *network.Response) (interface{}
r.ReadBytes(6) r.ReadBytes(6)
target := int64(uint32(r.ReadInt32())) target := int64(uint32(r.ReadInt32()))
t := r.ReadInt32() t := r.ReadInt32()
c.dispatchGroupMuteEvent(&GroupMuteEvent{
if target != 0 {
member := c.FindGroup(groupCode).FindMember(target)
if t > 0 {
member.ShutUpTimestamp = time.Now().Add(time.Second * time.Duration(t)).Unix()
} else {
member.ShutUpTimestamp = 0
}
}
c.GroupMuteEvent.dispatch(c, &GroupMuteEvent{
GroupCode: groupCode, GroupCode: groupCode,
OperatorUin: operator, OperatorUin: operator,
TargetUin: target, TargetUin: target,
@ -68,7 +78,7 @@ func decodeOnlinePushReqPacket(c *QQClient, resp *network.Response) (interface{}
if rm.MsgType == 2 { if rm.MsgType == 2 {
continue continue
} }
c.dispatchGroupMessageRecalledEvent(&GroupMessageRecalledEvent{ c.GroupMessageRecalledEvent.dispatch(c, &GroupMessageRecalledEvent{
GroupCode: groupCode, GroupCode: groupCode,
OperatorUin: b.OptMsgRecall.Uin, OperatorUin: b.OptMsgRecall.Uin,
AuthorUin: rm.AuthorUin, AuthorUin: rm.AuthorUin,
@ -82,7 +92,7 @@ func decodeOnlinePushReqPacket(c *QQClient, resp *network.Response) (interface{}
} }
if b.OptMsgRedTips != nil { if b.OptMsgRedTips != nil {
if b.OptMsgRedTips.LuckyFlag == 1 { // 运气王提示 if b.OptMsgRedTips.LuckyFlag == 1 { // 运气王提示
c.dispatchGroupNotifyEvent(&GroupRedBagLuckyKingNotifyEvent{ c.GroupNotifyEvent.dispatch(c, &GroupRedBagLuckyKingNotifyEvent{
GroupCode: groupCode, GroupCode: groupCode,
Sender: int64(b.OptMsgRedTips.SenderUin), Sender: int64(b.OptMsgRedTips.SenderUin),
LuckyKing: int64(b.OptMsgRedTips.LuckyUin), LuckyKing: int64(b.OptMsgRedTips.LuckyUin),
@ -91,7 +101,7 @@ func decodeOnlinePushReqPacket(c *QQClient, resp *network.Response) (interface{}
} }
if b.QqGroupDigestMsg != nil { if b.QqGroupDigestMsg != nil {
digest := b.QqGroupDigestMsg digest := b.QqGroupDigestMsg
c.dispatchGroupDigestEvent(&GroupDigestEvent{ c.GroupDigestEvent.dispatch(c, &GroupDigestEvent{
GroupCode: int64(digest.GroupCode), GroupCode: int64(digest.GroupCode),
MessageID: int32(digest.Seq), MessageID: int32(digest.Seq),
InternalMessageID: int32(digest.Random), InternalMessageID: int32(digest.Random),
@ -118,7 +128,7 @@ func decodeOnlinePushReqPacket(c *QQClient, resp *network.Response) (interface{}
return nil, errors.Wrap(err, "decode online push 0x210 error") return nil, errors.Wrap(err, "decode online push 0x210 error")
} }
} else { } else {
c.Debug("unknown online push 0x210 sub type 0x%v", strconv.FormatInt(subType, 16)) c.debug("unknown online push 0x210 sub type 0x%v", strconv.FormatInt(subType, 16))
} }
} }
} }
@ -132,7 +142,7 @@ func msgType0x210Sub8ADecoder(c *QQClient, protobuf []byte) error {
} }
for _, m := range s8a.MsgInfo { for _, m := range s8a.MsgInfo {
if m.ToUin == c.Uin { if m.ToUin == c.Uin {
c.dispatchFriendMessageRecalledEvent(&FriendMessageRecalledEvent{ c.FriendMessageRecalledEvent.dispatch(c, &FriendMessageRecalledEvent{
FriendUin: m.FromUin, FriendUin: m.FromUin,
MessageId: m.MsgSeq, MessageId: m.MsgSeq,
Time: m.MsgTime, Time: m.MsgTime,
@ -152,7 +162,7 @@ func msgType0x210SubB3Decoder(c *QQClient, protobuf []byte) error {
Nickname: b3.MsgAddFrdNotify.Nick, Nickname: b3.MsgAddFrdNotify.Nick,
} }
c.FriendList = append(c.FriendList, frd) c.FriendList = append(c.FriendList, frd)
c.dispatchNewFriendEvent(&NewFriendEvent{Friend: frd}) c.NewFriendEvent.dispatch(c, &NewFriendEvent{Friend: frd})
return nil return nil
} }
@ -167,7 +177,7 @@ func msgType0x210SubD4Decoder(c *QQClient, protobuf []byte) error {
groupLeaveLock.Unlock() groupLeaveLock.Unlock()
return err return err
} }
c.dispatchLeaveGroupEvent(&GroupLeaveEvent{Group: g}) c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{Group: g})
} }
groupLeaveLock.Unlock() groupLeaveLock.Unlock()
return nil return nil
@ -181,15 +191,15 @@ func msgType0x210Sub27Decoder(c *QQClient, protobuf []byte) error {
for _, m := range s27.ModInfos { for _, m := range s27.ModInfos {
if m.ModGroupProfile != nil { if m.ModGroupProfile != nil {
for _, info := range m.ModGroupProfile.GroupProfileInfos { for _, info := range m.ModGroupProfile.GroupProfileInfos {
if info.GetField() == 1 { if info.Field.Unwrap() == 1 {
if g := c.FindGroup(int64(m.ModGroupProfile.GetGroupCode())); g != nil { if g := c.FindGroup(int64(m.ModGroupProfile.GroupCode.Unwrap())); g != nil {
old := g.Name old := g.Name
g.Name = string(info.GetValue()) g.Name = string(info.Value)
c.dispatchGroupNameUpdatedEvent(&GroupNameUpdatedEvent{ c.GroupNameUpdatedEvent.dispatch(c, &GroupNameUpdatedEvent{
Group: g, Group: g,
OldName: old, OldName: old,
NewName: g.Name, NewName: g.Name,
OperatorUin: int64(m.ModGroupProfile.GetCmdUin()), OperatorUin: int64(m.ModGroupProfile.CmdUin.Unwrap()),
}) })
} }
} }
@ -198,6 +208,10 @@ func msgType0x210Sub27Decoder(c *QQClient, protobuf []byte) error {
if m.DelFriend != nil { if m.DelFriend != nil {
frdUin := m.DelFriend.Uins[0] frdUin := m.DelFriend.Uins[0]
if frd := c.FindFriend(int64(frdUin)); frd != nil { if frd := c.FindFriend(int64(frdUin)); frd != nil {
c.DeleteFriendEvent.dispatch(c, &DeleteFriendEvent{
Uin: frd.Uin,
Nickname: frd.Nickname,
})
if err := c.ReloadFriendList(); err != nil { if err := c.ReloadFriendList(); err != nil {
return errors.Wrap(err, "failed to reload friend list") return errors.Wrap(err, "failed to reload friend list")
} }
@ -221,7 +235,10 @@ func msgType0x210Sub122Decoder(c *QQClient, protobuf []byte) error {
if sender == 0 { if sender == 0 {
return nil return nil
} }
c.dispatchFriendNotifyEvent(&FriendPokeNotifyEvent{ if receiver == 0 {
receiver = c.Uin
}
c.FriendNotifyEvent.dispatch(c, &FriendPokeNotifyEvent{
Sender: sender, Sender: sender,
Receiver: receiver, Receiver: receiver,
}) })
@ -238,11 +255,11 @@ func msgType0x210Sub44Decoder(c *QQClient, protobuf []byte) error {
} }
groupJoinLock.Lock() groupJoinLock.Lock()
defer groupJoinLock.Unlock() defer groupJoinLock.Unlock()
if s44.GroupSyncMsg.GetGrpCode() == 0 { // member sync if s44.GroupSyncMsg.GrpCode == 0 { // member sync
return errors.New("invalid group code") return errors.New("invalid group code")
} }
c.Debug("syncing members.") c.debug("syncing members.")
if group := c.FindGroup(s44.GroupSyncMsg.GetGrpCode()); group != nil { if group := c.FindGroup(s44.GroupSyncMsg.GrpCode); group != nil {
group.lock.Lock() group.lock.Lock()
defer group.lock.Unlock() defer group.lock.Unlock()
@ -257,7 +274,7 @@ func msgType0x210Sub44Decoder(c *QQClient, protobuf []byte) error {
group.Members = newMem group.Members = newMem
for _, m := range newMem { for _, m := range newMem {
if lastJoinTime < m.JoinTime { if lastJoinTime < m.JoinTime {
go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{ c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
Group: group, Group: group,
Member: m, Member: m,
}) })

View File

@ -6,44 +6,18 @@ import (
) )
//go:noinline //go:noinline
func (c *QQClient) buildOicqRequestPacket(uin int64, command uint16, body []byte) []byte { func (c *QQClient) buildOicqRequestPacket(uin int64, command uint16, body *oicq.TLV) []byte {
req := oicq.Message{ req := oicq.Message{
Uin: uint32(uin), Uin: uint32(uin),
Command: command, Command: command,
EncryptionMethod: oicq.EM_ECDH, EncryptionMethod: oicq.EM_ECDH,
Body: body, Body: body.Marshal(),
} }
return c.oicq.Marshal(&req) return c.oicq.Marshal(&req)
} }
type decoderFunc = func(*QQClient, *network.Response) (interface{}, error)
func bindDecoder(c *QQClient, decoder decoderFunc) func(*network.Response) (interface{}, error) {
return func(response *network.Response) (interface{}, error) {
return decoder(c, response)
}
}
//go:noinline //go:noinline
func (c *QQClient) uniRequest(command string, body []byte, decoder decoderFunc) *network.Request { func (c *QQClient) uniPacket(command string, body []byte) (uint16, []byte) {
seq := c.nextSeq()
var decode func(*network.Response) (interface{}, error)
if decoder != nil {
decode = bindDecoder(c, decoder)
}
return &network.Request{
Type: network.RequestTypeSimple,
EncryptType: network.EncryptTypeD2Key,
Uin: c.Uin,
SequenceID: int32(seq),
CommandName: command,
Body: body,
Decode: decode,
}
}
//go:noinline
func (c *QQClient) uniCall(command string, body []byte) (*network.Response, error) {
seq := c.nextSeq() seq := c.nextSeq()
req := network.Request{ req := network.Request{
Type: network.RequestTypeSimple, Type: network.RequestTypeSimple,
@ -53,15 +27,11 @@ func (c *QQClient) uniCall(command string, body []byte) (*network.Response, erro
CommandName: command, CommandName: command,
Body: body, Body: body,
} }
return c.call(&req) return seq, c.transport.PackPacket(&req)
} }
//go:noinline //go:noinline
func (c *QQClient) uniPacketWithSeq(seq uint16, command string, body []byte, decoder decoderFunc) *network.Request { func (c *QQClient) uniPacketWithSeq(seq uint16, command string, body []byte) []byte {
var decode func(*network.Response) (interface{}, error)
if decoder != nil {
decode = bindDecoder(c, decoder)
}
req := network.Request{ req := network.Request{
Type: network.RequestTypeSimple, Type: network.RequestTypeSimple,
EncryptType: network.EncryptTypeD2Key, EncryptType: network.EncryptTypeD2Key,
@ -69,7 +39,6 @@ func (c *QQClient) uniPacketWithSeq(seq uint16, command string, body []byte, dec
SequenceID: int32(seq), SequenceID: int32(seq),
CommandName: command, CommandName: command,
Body: body, Body: body,
Decode: decode,
} }
return &req return c.transport.PackPacket(&req)
} }

View File

@ -3,259 +3,74 @@
package channel package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type ChannelUserInfo struct { type ChannelUserInfo struct {
ClientIdentity *ClientIdentity `protobuf:"bytes,1,opt"` ClientIdentity *ClientIdentity `protobuf:"bytes,1,opt"`
MemberType *uint32 `protobuf:"varint,2,opt"` MemberType proto.Option[uint32] `protobuf:"varint,2,opt"`
Permission *ChannelUserPermission `protobuf:"bytes,3,opt"` Permission *ChannelUserPermission `protobuf:"bytes,3,opt"`
RoleGroups []*BaseRoleGroupInfo `protobuf:"bytes,4,rep"` RoleGroups []*BaseRoleGroupInfo `protobuf:"bytes,4,rep"`
} }
func (x *ChannelUserInfo) GetClientIdentity() *ClientIdentity {
if x != nil {
return x.ClientIdentity
}
return nil
}
func (x *ChannelUserInfo) GetMemberType() uint32 {
if x != nil && x.MemberType != nil {
return *x.MemberType
}
return 0
}
func (x *ChannelUserInfo) GetPermission() *ChannelUserPermission {
if x != nil {
return x.Permission
}
return nil
}
func (x *ChannelUserInfo) GetRoleGroups() []*BaseRoleGroupInfo {
if x != nil {
return x.RoleGroups
}
return nil
}
type ChannelUserPermission struct { type ChannelUserPermission struct {
AllowReadFeed *bool `protobuf:"varint,1,opt"` AllowReadFeed proto.Option[bool] `protobuf:"varint,1,opt"`
AllowWriteFeed *bool `protobuf:"varint,2,opt"` AllowWriteFeed proto.Option[bool] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *ChannelUserPermission) GetAllowReadFeed() bool {
if x != nil && x.AllowReadFeed != nil {
return *x.AllowReadFeed
}
return false
}
func (x *ChannelUserPermission) GetAllowWriteFeed() bool {
if x != nil && x.AllowWriteFeed != nil {
return *x.AllowWriteFeed
}
return false
} }
type ClientIdentity struct { type ClientIdentity struct {
ClientId *uint32 `protobuf:"varint,1,opt"` ClientId proto.Option[uint32] `protobuf:"varint,1,opt"`
Desc *string `protobuf:"bytes,2,opt"` Desc proto.Option[string] `protobuf:"bytes,2,opt"`
} _ [0]func()
func (x *ClientIdentity) GetClientId() uint32 {
if x != nil && x.ClientId != nil {
return *x.ClientId
}
return 0
}
func (x *ClientIdentity) GetDesc() string {
if x != nil && x.Desc != nil {
return *x.Desc
}
return ""
} }
type BaseGuildInfo struct { type BaseGuildInfo struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name *string `protobuf:"bytes,2,opt"` Name proto.Option[string] `protobuf:"bytes,2,opt"`
JoinTime *uint64 `protobuf:"varint,3,opt"` JoinTime proto.Option[uint64] `protobuf:"varint,3,opt"`
} _ [0]func()
func (x *BaseGuildInfo) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *BaseGuildInfo) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *BaseGuildInfo) GetJoinTime() uint64 {
if x != nil && x.JoinTime != nil {
return *x.JoinTime
}
return 0
} }
type BaseRoleGroupInfo struct { type BaseRoleGroupInfo struct {
RoleId *uint64 `protobuf:"varint,1,opt"` RoleId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name *string `protobuf:"bytes,2,opt"` Name proto.Option[string] `protobuf:"bytes,2,opt"`
Color *uint32 `protobuf:"varint,3,opt"` Color proto.Option[uint32] `protobuf:"varint,3,opt"`
} _ [0]func()
func (x *BaseRoleGroupInfo) GetRoleId() uint64 {
if x != nil && x.RoleId != nil {
return *x.RoleId
}
return 0
}
func (x *BaseRoleGroupInfo) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *BaseRoleGroupInfo) GetColor() uint32 {
if x != nil && x.Color != nil {
return *x.Color
}
return 0
} }
type StChannelInfo struct { type StChannelInfo struct {
Sign *StChannelSign `protobuf:"bytes,1,opt"` Sign *StChannelSign `protobuf:"bytes,1,opt"`
Name *string `protobuf:"bytes,2,opt"` Name proto.Option[string] `protobuf:"bytes,2,opt"`
IconUrl *string `protobuf:"bytes,3,opt"` IconUrl proto.Option[string] `protobuf:"bytes,3,opt"`
} _ [0]func()
func (x *StChannelInfo) GetSign() *StChannelSign {
if x != nil {
return x.Sign
}
return nil
}
func (x *StChannelInfo) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *StChannelInfo) GetIconUrl() string {
if x != nil && x.IconUrl != nil {
return *x.IconUrl
}
return ""
} }
type StChannelSign struct { type StChannelSign struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelId *uint64 `protobuf:"varint,2,opt"` ChannelId proto.Option[uint64] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *StChannelSign) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *StChannelSign) GetChannelId() uint64 {
if x != nil && x.ChannelId != nil {
return *x.ChannelId
}
return 0
} }
type StEmotionReactionInfo struct { type StEmotionReactionInfo struct {
Id *string `protobuf:"bytes,1,opt"` Id proto.Option[string] `protobuf:"bytes,1,opt"`
EmojiReactionList []*EmojiReaction `protobuf:"bytes,2,rep"` EmojiReactionList []*EmojiReaction `protobuf:"bytes,2,rep"`
}
func (x *StEmotionReactionInfo) GetId() string {
if x != nil && x.Id != nil {
return *x.Id
}
return ""
}
func (x *StEmotionReactionInfo) GetEmojiReactionList() []*EmojiReaction {
if x != nil {
return x.EmojiReactionList
}
return nil
} }
type StCommonExt struct { type StCommonExt struct {
MapInfo []*CommonEntry `protobuf:"bytes,1,rep"` MapInfo []*CommonEntry `protobuf:"bytes,1,rep"`
AttachInfo *string `protobuf:"bytes,2,opt"` AttachInfo proto.Option[string] `protobuf:"bytes,2,opt"`
MapBytesInfo []*BytesEntry `protobuf:"bytes,3,rep"` MapBytesInfo []*BytesEntry `protobuf:"bytes,3,rep"`
}
func (x *StCommonExt) GetMapInfo() []*CommonEntry {
if x != nil {
return x.MapInfo
}
return nil
}
func (x *StCommonExt) GetAttachInfo() string {
if x != nil && x.AttachInfo != nil {
return *x.AttachInfo
}
return ""
}
func (x *StCommonExt) GetMapBytesInfo() []*BytesEntry {
if x != nil {
return x.MapBytesInfo
}
return nil
} }
type BytesEntry struct { type BytesEntry struct {
Key *string `protobuf:"bytes,1,opt"` Key proto.Option[string] `protobuf:"bytes,1,opt"`
Value []byte `protobuf:"bytes,2,opt"` Value []byte `protobuf:"bytes,2,opt"`
}
func (x *BytesEntry) GetKey() string {
if x != nil && x.Key != nil {
return *x.Key
}
return ""
}
func (x *BytesEntry) GetValue() []byte {
if x != nil {
return x.Value
}
return nil
} }
type CommonEntry struct { type CommonEntry struct {
Key *string `protobuf:"bytes,1,opt"` Key proto.Option[string] `protobuf:"bytes,1,opt"`
Value *string `protobuf:"bytes,2,opt"` Value proto.Option[string] `protobuf:"bytes,2,opt"`
} _ [0]func()
func (x *CommonEntry) GetKey() string {
if x != nil && x.Key != nil {
return *x.Key
}
return ""
}
func (x *CommonEntry) GetValue() string {
if x != nil && x.Value != nil {
return *x.Value
}
return ""
} }

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/MsgResponsesSvr.proto"; import "pb/channel/MsgResponsesSvr.proto";

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/GuildChannelBase.proto"; import "pb/channel/GuildChannelBase.proto";

View File

@ -3,422 +3,99 @@
package channel package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type GetNoticesReq struct { type GetNoticesReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
PageNum *uint32 `protobuf:"varint,2,opt"` PageNum proto.Option[uint32] `protobuf:"varint,2,opt"`
AttachInfo *string `protobuf:"bytes,3,opt"` AttachInfo proto.Option[string] `protobuf:"bytes,3,opt"`
} _ [0]func()
func (x *GetNoticesReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *GetNoticesReq) GetPageNum() uint32 {
if x != nil && x.PageNum != nil {
return *x.PageNum
}
return 0
}
func (x *GetNoticesReq) GetAttachInfo() string {
if x != nil && x.AttachInfo != nil {
return *x.AttachInfo
}
return ""
} }
type GetNoticesRsp struct { type GetNoticesRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Notices []*StNotice `protobuf:"bytes,2,rep"` Notices []*StNotice `protobuf:"bytes,2,rep"`
TotalNum *uint32 `protobuf:"varint,3,opt"` TotalNum proto.Option[uint32] `protobuf:"varint,3,opt"`
IsFinish *bool `protobuf:"varint,4,opt"` IsFinish proto.Option[bool] `protobuf:"varint,4,opt"`
AttachInfo *string `protobuf:"bytes,5,opt"` AttachInfo proto.Option[string] `protobuf:"bytes,5,opt"`
}
func (x *GetNoticesRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *GetNoticesRsp) GetNotices() []*StNotice {
if x != nil {
return x.Notices
}
return nil
}
func (x *GetNoticesRsp) GetTotalNum() uint32 {
if x != nil && x.TotalNum != nil {
return *x.TotalNum
}
return 0
}
func (x *GetNoticesRsp) GetIsFinish() bool {
if x != nil && x.IsFinish != nil {
return *x.IsFinish
}
return false
}
func (x *GetNoticesRsp) GetAttachInfo() string {
if x != nil && x.AttachInfo != nil {
return *x.AttachInfo
}
return ""
} }
type NeedInsertCommentInfo struct { type NeedInsertCommentInfo struct {
CommentID *string `protobuf:"bytes,1,opt"` CommentID proto.Option[string] `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *NeedInsertCommentInfo) GetCommentID() string {
if x != nil && x.CommentID != nil {
return *x.CommentID
}
return ""
} }
type RefreshToast struct { type RefreshToast struct {
Text *string `protobuf:"bytes,1,opt"` Text proto.Option[string] `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *RefreshToast) GetText() string {
if x != nil && x.Text != nil {
return *x.Text
}
return ""
} }
type StGetChannelFeedsReq struct { type StGetChannelFeedsReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Count *uint32 `protobuf:"varint,2,opt"` Count proto.Option[uint32] `protobuf:"varint,2,opt"`
From *uint32 `protobuf:"varint,3,opt"` From proto.Option[uint32] `protobuf:"varint,3,opt"`
ChannelSign *StChannelSign `protobuf:"bytes,4,opt"` ChannelSign *StChannelSign `protobuf:"bytes,4,opt"`
FeedAttchInfo *string `protobuf:"bytes,5,opt"` FeedAttchInfo proto.Option[string] `protobuf:"bytes,5,opt"`
} _ [0]func()
func (x *StGetChannelFeedsReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StGetChannelFeedsReq) GetCount() uint32 {
if x != nil && x.Count != nil {
return *x.Count
}
return 0
}
func (x *StGetChannelFeedsReq) GetFrom() uint32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StGetChannelFeedsReq) GetChannelSign() *StChannelSign {
if x != nil {
return x.ChannelSign
}
return nil
}
func (x *StGetChannelFeedsReq) GetFeedAttchInfo() string {
if x != nil && x.FeedAttchInfo != nil {
return *x.FeedAttchInfo
}
return ""
} }
type StGetChannelFeedsRsp struct { type StGetChannelFeedsRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
VecFeed []*StFeed `protobuf:"bytes,2,rep"` VecFeed []*StFeed `protobuf:"bytes,2,rep"`
IsFinish *uint32 `protobuf:"varint,3,opt"` IsFinish proto.Option[uint32] `protobuf:"varint,3,opt"`
User *StUser `protobuf:"bytes,4,opt"` User *StUser `protobuf:"bytes,4,opt"`
FeedAttchInfo *string `protobuf:"bytes,5,opt"` FeedAttchInfo proto.Option[string] `protobuf:"bytes,5,opt"`
RefreshToast *RefreshToast `protobuf:"bytes,6,opt"` RefreshToast *RefreshToast `protobuf:"bytes,6,opt"`
}
func (x *StGetChannelFeedsRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StGetChannelFeedsRsp) GetVecFeed() []*StFeed {
if x != nil {
return x.VecFeed
}
return nil
}
func (x *StGetChannelFeedsRsp) GetIsFinish() uint32 {
if x != nil && x.IsFinish != nil {
return *x.IsFinish
}
return 0
}
func (x *StGetChannelFeedsRsp) GetUser() *StUser {
if x != nil {
return x.User
}
return nil
}
func (x *StGetChannelFeedsRsp) GetFeedAttchInfo() string {
if x != nil && x.FeedAttchInfo != nil {
return *x.FeedAttchInfo
}
return ""
}
func (x *StGetChannelFeedsRsp) GetRefreshToast() *RefreshToast {
if x != nil {
return x.RefreshToast
}
return nil
} }
type StGetChannelShareFeedReq struct { type StGetChannelShareFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
From *uint32 `protobuf:"varint,2,opt"` From proto.Option[uint32] `protobuf:"varint,2,opt"`
ChannelShareInfo *StChannelShareInfo `protobuf:"bytes,3,opt"` ChannelShareInfo *StChannelShareInfo `protobuf:"bytes,3,opt"`
} _ [0]func()
func (x *StGetChannelShareFeedReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StGetChannelShareFeedReq) GetFrom() uint32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StGetChannelShareFeedReq) GetChannelShareInfo() *StChannelShareInfo {
if x != nil {
return x.ChannelShareInfo
}
return nil
} }
type StGetChannelShareFeedRsp struct { type StGetChannelShareFeedRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"` Feed *StFeed `protobuf:"bytes,2,opt"`
} _ [0]func()
func (x *StGetChannelShareFeedRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StGetChannelShareFeedRsp) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
} }
type StGetFeedCommentsReq struct { type StGetFeedCommentsReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
UserId *string `protobuf:"bytes,2,opt"` UserId proto.Option[string] `protobuf:"bytes,2,opt"`
FeedId *string `protobuf:"bytes,3,opt"` FeedId proto.Option[string] `protobuf:"bytes,3,opt"`
ListNum *uint32 `protobuf:"varint,4,opt"` ListNum proto.Option[uint32] `protobuf:"varint,4,opt"`
From *uint32 `protobuf:"varint,5,opt"` From proto.Option[uint32] `protobuf:"varint,5,opt"`
AttchInfo *string `protobuf:"bytes,6,opt"` AttchInfo proto.Option[string] `protobuf:"bytes,6,opt"`
EntrySchema *string `protobuf:"bytes,7,opt"` EntrySchema proto.Option[string] `protobuf:"bytes,7,opt"`
} _ [0]func()
func (x *StGetFeedCommentsReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StGetFeedCommentsReq) GetUserId() string {
if x != nil && x.UserId != nil {
return *x.UserId
}
return ""
}
func (x *StGetFeedCommentsReq) GetFeedId() string {
if x != nil && x.FeedId != nil {
return *x.FeedId
}
return ""
}
func (x *StGetFeedCommentsReq) GetListNum() uint32 {
if x != nil && x.ListNum != nil {
return *x.ListNum
}
return 0
}
func (x *StGetFeedCommentsReq) GetFrom() uint32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StGetFeedCommentsReq) GetAttchInfo() string {
if x != nil && x.AttchInfo != nil {
return *x.AttchInfo
}
return ""
}
func (x *StGetFeedCommentsReq) GetEntrySchema() string {
if x != nil && x.EntrySchema != nil {
return *x.EntrySchema
}
return ""
} }
type StGetFeedCommentsRsp struct { type StGetFeedCommentsRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
VecComment []*StComment `protobuf:"bytes,2,rep"` VecComment []*StComment `protobuf:"bytes,2,rep"`
TotalNum *uint32 `protobuf:"varint,3,opt"` TotalNum proto.Option[uint32] `protobuf:"varint,3,opt"`
IsFinish *uint32 `protobuf:"varint,4,opt"` IsFinish proto.Option[uint32] `protobuf:"varint,4,opt"`
AttchInfo *string `protobuf:"bytes,5,opt"` AttchInfo proto.Option[string] `protobuf:"bytes,5,opt"`
}
func (x *StGetFeedCommentsRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StGetFeedCommentsRsp) GetVecComment() []*StComment {
if x != nil {
return x.VecComment
}
return nil
}
func (x *StGetFeedCommentsRsp) GetTotalNum() uint32 {
if x != nil && x.TotalNum != nil {
return *x.TotalNum
}
return 0
}
func (x *StGetFeedCommentsRsp) GetIsFinish() uint32 {
if x != nil && x.IsFinish != nil {
return *x.IsFinish
}
return 0
}
func (x *StGetFeedCommentsRsp) GetAttchInfo() string {
if x != nil && x.AttchInfo != nil {
return *x.AttchInfo
}
return ""
} }
type StGetFeedDetailReq struct { type StGetFeedDetailReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
From *uint32 `protobuf:"varint,2,opt"` From proto.Option[uint32] `protobuf:"varint,2,opt"`
UserId *string `protobuf:"bytes,3,opt"` UserId proto.Option[string] `protobuf:"bytes,3,opt"`
FeedId *string `protobuf:"bytes,4,opt"` FeedId proto.Option[string] `protobuf:"bytes,4,opt"`
CreateTime *uint64 `protobuf:"varint,5,opt"` CreateTime proto.Option[uint64] `protobuf:"varint,5,opt"`
DetailType *uint32 `protobuf:"varint,6,opt"` DetailType proto.Option[uint32] `protobuf:"varint,6,opt"`
ChannelSign *StChannelSign `protobuf:"bytes,7,opt"` ChannelSign *StChannelSign `protobuf:"bytes,7,opt"`
} _ [0]func()
func (x *StGetFeedDetailReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StGetFeedDetailReq) GetFrom() uint32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StGetFeedDetailReq) GetUserId() string {
if x != nil && x.UserId != nil {
return *x.UserId
}
return ""
}
func (x *StGetFeedDetailReq) GetFeedId() string {
if x != nil && x.FeedId != nil {
return *x.FeedId
}
return ""
}
func (x *StGetFeedDetailReq) GetCreateTime() uint64 {
if x != nil && x.CreateTime != nil {
return *x.CreateTime
}
return 0
}
func (x *StGetFeedDetailReq) GetDetailType() uint32 {
if x != nil && x.DetailType != nil {
return *x.DetailType
}
return 0
}
func (x *StGetFeedDetailReq) GetChannelSign() *StChannelSign {
if x != nil {
return x.ChannelSign
}
return nil
} }
type StGetFeedDetailRsp struct { type StGetFeedDetailRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"` Feed *StFeed `protobuf:"bytes,2,opt"`
LoginUser *StUser `protobuf:"bytes,3,opt"` LoginUser *StUser `protobuf:"bytes,3,opt"`
} _ [0]func()
func (x *StGetFeedDetailRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StGetFeedDetailRsp) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StGetFeedDetailRsp) GetLoginUser() *StUser {
if x != nil {
return x.LoginUser
}
return nil
} }

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/GuildFeedCloudMeta.proto"; import "pb/channel/GuildFeedCloudMeta.proto";
import "pb/channel/GuildChannelBase.proto"; import "pb/channel/GuildChannelBase.proto";

View File

@ -3,79 +3,20 @@
package channel package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type StAlterFeedReq struct { type StAlterFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"` Feed *StFeed `protobuf:"bytes,2,opt"`
BusiReqData []byte `protobuf:"bytes,3,opt"` BusiReqData []byte `protobuf:"bytes,3,opt"`
MBitmap *uint64 `protobuf:"varint,4,opt"` MBitmap proto.Option[uint64] `protobuf:"varint,4,opt"`
From *int32 `protobuf:"varint,5,opt"` From proto.Option[int32] `protobuf:"varint,5,opt"`
Src *int32 `protobuf:"varint,6,opt"` Src proto.Option[int32] `protobuf:"varint,6,opt"`
AlterFeedExtInfo []*CommonEntry `protobuf:"bytes,7,rep"` AlterFeedExtInfo []*CommonEntry `protobuf:"bytes,7,rep"`
JsonFeed *string `protobuf:"bytes,8,opt"` JsonFeed proto.Option[string] `protobuf:"bytes,8,opt"`
ClientContent *StClientContent `protobuf:"bytes,9,opt"` ClientContent *StClientContent `protobuf:"bytes,9,opt"`
}
func (x *StAlterFeedReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StAlterFeedReq) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StAlterFeedReq) GetBusiReqData() []byte {
if x != nil {
return x.BusiReqData
}
return nil
}
func (x *StAlterFeedReq) GetMBitmap() uint64 {
if x != nil && x.MBitmap != nil {
return *x.MBitmap
}
return 0
}
func (x *StAlterFeedReq) GetFrom() int32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StAlterFeedReq) GetSrc() int32 {
if x != nil && x.Src != nil {
return *x.Src
}
return 0
}
func (x *StAlterFeedReq) GetAlterFeedExtInfo() []*CommonEntry {
if x != nil {
return x.AlterFeedExtInfo
}
return nil
}
func (x *StAlterFeedReq) GetJsonFeed() string {
if x != nil && x.JsonFeed != nil {
return *x.JsonFeed
}
return ""
}
func (x *StAlterFeedReq) GetClientContent() *StClientContent {
if x != nil {
return x.ClientContent
}
return nil
} }
type StAlterFeedRsp struct { type StAlterFeedRsp struct {
@ -84,211 +25,47 @@ type StAlterFeedRsp struct {
BusiRspData []byte `protobuf:"bytes,3,opt"` BusiRspData []byte `protobuf:"bytes,3,opt"`
} }
func (x *StAlterFeedRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StAlterFeedRsp) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StAlterFeedRsp) GetBusiRspData() []byte {
if x != nil {
return x.BusiRspData
}
return nil
}
type StClientContent struct { type StClientContent struct {
ClientImageContents []*StClientImageContent `protobuf:"bytes,1,rep"` ClientImageContents []*StClientImageContent `protobuf:"bytes,1,rep"`
ClientVideoContents []*StClientVideoContent `protobuf:"bytes,2,rep"` ClientVideoContents []*StClientVideoContent `protobuf:"bytes,2,rep"`
} }
func (x *StClientContent) GetClientImageContents() []*StClientImageContent {
if x != nil {
return x.ClientImageContents
}
return nil
}
func (x *StClientContent) GetClientVideoContents() []*StClientVideoContent {
if x != nil {
return x.ClientVideoContents
}
return nil
}
type StClientImageContent struct { type StClientImageContent struct {
TaskId *string `protobuf:"bytes,1,opt"` TaskId proto.Option[string] `protobuf:"bytes,1,opt"`
PicId *string `protobuf:"bytes,2,opt"` PicId proto.Option[string] `protobuf:"bytes,2,opt"`
Url *string `protobuf:"bytes,3,opt"` Url proto.Option[string] `protobuf:"bytes,3,opt"`
} _ [0]func()
func (x *StClientImageContent) GetTaskId() string {
if x != nil && x.TaskId != nil {
return *x.TaskId
}
return ""
}
func (x *StClientImageContent) GetPicId() string {
if x != nil && x.PicId != nil {
return *x.PicId
}
return ""
}
func (x *StClientImageContent) GetUrl() string {
if x != nil && x.Url != nil {
return *x.Url
}
return ""
} }
type StClientVideoContent struct { type StClientVideoContent struct {
TaskId *string `protobuf:"bytes,1,opt"` TaskId proto.Option[string] `protobuf:"bytes,1,opt"`
VideoId *string `protobuf:"bytes,2,opt"` VideoId proto.Option[string] `protobuf:"bytes,2,opt"`
VideoUrl *string `protobuf:"bytes,3,opt"` VideoUrl proto.Option[string] `protobuf:"bytes,3,opt"`
CoverUrl *string `protobuf:"bytes,4,opt"` CoverUrl proto.Option[string] `protobuf:"bytes,4,opt"`
} _ [0]func()
func (x *StClientVideoContent) GetTaskId() string {
if x != nil && x.TaskId != nil {
return *x.TaskId
}
return ""
}
func (x *StClientVideoContent) GetVideoId() string {
if x != nil && x.VideoId != nil {
return *x.VideoId
}
return ""
}
func (x *StClientVideoContent) GetVideoUrl() string {
if x != nil && x.VideoUrl != nil {
return *x.VideoUrl
}
return ""
}
func (x *StClientVideoContent) GetCoverUrl() string {
if x != nil && x.CoverUrl != nil {
return *x.CoverUrl
}
return ""
} }
type StDelFeedReq struct { type StDelFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"` Feed *StFeed `protobuf:"bytes,2,opt"`
From *int32 `protobuf:"varint,3,opt"` From proto.Option[int32] `protobuf:"varint,3,opt"`
Src *int32 `protobuf:"varint,4,opt"` Src proto.Option[int32] `protobuf:"varint,4,opt"`
} _ [0]func()
func (x *StDelFeedReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StDelFeedReq) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StDelFeedReq) GetFrom() int32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StDelFeedReq) GetSrc() int32 {
if x != nil && x.Src != nil {
return *x.Src
}
return 0
} }
type StDelFeedRsp struct { type StDelFeedRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *StDelFeedRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
} }
type StDoCommentReq struct { type StDoCommentReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
CommentType *uint32 `protobuf:"varint,2,opt"` CommentType proto.Option[uint32] `protobuf:"varint,2,opt"`
Comment *StComment `protobuf:"bytes,3,opt"` Comment *StComment `protobuf:"bytes,3,opt"`
Feed *StFeed `protobuf:"bytes,4,opt"` Feed *StFeed `protobuf:"bytes,4,opt"`
From *int32 `protobuf:"varint,5,opt"` From proto.Option[int32] `protobuf:"varint,5,opt"`
BusiReqData []byte `protobuf:"bytes,6,opt"` BusiReqData []byte `protobuf:"bytes,6,opt"`
Src *int32 `protobuf:"varint,7,opt"` Src proto.Option[int32] `protobuf:"varint,7,opt"`
}
func (x *StDoCommentReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StDoCommentReq) GetCommentType() uint32 {
if x != nil && x.CommentType != nil {
return *x.CommentType
}
return 0
}
func (x *StDoCommentReq) GetComment() *StComment {
if x != nil {
return x.Comment
}
return nil
}
func (x *StDoCommentReq) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StDoCommentReq) GetFrom() int32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StDoCommentReq) GetBusiReqData() []byte {
if x != nil {
return x.BusiReqData
}
return nil
}
func (x *StDoCommentReq) GetSrc() int32 {
if x != nil && x.Src != nil {
return *x.Src
}
return 0
} }
type StDoCommentRsp struct { type StDoCommentRsp struct {
@ -297,110 +74,19 @@ type StDoCommentRsp struct {
BusiRspData []byte `protobuf:"bytes,3,opt"` BusiRspData []byte `protobuf:"bytes,3,opt"`
} }
func (x *StDoCommentRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StDoCommentRsp) GetComment() *StComment {
if x != nil {
return x.Comment
}
return nil
}
func (x *StDoCommentRsp) GetBusiRspData() []byte {
if x != nil {
return x.BusiRspData
}
return nil
}
type StDoLikeReq struct { type StDoLikeReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
LikeType *uint32 `protobuf:"varint,2,opt"` LikeType proto.Option[uint32] `protobuf:"varint,2,opt"`
Like *StLike `protobuf:"bytes,3,opt"` Like *StLike `protobuf:"bytes,3,opt"`
Feed *StFeed `protobuf:"bytes,4,opt"` Feed *StFeed `protobuf:"bytes,4,opt"`
BusiReqData []byte `protobuf:"bytes,5,opt"` BusiReqData []byte `protobuf:"bytes,5,opt"`
Comment *StComment `protobuf:"bytes,6,opt"` Comment *StComment `protobuf:"bytes,6,opt"`
Reply *StReply `protobuf:"bytes,7,opt"` Reply *StReply `protobuf:"bytes,7,opt"`
From *int32 `protobuf:"varint,8,opt"` From proto.Option[int32] `protobuf:"varint,8,opt"`
Src *int32 `protobuf:"varint,9,opt"` Src proto.Option[int32] `protobuf:"varint,9,opt"`
EmotionReaction *StEmotionReactionInfo `protobuf:"bytes,10,opt"` EmotionReaction *StEmotionReactionInfo `protobuf:"bytes,10,opt"`
} }
func (x *StDoLikeReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StDoLikeReq) GetLikeType() uint32 {
if x != nil && x.LikeType != nil {
return *x.LikeType
}
return 0
}
func (x *StDoLikeReq) GetLike() *StLike {
if x != nil {
return x.Like
}
return nil
}
func (x *StDoLikeReq) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StDoLikeReq) GetBusiReqData() []byte {
if x != nil {
return x.BusiReqData
}
return nil
}
func (x *StDoLikeReq) GetComment() *StComment {
if x != nil {
return x.Comment
}
return nil
}
func (x *StDoLikeReq) GetReply() *StReply {
if x != nil {
return x.Reply
}
return nil
}
func (x *StDoLikeReq) GetFrom() int32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StDoLikeReq) GetSrc() int32 {
if x != nil && x.Src != nil {
return *x.Src
}
return 0
}
func (x *StDoLikeReq) GetEmotionReaction() *StEmotionReactionInfo {
if x != nil {
return x.EmotionReaction
}
return nil
}
type StDoLikeRsp struct { type StDoLikeRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Like *StLike `protobuf:"bytes,2,opt"` Like *StLike `protobuf:"bytes,2,opt"`
@ -408,99 +94,15 @@ type StDoLikeRsp struct {
EmotionReaction *StEmotionReactionInfo `protobuf:"bytes,4,opt"` EmotionReaction *StEmotionReactionInfo `protobuf:"bytes,4,opt"`
} }
func (x *StDoLikeRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StDoLikeRsp) GetLike() *StLike {
if x != nil {
return x.Like
}
return nil
}
func (x *StDoLikeRsp) GetBusiRspData() []byte {
if x != nil {
return x.BusiRspData
}
return nil
}
func (x *StDoLikeRsp) GetEmotionReaction() *StEmotionReactionInfo {
if x != nil {
return x.EmotionReaction
}
return nil
}
type StDoReplyReq struct { type StDoReplyReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
ReplyType *uint32 `protobuf:"varint,2,opt"` ReplyType proto.Option[uint32] `protobuf:"varint,2,opt"`
Reply *StReply `protobuf:"bytes,3,opt"` Reply *StReply `protobuf:"bytes,3,opt"`
Comment *StComment `protobuf:"bytes,4,opt"` Comment *StComment `protobuf:"bytes,4,opt"`
Feed *StFeed `protobuf:"bytes,5,opt"` Feed *StFeed `protobuf:"bytes,5,opt"`
From *int32 `protobuf:"varint,6,opt"` From proto.Option[int32] `protobuf:"varint,6,opt"`
BusiReqData []byte `protobuf:"bytes,7,opt"` BusiReqData []byte `protobuf:"bytes,7,opt"`
Src *int32 `protobuf:"varint,8,opt"` Src proto.Option[int32] `protobuf:"varint,8,opt"`
}
func (x *StDoReplyReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StDoReplyReq) GetReplyType() uint32 {
if x != nil && x.ReplyType != nil {
return *x.ReplyType
}
return 0
}
func (x *StDoReplyReq) GetReply() *StReply {
if x != nil {
return x.Reply
}
return nil
}
func (x *StDoReplyReq) GetComment() *StComment {
if x != nil {
return x.Comment
}
return nil
}
func (x *StDoReplyReq) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StDoReplyReq) GetFrom() int32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StDoReplyReq) GetBusiReqData() []byte {
if x != nil {
return x.BusiReqData
}
return nil
}
func (x *StDoReplyReq) GetSrc() int32 {
if x != nil && x.Src != nil {
return *x.Src
}
return 0
} }
type StDoReplyRsp struct { type StDoReplyRsp struct {
@ -509,138 +111,28 @@ type StDoReplyRsp struct {
BusiRspData []byte `protobuf:"bytes,3,opt"` BusiRspData []byte `protobuf:"bytes,3,opt"`
} }
func (x *StDoReplyRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StDoReplyRsp) GetReply() *StReply {
if x != nil {
return x.Reply
}
return nil
}
func (x *StDoReplyRsp) GetBusiRspData() []byte {
if x != nil {
return x.BusiRspData
}
return nil
}
type StDoSecurityReq struct { type StDoSecurityReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"` Feed *StFeed `protobuf:"bytes,2,opt"`
Comment *StComment `protobuf:"bytes,3,opt"` Comment *StComment `protobuf:"bytes,3,opt"`
Reply *StReply `protobuf:"bytes,4,opt"` Reply *StReply `protobuf:"bytes,4,opt"`
Poster *StUser `protobuf:"bytes,5,opt"` Poster *StUser `protobuf:"bytes,5,opt"`
SecType *int32 `protobuf:"varint,6,opt"` SecType proto.Option[int32] `protobuf:"varint,6,opt"`
} _ [0]func()
func (x *StDoSecurityReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StDoSecurityReq) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StDoSecurityReq) GetComment() *StComment {
if x != nil {
return x.Comment
}
return nil
}
func (x *StDoSecurityReq) GetReply() *StReply {
if x != nil {
return x.Reply
}
return nil
}
func (x *StDoSecurityReq) GetPoster() *StUser {
if x != nil {
return x.Poster
}
return nil
}
func (x *StDoSecurityReq) GetSecType() int32 {
if x != nil && x.SecType != nil {
return *x.SecType
}
return 0
} }
type StDoSecurityRsp struct { type StDoSecurityRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *StDoSecurityRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
} }
type StModifyFeedReq struct { type StModifyFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"` Feed *StFeed `protobuf:"bytes,2,opt"`
MBitmap *uint64 `protobuf:"varint,3,opt"` MBitmap proto.Option[uint64] `protobuf:"varint,3,opt"`
From *int32 `protobuf:"varint,4,opt"` From proto.Option[int32] `protobuf:"varint,4,opt"`
Src *int32 `protobuf:"varint,5,opt"` Src proto.Option[int32] `protobuf:"varint,5,opt"`
ModifyFeedExtInfo []*CommonEntry `protobuf:"bytes,6,rep"` ModifyFeedExtInfo []*CommonEntry `protobuf:"bytes,6,rep"`
}
func (x *StModifyFeedReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StModifyFeedReq) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StModifyFeedReq) GetMBitmap() uint64 {
if x != nil && x.MBitmap != nil {
return *x.MBitmap
}
return 0
}
func (x *StModifyFeedReq) GetFrom() int32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StModifyFeedReq) GetSrc() int32 {
if x != nil && x.Src != nil {
return *x.Src
}
return 0
}
func (x *StModifyFeedReq) GetModifyFeedExtInfo() []*CommonEntry {
if x != nil {
return x.ModifyFeedExtInfo
}
return nil
} }
type StModifyFeedRsp struct { type StModifyFeedRsp struct {
@ -649,92 +141,15 @@ type StModifyFeedRsp struct {
BusiRspData []byte `protobuf:"bytes,3,opt"` BusiRspData []byte `protobuf:"bytes,3,opt"`
} }
func (x *StModifyFeedRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StModifyFeedRsp) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StModifyFeedRsp) GetBusiRspData() []byte {
if x != nil {
return x.BusiRspData
}
return nil
}
type StPublishFeedReq struct { type StPublishFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"` ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"` Feed *StFeed `protobuf:"bytes,2,opt"`
BusiReqData []byte `protobuf:"bytes,3,opt"` BusiReqData []byte `protobuf:"bytes,3,opt"`
From *int32 `protobuf:"varint,4,opt"` From proto.Option[int32] `protobuf:"varint,4,opt"`
Src *int32 `protobuf:"varint,5,opt"` Src proto.Option[int32] `protobuf:"varint,5,opt"`
StoreFeedExtInfo []*CommonEntry `protobuf:"bytes,6,rep"` StoreFeedExtInfo []*CommonEntry `protobuf:"bytes,6,rep"`
JsonFeed *string `protobuf:"bytes,7,opt"` JsonFeed proto.Option[string] `protobuf:"bytes,7,opt"`
ClientContent *StClientContent `protobuf:"bytes,8,opt"` ClientContent *StClientContent `protobuf:"bytes,8,opt"`
}
func (x *StPublishFeedReq) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StPublishFeedReq) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StPublishFeedReq) GetBusiReqData() []byte {
if x != nil {
return x.BusiReqData
}
return nil
}
func (x *StPublishFeedReq) GetFrom() int32 {
if x != nil && x.From != nil {
return *x.From
}
return 0
}
func (x *StPublishFeedReq) GetSrc() int32 {
if x != nil && x.Src != nil {
return *x.Src
}
return 0
}
func (x *StPublishFeedReq) GetStoreFeedExtInfo() []*CommonEntry {
if x != nil {
return x.StoreFeedExtInfo
}
return nil
}
func (x *StPublishFeedReq) GetJsonFeed() string {
if x != nil && x.JsonFeed != nil {
return *x.JsonFeed
}
return ""
}
func (x *StPublishFeedReq) GetClientContent() *StClientContent {
if x != nil {
return x.ClientContent
}
return nil
} }
type StPublishFeedRsp struct { type StPublishFeedRsp struct {
@ -742,24 +157,3 @@ type StPublishFeedRsp struct {
Feed *StFeed `protobuf:"bytes,2,opt"` Feed *StFeed `protobuf:"bytes,2,opt"`
BusiRspData []byte `protobuf:"bytes,3,opt"` BusiRspData []byte `protobuf:"bytes,3,opt"`
} }
func (x *StPublishFeedRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
func (x *StPublishFeedRsp) GetFeed() *StFeed {
if x != nil {
return x.Feed
}
return nil
}
func (x *StPublishFeedRsp) GetBusiRspData() []byte {
if x != nil {
return x.BusiRspData
}
return nil
}

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/GuildFeedCloudMeta.proto"; import "pb/channel/GuildFeedCloudMeta.proto";
import "pb/channel/GuildChannelBase.proto"; import "pb/channel/GuildChannelBase.proto";

View File

@ -3,145 +3,45 @@
package channel package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type BatchGetMsgRspCountReq struct { type BatchGetMsgRspCountReq struct {
GuildMsgList []*GuildMsg `protobuf:"bytes,1,rep"` GuildMsgList []*GuildMsg `protobuf:"bytes,1,rep"`
} }
func (x *BatchGetMsgRspCountReq) GetGuildMsgList() []*GuildMsg {
if x != nil {
return x.GuildMsgList
}
return nil
}
type BatchGetMsgRspCountRsp struct { type BatchGetMsgRspCountRsp struct {
GuildMsgInfoList []*GuildMsgInfo `protobuf:"bytes,1,rep"` GuildMsgInfoList []*GuildMsgInfo `protobuf:"bytes,1,rep"`
} }
func (x *BatchGetMsgRspCountRsp) GetGuildMsgInfoList() []*GuildMsgInfo {
if x != nil {
return x.GuildMsgInfoList
}
return nil
}
type SvrChannelMsg struct { type SvrChannelMsg struct {
ChannelId *uint64 `protobuf:"varint,1,opt"` ChannelId proto.Option[uint64] `protobuf:"varint,1,opt"`
Id []*MsgId `protobuf:"bytes,2,rep"` Id []*MsgId `protobuf:"bytes,2,rep"`
}
func (x *SvrChannelMsg) GetChannelId() uint64 {
if x != nil && x.ChannelId != nil {
return *x.ChannelId
}
return 0
}
func (x *SvrChannelMsg) GetId() []*MsgId {
if x != nil {
return x.Id
}
return nil
} }
type ChannelMsgInfo struct { type ChannelMsgInfo struct {
ChannelId *uint64 `protobuf:"varint,1,opt"` ChannelId proto.Option[uint64] `protobuf:"varint,1,opt"`
RespData []*MsgRespData `protobuf:"bytes,2,rep"` RespData []*MsgRespData `protobuf:"bytes,2,rep"`
}
func (x *ChannelMsgInfo) GetChannelId() uint64 {
if x != nil && x.ChannelId != nil {
return *x.ChannelId
}
return 0
}
func (x *ChannelMsgInfo) GetRespData() []*MsgRespData {
if x != nil {
return x.RespData
}
return nil
} }
type EmojiReaction struct { type EmojiReaction struct {
EmojiId *string `protobuf:"bytes,1,opt"` EmojiId proto.Option[string] `protobuf:"bytes,1,opt"`
EmojiType *uint64 `protobuf:"varint,2,opt"` EmojiType proto.Option[uint64] `protobuf:"varint,2,opt"`
Cnt *uint64 `protobuf:"varint,3,opt"` Cnt proto.Option[uint64] `protobuf:"varint,3,opt"`
IsClicked *bool `protobuf:"varint,4,opt"` IsClicked proto.Option[bool] `protobuf:"varint,4,opt"`
IsDefaultEmoji *bool `protobuf:"varint,10001,opt"` IsDefaultEmoji proto.Option[bool] `protobuf:"varint,10001,opt"`
} _ [0]func()
func (x *EmojiReaction) GetEmojiId() string {
if x != nil && x.EmojiId != nil {
return *x.EmojiId
}
return ""
}
func (x *EmojiReaction) GetEmojiType() uint64 {
if x != nil && x.EmojiType != nil {
return *x.EmojiType
}
return 0
}
func (x *EmojiReaction) GetCnt() uint64 {
if x != nil && x.Cnt != nil {
return *x.Cnt
}
return 0
}
func (x *EmojiReaction) GetIsClicked() bool {
if x != nil && x.IsClicked != nil {
return *x.IsClicked
}
return false
}
func (x *EmojiReaction) GetIsDefaultEmoji() bool {
if x != nil && x.IsDefaultEmoji != nil {
return *x.IsDefaultEmoji
}
return false
} }
type GuildMsg struct { type GuildMsg struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelMsgList []*SvrChannelMsg `protobuf:"bytes,2,rep"` ChannelMsgList []*SvrChannelMsg `protobuf:"bytes,2,rep"`
}
func (x *GuildMsg) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *GuildMsg) GetChannelMsgList() []*SvrChannelMsg {
if x != nil {
return x.ChannelMsgList
}
return nil
} }
type GuildMsgInfo struct { type GuildMsgInfo struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelMsgInfoList []*ChannelMsgInfo `protobuf:"bytes,2,rep"` ChannelMsgInfoList []*ChannelMsgInfo `protobuf:"bytes,2,rep"`
}
func (x *GuildMsgInfo) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *GuildMsgInfo) GetChannelMsgInfoList() []*ChannelMsgInfo {
if x != nil {
return x.ChannelMsgInfoList
}
return nil
} }
type MsgCnt struct { type MsgCnt struct {
@ -149,54 +49,13 @@ type MsgCnt struct {
EmojiReaction []*EmojiReaction `protobuf:"bytes,2,rep"` EmojiReaction []*EmojiReaction `protobuf:"bytes,2,rep"`
} }
func (x *MsgCnt) GetId() *MsgId {
if x != nil {
return x.Id
}
return nil
}
func (x *MsgCnt) GetEmojiReaction() []*EmojiReaction {
if x != nil {
return x.EmojiReaction
}
return nil
}
type MsgId struct { type MsgId struct {
Version *uint64 `protobuf:"varint,1,opt"` Version proto.Option[uint64] `protobuf:"varint,1,opt"`
Seq *uint64 `protobuf:"varint,2,opt"` Seq proto.Option[uint64] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *MsgId) GetVersion() uint64 {
if x != nil && x.Version != nil {
return *x.Version
}
return 0
}
func (x *MsgId) GetSeq() uint64 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
} }
type MsgRespData struct { type MsgRespData struct {
Id *MsgId `protobuf:"bytes,1,opt"` Id *MsgId `protobuf:"bytes,1,opt"`
Cnt []byte `protobuf:"bytes,2,opt"` Cnt []byte `protobuf:"bytes,2,opt"`
} }
func (x *MsgRespData) GetId() *MsgId {
if x != nil {
return x.Id
}
return nil
}
func (x *MsgRespData) GetCnt() []byte {
if x != nil {
return x.Cnt
}
return nil
}

View File

@ -1,6 +1,6 @@
syntax = "proto2"; syntax = "proto2";
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
message BatchGetMsgRspCountReq { message BatchGetMsgRspCountReq {
repeated GuildMsg guildMsgList = 1; repeated GuildMsg guildMsgList = 1;

View File

@ -5,374 +5,77 @@ package channel
import ( import (
msg "github.com/Mrs4s/MiraiGo/client/pb/msg" msg "github.com/Mrs4s/MiraiGo/client/pb/msg"
proto "github.com/RomiChan/protobuf/proto"
) )
type ChannelContentHead struct { type ChannelContentHead struct {
Type *uint64 `protobuf:"varint,1,opt"` Type proto.Option[uint64] `protobuf:"varint,1,opt"`
SubType *uint64 `protobuf:"varint,2,opt"` SubType proto.Option[uint64] `protobuf:"varint,2,opt"`
Random *uint64 `protobuf:"varint,3,opt"` Random proto.Option[uint64] `protobuf:"varint,3,opt"`
Seq *uint64 `protobuf:"varint,4,opt"` Seq proto.Option[uint64] `protobuf:"varint,4,opt"`
CntSeq *uint64 `protobuf:"varint,5,opt"` CntSeq proto.Option[uint64] `protobuf:"varint,5,opt"`
Time *uint64 `protobuf:"varint,6,opt"` Time proto.Option[uint64] `protobuf:"varint,6,opt"`
Meta []byte `protobuf:"bytes,7,opt"` Meta []byte `protobuf:"bytes,7,opt"`
}
func (x *ChannelContentHead) GetType() uint64 {
if x != nil && x.Type != nil {
return *x.Type
}
return 0
}
func (x *ChannelContentHead) GetSubType() uint64 {
if x != nil && x.SubType != nil {
return *x.SubType
}
return 0
}
func (x *ChannelContentHead) GetRandom() uint64 {
if x != nil && x.Random != nil {
return *x.Random
}
return 0
}
func (x *ChannelContentHead) GetSeq() uint64 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *ChannelContentHead) GetCntSeq() uint64 {
if x != nil && x.CntSeq != nil {
return *x.CntSeq
}
return 0
}
func (x *ChannelContentHead) GetTime() uint64 {
if x != nil && x.Time != nil {
return *x.Time
}
return 0
}
func (x *ChannelContentHead) GetMeta() []byte {
if x != nil {
return x.Meta
}
return nil
} }
type DirectMessageMember struct { type DirectMessageMember struct {
Uin *uint64 `protobuf:"varint,1,opt"` Uin proto.Option[uint64] `protobuf:"varint,1,opt"`
Tinyid *uint64 `protobuf:"varint,2,opt"` Tinyid proto.Option[uint64] `protobuf:"varint,2,opt"`
SourceGuildId *uint64 `protobuf:"varint,3,opt"` SourceGuildId proto.Option[uint64] `protobuf:"varint,3,opt"`
SourceGuildName []byte `protobuf:"bytes,4,opt"` SourceGuildName []byte `protobuf:"bytes,4,opt"`
NickName []byte `protobuf:"bytes,5,opt"` NickName []byte `protobuf:"bytes,5,opt"`
MemberName []byte `protobuf:"bytes,6,opt"` MemberName []byte `protobuf:"bytes,6,opt"`
NotifyType *uint32 `protobuf:"varint,7,opt"` NotifyType proto.Option[uint32] `protobuf:"varint,7,opt"`
}
func (x *DirectMessageMember) GetUin() uint64 {
if x != nil && x.Uin != nil {
return *x.Uin
}
return 0
}
func (x *DirectMessageMember) GetTinyid() uint64 {
if x != nil && x.Tinyid != nil {
return *x.Tinyid
}
return 0
}
func (x *DirectMessageMember) GetSourceGuildId() uint64 {
if x != nil && x.SourceGuildId != nil {
return *x.SourceGuildId
}
return 0
}
func (x *DirectMessageMember) GetSourceGuildName() []byte {
if x != nil {
return x.SourceGuildName
}
return nil
}
func (x *DirectMessageMember) GetNickName() []byte {
if x != nil {
return x.NickName
}
return nil
}
func (x *DirectMessageMember) GetMemberName() []byte {
if x != nil {
return x.MemberName
}
return nil
}
func (x *DirectMessageMember) GetNotifyType() uint32 {
if x != nil && x.NotifyType != nil {
return *x.NotifyType
}
return 0
} }
type ChannelEvent struct { type ChannelEvent struct {
Type *uint64 `protobuf:"varint,1,opt"` Type proto.Option[uint64] `protobuf:"varint,1,opt"`
Version *uint64 `protobuf:"varint,2,opt"` Version proto.Option[uint64] `protobuf:"varint,2,opt"`
OpInfo *ChannelMsgOpInfo `protobuf:"bytes,3,opt"` OpInfo *ChannelMsgOpInfo `protobuf:"bytes,3,opt"`
} _ [0]func()
func (x *ChannelEvent) GetType() uint64 {
if x != nil && x.Type != nil {
return *x.Type
}
return 0
}
func (x *ChannelEvent) GetVersion() uint64 {
if x != nil && x.Version != nil {
return *x.Version
}
return 0
}
func (x *ChannelEvent) GetOpInfo() *ChannelMsgOpInfo {
if x != nil {
return x.OpInfo
}
return nil
} }
type ChannelExtInfo struct { type ChannelExtInfo struct {
FromNick []byte `protobuf:"bytes,1,opt"` FromNick []byte `protobuf:"bytes,1,opt"`
GuildName []byte `protobuf:"bytes,2,opt"` GuildName []byte `protobuf:"bytes,2,opt"`
ChannelName []byte `protobuf:"bytes,3,opt"` ChannelName []byte `protobuf:"bytes,3,opt"`
Visibility *uint32 `protobuf:"varint,4,opt"` Visibility proto.Option[uint32] `protobuf:"varint,4,opt"`
NotifyType *uint32 `protobuf:"varint,5,opt"` NotifyType proto.Option[uint32] `protobuf:"varint,5,opt"`
OfflineFlag *uint32 `protobuf:"varint,6,opt"` OfflineFlag proto.Option[uint32] `protobuf:"varint,6,opt"`
NameType *uint32 `protobuf:"varint,7,opt"` NameType proto.Option[uint32] `protobuf:"varint,7,opt"`
MemberName []byte `protobuf:"bytes,8,opt"` MemberName []byte `protobuf:"bytes,8,opt"`
Timestamp *uint32 `protobuf:"varint,9,opt"` Timestamp proto.Option[uint32] `protobuf:"varint,9,opt"`
EventVersion *uint64 `protobuf:"varint,10,opt"` EventVersion proto.Option[uint64] `protobuf:"varint,10,opt"`
Events []*ChannelEvent `protobuf:"bytes,11,rep"` Events []*ChannelEvent `protobuf:"bytes,11,rep"`
FromRoleInfo *ChannelRole `protobuf:"bytes,12,opt"` FromRoleInfo *ChannelRole `protobuf:"bytes,12,opt"`
FreqLimitInfo *ChannelFreqLimitInfo `protobuf:"bytes,13,opt"` FreqLimitInfo *ChannelFreqLimitInfo `protobuf:"bytes,13,opt"`
DirectMessageMember []*DirectMessageMember `protobuf:"bytes,14,rep"` DirectMessageMember []*DirectMessageMember `protobuf:"bytes,14,rep"`
} }
func (x *ChannelExtInfo) GetFromNick() []byte {
if x != nil {
return x.FromNick
}
return nil
}
func (x *ChannelExtInfo) GetGuildName() []byte {
if x != nil {
return x.GuildName
}
return nil
}
func (x *ChannelExtInfo) GetChannelName() []byte {
if x != nil {
return x.ChannelName
}
return nil
}
func (x *ChannelExtInfo) GetVisibility() uint32 {
if x != nil && x.Visibility != nil {
return *x.Visibility
}
return 0
}
func (x *ChannelExtInfo) GetNotifyType() uint32 {
if x != nil && x.NotifyType != nil {
return *x.NotifyType
}
return 0
}
func (x *ChannelExtInfo) GetOfflineFlag() uint32 {
if x != nil && x.OfflineFlag != nil {
return *x.OfflineFlag
}
return 0
}
func (x *ChannelExtInfo) GetNameType() uint32 {
if x != nil && x.NameType != nil {
return *x.NameType
}
return 0
}
func (x *ChannelExtInfo) GetMemberName() []byte {
if x != nil {
return x.MemberName
}
return nil
}
func (x *ChannelExtInfo) GetTimestamp() uint32 {
if x != nil && x.Timestamp != nil {
return *x.Timestamp
}
return 0
}
func (x *ChannelExtInfo) GetEventVersion() uint64 {
if x != nil && x.EventVersion != nil {
return *x.EventVersion
}
return 0
}
func (x *ChannelExtInfo) GetEvents() []*ChannelEvent {
if x != nil {
return x.Events
}
return nil
}
func (x *ChannelExtInfo) GetFromRoleInfo() *ChannelRole {
if x != nil {
return x.FromRoleInfo
}
return nil
}
func (x *ChannelExtInfo) GetFreqLimitInfo() *ChannelFreqLimitInfo {
if x != nil {
return x.FreqLimitInfo
}
return nil
}
func (x *ChannelExtInfo) GetDirectMessageMember() []*DirectMessageMember {
if x != nil {
return x.DirectMessageMember
}
return nil
}
type ChannelFreqLimitInfo struct { type ChannelFreqLimitInfo struct {
IsLimited *uint32 `protobuf:"varint,1,opt"` IsLimited proto.Option[uint32] `protobuf:"varint,1,opt"`
LeftCount *uint32 `protobuf:"varint,2,opt"` LeftCount proto.Option[uint32] `protobuf:"varint,2,opt"`
LimitTimestamp *uint64 `protobuf:"varint,3,opt"` LimitTimestamp proto.Option[uint64] `protobuf:"varint,3,opt"`
} _ [0]func()
func (x *ChannelFreqLimitInfo) GetIsLimited() uint32 {
if x != nil && x.IsLimited != nil {
return *x.IsLimited
}
return 0
}
func (x *ChannelFreqLimitInfo) GetLeftCount() uint32 {
if x != nil && x.LeftCount != nil {
return *x.LeftCount
}
return 0
}
func (x *ChannelFreqLimitInfo) GetLimitTimestamp() uint64 {
if x != nil && x.LimitTimestamp != nil {
return *x.LimitTimestamp
}
return 0
} }
type ChannelInfo struct { type ChannelInfo struct {
Id *uint64 `protobuf:"varint,1,opt"` Id proto.Option[uint64] `protobuf:"varint,1,opt"`
Name []byte `protobuf:"bytes,2,opt"` Name []byte `protobuf:"bytes,2,opt"`
Color *uint32 `protobuf:"varint,3,opt"` Color proto.Option[uint32] `protobuf:"varint,3,opt"`
Hoist *uint32 `protobuf:"varint,4,opt"` Hoist proto.Option[uint32] `protobuf:"varint,4,opt"`
}
func (x *ChannelInfo) GetId() uint64 {
if x != nil && x.Id != nil {
return *x.Id
}
return 0
}
func (x *ChannelInfo) GetName() []byte {
if x != nil {
return x.Name
}
return nil
}
func (x *ChannelInfo) GetColor() uint32 {
if x != nil && x.Color != nil {
return *x.Color
}
return 0
}
func (x *ChannelInfo) GetHoist() uint32 {
if x != nil && x.Hoist != nil {
return *x.Hoist
}
return 0
} }
type ChannelLoginSig struct { type ChannelLoginSig struct {
Type *uint32 `protobuf:"varint,1,opt"` Type proto.Option[uint32] `protobuf:"varint,1,opt"`
Sig []byte `protobuf:"bytes,2,opt"` Sig []byte `protobuf:"bytes,2,opt"`
Appid *uint32 `protobuf:"varint,3,opt"` Appid proto.Option[uint32] `protobuf:"varint,3,opt"`
}
func (x *ChannelLoginSig) GetType() uint32 {
if x != nil && x.Type != nil {
return *x.Type
}
return 0
}
func (x *ChannelLoginSig) GetSig() []byte {
if x != nil {
return x.Sig
}
return nil
}
func (x *ChannelLoginSig) GetAppid() uint32 {
if x != nil && x.Appid != nil {
return *x.Appid
}
return 0
} }
type ChannelMeta struct { type ChannelMeta struct {
FromUin *uint64 `protobuf:"varint,1,opt"` FromUin proto.Option[uint64] `protobuf:"varint,1,opt"`
LoginSig *ChannelLoginSig `protobuf:"bytes,2,opt"` LoginSig *ChannelLoginSig `protobuf:"bytes,2,opt"`
} _ [0]func()
func (x *ChannelMeta) GetFromUin() uint64 {
if x != nil && x.FromUin != nil {
return *x.FromUin
}
return 0
}
func (x *ChannelMeta) GetLoginSig() *ChannelLoginSig {
if x != nil {
return x.LoginSig
}
return nil
} }
type ChannelMsgContent struct { type ChannelMsgContent struct {
@ -380,303 +83,64 @@ type ChannelMsgContent struct {
CtrlHead *ChannelMsgCtrlHead `protobuf:"bytes,2,opt"` CtrlHead *ChannelMsgCtrlHead `protobuf:"bytes,2,opt"`
Body *msg.MessageBody `protobuf:"bytes,3,opt"` Body *msg.MessageBody `protobuf:"bytes,3,opt"`
ExtInfo *ChannelExtInfo `protobuf:"bytes,4,opt"` ExtInfo *ChannelExtInfo `protobuf:"bytes,4,opt"`
} _ [0]func()
func (x *ChannelMsgContent) GetHead() *ChannelMsgHead {
if x != nil {
return x.Head
}
return nil
}
func (x *ChannelMsgContent) GetCtrlHead() *ChannelMsgCtrlHead {
if x != nil {
return x.CtrlHead
}
return nil
}
func (x *ChannelMsgContent) GetBody() *msg.MessageBody {
if x != nil {
return x.Body
}
return nil
}
func (x *ChannelMsgContent) GetExtInfo() *ChannelExtInfo {
if x != nil {
return x.ExtInfo
}
return nil
} }
type ChannelMsgCtrlHead struct { type ChannelMsgCtrlHead struct {
IncludeUin [][]byte `protobuf:"bytes,1,rep"` IncludeUin [][]byte `protobuf:"bytes,1,rep"`
// repeated uint64 excludeUin = 2; // bytes? // repeated uint64 excludeUin = 2; // bytes?
// repeated uint64 featureid = 3; // repeated uint64 featureid = 3;
OfflineFlag *uint32 `protobuf:"varint,4,opt"` OfflineFlag proto.Option[uint32] `protobuf:"varint,4,opt"`
Visibility *uint32 `protobuf:"varint,5,opt"` Visibility proto.Option[uint32] `protobuf:"varint,5,opt"`
CtrlFlag *uint64 `protobuf:"varint,6,opt"` CtrlFlag proto.Option[uint64] `protobuf:"varint,6,opt"`
Events []*ChannelEvent `protobuf:"bytes,7,rep"` Events []*ChannelEvent `protobuf:"bytes,7,rep"`
Level *uint64 `protobuf:"varint,8,opt"` Level proto.Option[uint64] `protobuf:"varint,8,opt"`
PersonalLevels []*PersonalLevel `protobuf:"bytes,9,rep"` PersonalLevels []*PersonalLevel `protobuf:"bytes,9,rep"`
GuildSyncSeq *uint64 `protobuf:"varint,10,opt"` GuildSyncSeq proto.Option[uint64] `protobuf:"varint,10,opt"`
MemberNum *uint32 `protobuf:"varint,11,opt"` MemberNum proto.Option[uint32] `protobuf:"varint,11,opt"`
ChannelType *uint32 `protobuf:"varint,12,opt"` ChannelType proto.Option[uint32] `protobuf:"varint,12,opt"`
PrivateType *uint32 `protobuf:"varint,13,opt"` PrivateType proto.Option[uint32] `protobuf:"varint,13,opt"`
}
func (x *ChannelMsgCtrlHead) GetIncludeUin() [][]byte {
if x != nil {
return x.IncludeUin
}
return nil
}
func (x *ChannelMsgCtrlHead) GetOfflineFlag() uint32 {
if x != nil && x.OfflineFlag != nil {
return *x.OfflineFlag
}
return 0
}
func (x *ChannelMsgCtrlHead) GetVisibility() uint32 {
if x != nil && x.Visibility != nil {
return *x.Visibility
}
return 0
}
func (x *ChannelMsgCtrlHead) GetCtrlFlag() uint64 {
if x != nil && x.CtrlFlag != nil {
return *x.CtrlFlag
}
return 0
}
func (x *ChannelMsgCtrlHead) GetEvents() []*ChannelEvent {
if x != nil {
return x.Events
}
return nil
}
func (x *ChannelMsgCtrlHead) GetLevel() uint64 {
if x != nil && x.Level != nil {
return *x.Level
}
return 0
}
func (x *ChannelMsgCtrlHead) GetPersonalLevels() []*PersonalLevel {
if x != nil {
return x.PersonalLevels
}
return nil
}
func (x *ChannelMsgCtrlHead) GetGuildSyncSeq() uint64 {
if x != nil && x.GuildSyncSeq != nil {
return *x.GuildSyncSeq
}
return 0
}
func (x *ChannelMsgCtrlHead) GetMemberNum() uint32 {
if x != nil && x.MemberNum != nil {
return *x.MemberNum
}
return 0
}
func (x *ChannelMsgCtrlHead) GetChannelType() uint32 {
if x != nil && x.ChannelType != nil {
return *x.ChannelType
}
return 0
}
func (x *ChannelMsgCtrlHead) GetPrivateType() uint32 {
if x != nil && x.PrivateType != nil {
return *x.PrivateType
}
return 0
} }
type ChannelMsgHead struct { type ChannelMsgHead struct {
RoutingHead *ChannelRoutingHead `protobuf:"bytes,1,opt"` RoutingHead *ChannelRoutingHead `protobuf:"bytes,1,opt"`
ContentHead *ChannelContentHead `protobuf:"bytes,2,opt"` ContentHead *ChannelContentHead `protobuf:"bytes,2,opt"`
} _ [0]func()
func (x *ChannelMsgHead) GetRoutingHead() *ChannelRoutingHead {
if x != nil {
return x.RoutingHead
}
return nil
}
func (x *ChannelMsgHead) GetContentHead() *ChannelContentHead {
if x != nil {
return x.ContentHead
}
return nil
} }
type ChannelMsgMeta struct { type ChannelMsgMeta struct {
AtAllSeq *uint64 `protobuf:"varint,1,opt"` AtAllSeq proto.Option[uint64] `protobuf:"varint,1,opt"`
} _ [0]func()
func (x *ChannelMsgMeta) GetAtAllSeq() uint64 {
if x != nil && x.AtAllSeq != nil {
return *x.AtAllSeq
}
return 0
} }
type ChannelMsgOpInfo struct { type ChannelMsgOpInfo struct {
OperatorTinyid *uint64 `protobuf:"varint,1,opt"` OperatorTinyid proto.Option[uint64] `protobuf:"varint,1,opt"`
OperatorRole *uint64 `protobuf:"varint,2,opt"` OperatorRole proto.Option[uint64] `protobuf:"varint,2,opt"`
Reason *uint64 `protobuf:"varint,3,opt"` Reason proto.Option[uint64] `protobuf:"varint,3,opt"`
Timestamp *uint64 `protobuf:"varint,4,opt"` Timestamp proto.Option[uint64] `protobuf:"varint,4,opt"`
AtType *uint64 `protobuf:"varint,5,opt"` AtType proto.Option[uint64] `protobuf:"varint,5,opt"`
} _ [0]func()
func (x *ChannelMsgOpInfo) GetOperatorTinyid() uint64 {
if x != nil && x.OperatorTinyid != nil {
return *x.OperatorTinyid
}
return 0
}
func (x *ChannelMsgOpInfo) GetOperatorRole() uint64 {
if x != nil && x.OperatorRole != nil {
return *x.OperatorRole
}
return 0
}
func (x *ChannelMsgOpInfo) GetReason() uint64 {
if x != nil && x.Reason != nil {
return *x.Reason
}
return 0
}
func (x *ChannelMsgOpInfo) GetTimestamp() uint64 {
if x != nil && x.Timestamp != nil {
return *x.Timestamp
}
return 0
}
func (x *ChannelMsgOpInfo) GetAtType() uint64 {
if x != nil && x.AtType != nil {
return *x.AtType
}
return 0
} }
type PersonalLevel struct { type PersonalLevel struct {
ToUin *uint64 `protobuf:"varint,1,opt"` ToUin proto.Option[uint64] `protobuf:"varint,1,opt"`
Level *uint64 `protobuf:"varint,2,opt"` Level proto.Option[uint64] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *PersonalLevel) GetToUin() uint64 {
if x != nil && x.ToUin != nil {
return *x.ToUin
}
return 0
}
func (x *PersonalLevel) GetLevel() uint64 {
if x != nil && x.Level != nil {
return *x.Level
}
return 0
} }
type ChannelRole struct { type ChannelRole struct {
Id *uint64 `protobuf:"varint,1,opt"` Id proto.Option[uint64] `protobuf:"varint,1,opt"`
Info []byte `protobuf:"bytes,2,opt"` Info []byte `protobuf:"bytes,2,opt"`
Flag *uint32 `protobuf:"varint,3,opt"` Flag proto.Option[uint32] `protobuf:"varint,3,opt"`
}
func (x *ChannelRole) GetId() uint64 {
if x != nil && x.Id != nil {
return *x.Id
}
return 0
}
func (x *ChannelRole) GetInfo() []byte {
if x != nil {
return x.Info
}
return nil
}
func (x *ChannelRole) GetFlag() uint32 {
if x != nil && x.Flag != nil {
return *x.Flag
}
return 0
} }
type ChannelRoutingHead struct { type ChannelRoutingHead struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelId *uint64 `protobuf:"varint,2,opt"` ChannelId proto.Option[uint64] `protobuf:"varint,2,opt"`
FromUin *uint64 `protobuf:"varint,3,opt"` FromUin proto.Option[uint64] `protobuf:"varint,3,opt"`
FromTinyid *uint64 `protobuf:"varint,4,opt"` FromTinyid proto.Option[uint64] `protobuf:"varint,4,opt"`
GuildCode *uint64 `protobuf:"varint,5,opt"` GuildCode proto.Option[uint64] `protobuf:"varint,5,opt"`
FromAppid *uint64 `protobuf:"varint,6,opt"` FromAppid proto.Option[uint64] `protobuf:"varint,6,opt"`
DirectMessageFlag *uint32 `protobuf:"varint,7,opt"` DirectMessageFlag proto.Option[uint32] `protobuf:"varint,7,opt"`
} _ [0]func()
func (x *ChannelRoutingHead) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *ChannelRoutingHead) GetChannelId() uint64 {
if x != nil && x.ChannelId != nil {
return *x.ChannelId
}
return 0
}
func (x *ChannelRoutingHead) GetFromUin() uint64 {
if x != nil && x.FromUin != nil {
return *x.FromUin
}
return 0
}
func (x *ChannelRoutingHead) GetFromTinyid() uint64 {
if x != nil && x.FromTinyid != nil {
return *x.FromTinyid
}
return 0
}
func (x *ChannelRoutingHead) GetGuildCode() uint64 {
if x != nil && x.GuildCode != nil {
return *x.GuildCode
}
return 0
}
func (x *ChannelRoutingHead) GetFromAppid() uint64 {
if x != nil && x.FromAppid != nil {
return *x.FromAppid
}
return 0
}
func (x *ChannelRoutingHead) GetDirectMessageFlag() uint32 {
if x != nil && x.DirectMessageFlag != nil {
return *x.DirectMessageFlag
}
return 0
} }

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel;channel";
import "pb/msg/msg.proto"; import "pb/msg/msg.proto";

View File

@ -3,129 +3,35 @@
package channel package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type FocusInfo struct { type FocusInfo struct {
ChannelIdList []uint64 `protobuf:"varint,1,rep"` ChannelIdList []uint64 `protobuf:"varint,1,rep"`
} }
func (x *FocusInfo) GetChannelIdList() []uint64 {
if x != nil {
return x.ChannelIdList
}
return nil
}
type MsgOnlinePush struct { type MsgOnlinePush struct {
Msgs []*ChannelMsgContent `protobuf:"bytes,1,rep"` Msgs []*ChannelMsgContent `protobuf:"bytes,1,rep"`
GeneralFlag *uint32 `protobuf:"varint,2,opt"` GeneralFlag proto.Option[uint32] `protobuf:"varint,2,opt"`
NeedResp *uint32 `protobuf:"varint,3,opt"` NeedResp proto.Option[uint32] `protobuf:"varint,3,opt"`
ServerBuf []byte `protobuf:"bytes,4,opt"` ServerBuf []byte `protobuf:"bytes,4,opt"`
CompressFlag *uint32 `protobuf:"varint,5,opt"` CompressFlag proto.Option[uint32] `protobuf:"varint,5,opt"`
CompressMsg []byte `protobuf:"bytes,6,opt"` CompressMsg []byte `protobuf:"bytes,6,opt"`
FocusInfo *FocusInfo `protobuf:"bytes,7,opt"` FocusInfo *FocusInfo `protobuf:"bytes,7,opt"`
HugeFlag *uint32 `protobuf:"varint,8,opt"` HugeFlag proto.Option[uint32] `protobuf:"varint,8,opt"`
}
func (x *MsgOnlinePush) GetMsgs() []*ChannelMsgContent {
if x != nil {
return x.Msgs
}
return nil
}
func (x *MsgOnlinePush) GetGeneralFlag() uint32 {
if x != nil && x.GeneralFlag != nil {
return *x.GeneralFlag
}
return 0
}
func (x *MsgOnlinePush) GetNeedResp() uint32 {
if x != nil && x.NeedResp != nil {
return *x.NeedResp
}
return 0
}
func (x *MsgOnlinePush) GetServerBuf() []byte {
if x != nil {
return x.ServerBuf
}
return nil
}
func (x *MsgOnlinePush) GetCompressFlag() uint32 {
if x != nil && x.CompressFlag != nil {
return *x.CompressFlag
}
return 0
}
func (x *MsgOnlinePush) GetCompressMsg() []byte {
if x != nil {
return x.CompressMsg
}
return nil
}
func (x *MsgOnlinePush) GetFocusInfo() *FocusInfo {
if x != nil {
return x.FocusInfo
}
return nil
}
func (x *MsgOnlinePush) GetHugeFlag() uint32 {
if x != nil && x.HugeFlag != nil {
return *x.HugeFlag
}
return 0
} }
type MsgPushResp struct { type MsgPushResp struct {
ServerBuf []byte `protobuf:"bytes,1,opt"` ServerBuf []byte `protobuf:"bytes,1,opt"`
} }
func (x *MsgPushResp) GetServerBuf() []byte {
if x != nil {
return x.ServerBuf
}
return nil
}
type PressMsg struct { type PressMsg struct {
Msgs []*ChannelMsgContent `protobuf:"bytes,1,rep"` Msgs []*ChannelMsgContent `protobuf:"bytes,1,rep"`
} }
func (x *PressMsg) GetMsgs() []*ChannelMsgContent {
if x != nil {
return x.Msgs
}
return nil
}
type ServerBuf struct { type ServerBuf struct {
SvrIp *uint32 `protobuf:"varint,1,opt"` SvrIp proto.Option[uint32] `protobuf:"varint,1,opt"`
SvrPort *uint32 `protobuf:"varint,2,opt"` SvrPort proto.Option[uint32] `protobuf:"varint,2,opt"`
EchoKey []byte `protobuf:"bytes,3,opt"` EchoKey []byte `protobuf:"bytes,3,opt"`
}
func (x *ServerBuf) GetSvrIp() uint32 {
if x != nil && x.SvrIp != nil {
return *x.SvrIp
}
return 0
}
func (x *ServerBuf) GetSvrPort() uint32 {
if x != nil && x.SvrPort != nil {
return *x.SvrPort
}
return 0
}
func (x *ServerBuf) GetEchoKey() []byte {
if x != nil {
return x.EchoKey
}
return nil
} }

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/common.proto"; import "pb/channel/common.proto";

View File

@ -5,117 +5,28 @@ package channel
import ( import (
msg "github.com/Mrs4s/MiraiGo/client/pb/msg" msg "github.com/Mrs4s/MiraiGo/client/pb/msg"
proto "github.com/RomiChan/protobuf/proto"
) )
type DF62ReqBody struct { type DF62ReqBody struct {
Msg *ChannelMsgContent `protobuf:"bytes,1,opt"` Msg *ChannelMsgContent `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *DF62ReqBody) GetMsg() *ChannelMsgContent {
if x != nil {
return x.Msg
}
return nil
} }
type DF62RspBody struct { type DF62RspBody struct {
Result *uint32 `protobuf:"varint,1,opt"` Result proto.Option[uint32] `protobuf:"varint,1,opt"`
Errmsg []byte `protobuf:"bytes,2,opt"` Errmsg []byte `protobuf:"bytes,2,opt"`
SendTime *uint32 `protobuf:"varint,3,opt"` SendTime proto.Option[uint32] `protobuf:"varint,3,opt"`
Head *ChannelMsgHead `protobuf:"bytes,4,opt"` Head *ChannelMsgHead `protobuf:"bytes,4,opt"`
ErrType *uint32 `protobuf:"varint,5,opt"` ErrType proto.Option[uint32] `protobuf:"varint,5,opt"`
TransSvrInfo *TransSvrInfo `protobuf:"bytes,6,opt"` TransSvrInfo *TransSvrInfo `protobuf:"bytes,6,opt"`
FreqLimitInfo *ChannelFreqLimitInfo `protobuf:"bytes,7,opt"` FreqLimitInfo *ChannelFreqLimitInfo `protobuf:"bytes,7,opt"`
Body *msg.MessageBody `protobuf:"bytes,8,opt"` Body *msg.MessageBody `protobuf:"bytes,8,opt"`
} }
func (x *DF62RspBody) GetResult() uint32 {
if x != nil && x.Result != nil {
return *x.Result
}
return 0
}
func (x *DF62RspBody) GetErrmsg() []byte {
if x != nil {
return x.Errmsg
}
return nil
}
func (x *DF62RspBody) GetSendTime() uint32 {
if x != nil && x.SendTime != nil {
return *x.SendTime
}
return 0
}
func (x *DF62RspBody) GetHead() *ChannelMsgHead {
if x != nil {
return x.Head
}
return nil
}
func (x *DF62RspBody) GetErrType() uint32 {
if x != nil && x.ErrType != nil {
return *x.ErrType
}
return 0
}
func (x *DF62RspBody) GetTransSvrInfo() *TransSvrInfo {
if x != nil {
return x.TransSvrInfo
}
return nil
}
func (x *DF62RspBody) GetFreqLimitInfo() *ChannelFreqLimitInfo {
if x != nil {
return x.FreqLimitInfo
}
return nil
}
func (x *DF62RspBody) GetBody() *msg.MessageBody {
if x != nil {
return x.Body
}
return nil
}
type TransSvrInfo struct { type TransSvrInfo struct {
SubType *uint32 `protobuf:"varint,1,opt"` SubType proto.Option[uint32] `protobuf:"varint,1,opt"`
RetCode *int32 `protobuf:"varint,2,opt"` RetCode proto.Option[int32] `protobuf:"varint,2,opt"`
ErrMsg []byte `protobuf:"bytes,3,opt"` ErrMsg []byte `protobuf:"bytes,3,opt"`
TransInfo []byte `protobuf:"bytes,4,opt"` TransInfo []byte `protobuf:"bytes,4,opt"`
}
func (x *TransSvrInfo) GetSubType() uint32 {
if x != nil && x.SubType != nil {
return *x.SubType
}
return 0
}
func (x *TransSvrInfo) GetRetCode() int32 {
if x != nil && x.RetCode != nil {
return *x.RetCode
}
return 0
}
func (x *TransSvrInfo) GetErrMsg() []byte {
if x != nil {
return x.ErrMsg
}
return nil
}
func (x *TransSvrInfo) GetTransInfo() []byte {
if x != nil {
return x.TransInfo
}
return nil
} }

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/common.proto"; import "pb/channel/common.proto";
import "pb/msg/msg.proto"; import "pb/msg/msg.proto";

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
message AppChannelMsg { message AppChannelMsg {
optional string summary = 1; optional string summary = 1;

View File

@ -3,674 +3,136 @@
package channel package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type ChannelMsg struct { type ChannelMsg struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelId *uint64 `protobuf:"varint,2,opt"` ChannelId proto.Option[uint64] `protobuf:"varint,2,opt"`
Result *uint32 `protobuf:"varint,3,opt"` Result proto.Option[uint32] `protobuf:"varint,3,opt"`
RspBeginSeq *uint64 `protobuf:"varint,4,opt"` RspBeginSeq proto.Option[uint64] `protobuf:"varint,4,opt"`
RspEndSeq *uint64 `protobuf:"varint,5,opt"` RspEndSeq proto.Option[uint64] `protobuf:"varint,5,opt"`
Msgs []*ChannelMsgContent `protobuf:"bytes,6,rep"` Msgs []*ChannelMsgContent `protobuf:"bytes,6,rep"`
} }
func (x *ChannelMsg) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *ChannelMsg) GetChannelId() uint64 {
if x != nil && x.ChannelId != nil {
return *x.ChannelId
}
return 0
}
func (x *ChannelMsg) GetResult() uint32 {
if x != nil && x.Result != nil {
return *x.Result
}
return 0
}
func (x *ChannelMsg) GetRspBeginSeq() uint64 {
if x != nil && x.RspBeginSeq != nil {
return *x.RspBeginSeq
}
return 0
}
func (x *ChannelMsg) GetRspEndSeq() uint64 {
if x != nil && x.RspEndSeq != nil {
return *x.RspEndSeq
}
return 0
}
func (x *ChannelMsg) GetMsgs() []*ChannelMsgContent {
if x != nil {
return x.Msgs
}
return nil
}
type ChannelMsgReq struct { type ChannelMsgReq struct {
ChannelParam *ChannelParam `protobuf:"bytes,1,opt"` ChannelParam *ChannelParam `protobuf:"bytes,1,opt"`
WithVersionFlag *uint32 `protobuf:"varint,2,opt"` WithVersionFlag proto.Option[uint32] `protobuf:"varint,2,opt"`
DirectMessageFlag *uint32 `protobuf:"varint,3,opt"` DirectMessageFlag proto.Option[uint32] `protobuf:"varint,3,opt"`
} _ [0]func()
func (x *ChannelMsgReq) GetChannelParam() *ChannelParam {
if x != nil {
return x.ChannelParam
}
return nil
}
func (x *ChannelMsgReq) GetWithVersionFlag() uint32 {
if x != nil && x.WithVersionFlag != nil {
return *x.WithVersionFlag
}
return 0
}
func (x *ChannelMsgReq) GetDirectMessageFlag() uint32 {
if x != nil && x.DirectMessageFlag != nil {
return *x.DirectMessageFlag
}
return 0
} }
type ChannelMsgRsp struct { type ChannelMsgRsp struct {
Result *uint32 `protobuf:"varint,1,opt"` Result proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrMsg []byte `protobuf:"bytes,2,opt"` ErrMsg []byte `protobuf:"bytes,2,opt"`
ChannelMsg *ChannelMsg `protobuf:"bytes,3,opt"` ChannelMsg *ChannelMsg `protobuf:"bytes,3,opt"`
WithVersionFlag *uint32 `protobuf:"varint,4,opt"` WithVersionFlag proto.Option[uint32] `protobuf:"varint,4,opt"`
GetMsgTime *uint64 `protobuf:"varint,5,opt"` GetMsgTime proto.Option[uint64] `protobuf:"varint,5,opt"`
}
func (x *ChannelMsgRsp) GetResult() uint32 {
if x != nil && x.Result != nil {
return *x.Result
}
return 0
}
func (x *ChannelMsgRsp) GetErrMsg() []byte {
if x != nil {
return x.ErrMsg
}
return nil
}
func (x *ChannelMsgRsp) GetChannelMsg() *ChannelMsg {
if x != nil {
return x.ChannelMsg
}
return nil
}
func (x *ChannelMsgRsp) GetWithVersionFlag() uint32 {
if x != nil && x.WithVersionFlag != nil {
return *x.WithVersionFlag
}
return 0
}
func (x *ChannelMsgRsp) GetGetMsgTime() uint64 {
if x != nil && x.GetMsgTime != nil {
return *x.GetMsgTime
}
return 0
} }
type ChannelNode struct { type ChannelNode struct {
ChannelId *uint64 `protobuf:"varint,1,opt"` ChannelId proto.Option[uint64] `protobuf:"varint,1,opt"`
Seq *uint64 `protobuf:"varint,2,opt"` Seq proto.Option[uint64] `protobuf:"varint,2,opt"`
CntSeq *uint64 `protobuf:"varint,3,opt"` CntSeq proto.Option[uint64] `protobuf:"varint,3,opt"`
Time *uint64 `protobuf:"varint,4,opt"` Time proto.Option[uint64] `protobuf:"varint,4,opt"`
MemberReadMsgSeq *uint64 `protobuf:"varint,5,opt"` MemberReadMsgSeq proto.Option[uint64] `protobuf:"varint,5,opt"`
MemberReadCntSeq *uint64 `protobuf:"varint,6,opt"` MemberReadCntSeq proto.Option[uint64] `protobuf:"varint,6,opt"`
NotifyType *uint32 `protobuf:"varint,7,opt"` NotifyType proto.Option[uint32] `protobuf:"varint,7,opt"`
ChannelName []byte `protobuf:"bytes,8,opt"` ChannelName []byte `protobuf:"bytes,8,opt"`
ChannelType *uint32 `protobuf:"varint,9,opt"` ChannelType proto.Option[uint32] `protobuf:"varint,9,opt"`
Meta []byte `protobuf:"bytes,10,opt"` Meta []byte `protobuf:"bytes,10,opt"`
ReadMsgMeta []byte `protobuf:"bytes,11,opt"` ReadMsgMeta []byte `protobuf:"bytes,11,opt"`
EventTime *uint32 `protobuf:"varint,12,opt"` EventTime proto.Option[uint32] `protobuf:"varint,12,opt"`
}
func (x *ChannelNode) GetChannelId() uint64 {
if x != nil && x.ChannelId != nil {
return *x.ChannelId
}
return 0
}
func (x *ChannelNode) GetSeq() uint64 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *ChannelNode) GetCntSeq() uint64 {
if x != nil && x.CntSeq != nil {
return *x.CntSeq
}
return 0
}
func (x *ChannelNode) GetTime() uint64 {
if x != nil && x.Time != nil {
return *x.Time
}
return 0
}
func (x *ChannelNode) GetMemberReadMsgSeq() uint64 {
if x != nil && x.MemberReadMsgSeq != nil {
return *x.MemberReadMsgSeq
}
return 0
}
func (x *ChannelNode) GetMemberReadCntSeq() uint64 {
if x != nil && x.MemberReadCntSeq != nil {
return *x.MemberReadCntSeq
}
return 0
}
func (x *ChannelNode) GetNotifyType() uint32 {
if x != nil && x.NotifyType != nil {
return *x.NotifyType
}
return 0
}
func (x *ChannelNode) GetChannelName() []byte {
if x != nil {
return x.ChannelName
}
return nil
}
func (x *ChannelNode) GetChannelType() uint32 {
if x != nil && x.ChannelType != nil {
return *x.ChannelType
}
return 0
}
func (x *ChannelNode) GetMeta() []byte {
if x != nil {
return x.Meta
}
return nil
}
func (x *ChannelNode) GetReadMsgMeta() []byte {
if x != nil {
return x.ReadMsgMeta
}
return nil
}
func (x *ChannelNode) GetEventTime() uint32 {
if x != nil && x.EventTime != nil {
return *x.EventTime
}
return 0
} }
type ChannelParam struct { type ChannelParam struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelId *uint64 `protobuf:"varint,2,opt"` ChannelId proto.Option[uint64] `protobuf:"varint,2,opt"`
BeginSeq *uint64 `protobuf:"varint,3,opt"` BeginSeq proto.Option[uint64] `protobuf:"varint,3,opt"`
EndSeq *uint64 `protobuf:"varint,4,opt"` EndSeq proto.Option[uint64] `protobuf:"varint,4,opt"`
Time *uint64 `protobuf:"varint,5,opt"` Time proto.Option[uint64] `protobuf:"varint,5,opt"`
Version []uint64 `protobuf:"varint,6,rep"` Version []uint64 `protobuf:"varint,6,rep"`
Seqs []*MsgCond `protobuf:"bytes,7,rep"` Seqs []*MsgCond `protobuf:"bytes,7,rep"`
}
func (x *ChannelParam) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *ChannelParam) GetChannelId() uint64 {
if x != nil && x.ChannelId != nil {
return *x.ChannelId
}
return 0
}
func (x *ChannelParam) GetBeginSeq() uint64 {
if x != nil && x.BeginSeq != nil {
return *x.BeginSeq
}
return 0
}
func (x *ChannelParam) GetEndSeq() uint64 {
if x != nil && x.EndSeq != nil {
return *x.EndSeq
}
return 0
}
func (x *ChannelParam) GetTime() uint64 {
if x != nil && x.Time != nil {
return *x.Time
}
return 0
}
func (x *ChannelParam) GetVersion() []uint64 {
if x != nil {
return x.Version
}
return nil
}
func (x *ChannelParam) GetSeqs() []*MsgCond {
if x != nil {
return x.Seqs
}
return nil
} }
type DirectMessageSource struct { type DirectMessageSource struct {
TinyId *uint64 `protobuf:"varint,1,opt"` TinyId proto.Option[uint64] `protobuf:"varint,1,opt"`
GuildId *uint64 `protobuf:"varint,2,opt"` GuildId proto.Option[uint64] `protobuf:"varint,2,opt"`
GuildName []byte `protobuf:"bytes,3,opt"` GuildName []byte `protobuf:"bytes,3,opt"`
MemberName []byte `protobuf:"bytes,4,opt"` MemberName []byte `protobuf:"bytes,4,opt"`
NickName []byte `protobuf:"bytes,5,opt"` NickName []byte `protobuf:"bytes,5,opt"`
}
func (x *DirectMessageSource) GetTinyId() uint64 {
if x != nil && x.TinyId != nil {
return *x.TinyId
}
return 0
}
func (x *DirectMessageSource) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *DirectMessageSource) GetGuildName() []byte {
if x != nil {
return x.GuildName
}
return nil
}
func (x *DirectMessageSource) GetMemberName() []byte {
if x != nil {
return x.MemberName
}
return nil
}
func (x *DirectMessageSource) GetNickName() []byte {
if x != nil {
return x.NickName
}
return nil
} }
type FirstViewMsg struct { type FirstViewMsg struct {
PushFlag *uint32 `protobuf:"varint,1,opt"` PushFlag proto.Option[uint32] `protobuf:"varint,1,opt"`
Seq *uint32 `protobuf:"varint,2,opt"` Seq proto.Option[uint32] `protobuf:"varint,2,opt"`
GuildNodes []*GuildNode `protobuf:"bytes,3,rep"` GuildNodes []*GuildNode `protobuf:"bytes,3,rep"`
ChannelMsgs []*ChannelMsg `protobuf:"bytes,4,rep"` ChannelMsgs []*ChannelMsg `protobuf:"bytes,4,rep"`
GetMsgTime *uint64 `protobuf:"varint,5,opt"` GetMsgTime proto.Option[uint64] `protobuf:"varint,5,opt"`
DirectMessageGuildNodes []*GuildNode `protobuf:"bytes,6,rep"` DirectMessageGuildNodes []*GuildNode `protobuf:"bytes,6,rep"`
}
func (x *FirstViewMsg) GetPushFlag() uint32 {
if x != nil && x.PushFlag != nil {
return *x.PushFlag
}
return 0
}
func (x *FirstViewMsg) GetSeq() uint32 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *FirstViewMsg) GetGuildNodes() []*GuildNode {
if x != nil {
return x.GuildNodes
}
return nil
}
func (x *FirstViewMsg) GetChannelMsgs() []*ChannelMsg {
if x != nil {
return x.ChannelMsgs
}
return nil
}
func (x *FirstViewMsg) GetGetMsgTime() uint64 {
if x != nil && x.GetMsgTime != nil {
return *x.GetMsgTime
}
return 0
}
func (x *FirstViewMsg) GetDirectMessageGuildNodes() []*GuildNode {
if x != nil {
return x.DirectMessageGuildNodes
}
return nil
} }
type FirstViewReq struct { type FirstViewReq struct {
LastMsgTime *uint64 `protobuf:"varint,1,opt"` LastMsgTime proto.Option[uint64] `protobuf:"varint,1,opt"`
UdcFlag *uint32 `protobuf:"varint,2,opt"` UdcFlag proto.Option[uint32] `protobuf:"varint,2,opt"`
Seq *uint32 `protobuf:"varint,3,opt"` Seq proto.Option[uint32] `protobuf:"varint,3,opt"`
DirectMessageFlag *uint32 `protobuf:"varint,4,opt"` DirectMessageFlag proto.Option[uint32] `protobuf:"varint,4,opt"`
} _ [0]func()
func (x *FirstViewReq) GetLastMsgTime() uint64 {
if x != nil && x.LastMsgTime != nil {
return *x.LastMsgTime
}
return 0
}
func (x *FirstViewReq) GetUdcFlag() uint32 {
if x != nil && x.UdcFlag != nil {
return *x.UdcFlag
}
return 0
}
func (x *FirstViewReq) GetSeq() uint32 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *FirstViewReq) GetDirectMessageFlag() uint32 {
if x != nil && x.DirectMessageFlag != nil {
return *x.DirectMessageFlag
}
return 0
} }
type FirstViewRsp struct { type FirstViewRsp struct {
Result *uint32 `protobuf:"varint,1,opt"` Result proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrMsg []byte `protobuf:"bytes,2,opt"` ErrMsg []byte `protobuf:"bytes,2,opt"`
Seq *uint32 `protobuf:"varint,3,opt"` Seq proto.Option[uint32] `protobuf:"varint,3,opt"`
UdcFlag *uint32 `protobuf:"varint,4,opt"` UdcFlag proto.Option[uint32] `protobuf:"varint,4,opt"`
GuildCount *uint32 `protobuf:"varint,5,opt"` GuildCount proto.Option[uint32] `protobuf:"varint,5,opt"`
SelfTinyid *uint64 `protobuf:"varint,6,opt"` SelfTinyid proto.Option[uint64] `protobuf:"varint,6,opt"`
DirectMessageSwitch *uint32 `protobuf:"varint,7,opt"` DirectMessageSwitch proto.Option[uint32] `protobuf:"varint,7,opt"`
DirectMessageGuildCount *uint32 `protobuf:"varint,8,opt"` DirectMessageGuildCount proto.Option[uint32] `protobuf:"varint,8,opt"`
}
func (x *FirstViewRsp) GetResult() uint32 {
if x != nil && x.Result != nil {
return *x.Result
}
return 0
}
func (x *FirstViewRsp) GetErrMsg() []byte {
if x != nil {
return x.ErrMsg
}
return nil
}
func (x *FirstViewRsp) GetSeq() uint32 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *FirstViewRsp) GetUdcFlag() uint32 {
if x != nil && x.UdcFlag != nil {
return *x.UdcFlag
}
return 0
}
func (x *FirstViewRsp) GetGuildCount() uint32 {
if x != nil && x.GuildCount != nil {
return *x.GuildCount
}
return 0
}
func (x *FirstViewRsp) GetSelfTinyid() uint64 {
if x != nil && x.SelfTinyid != nil {
return *x.SelfTinyid
}
return 0
}
func (x *FirstViewRsp) GetDirectMessageSwitch() uint32 {
if x != nil && x.DirectMessageSwitch != nil {
return *x.DirectMessageSwitch
}
return 0
}
func (x *FirstViewRsp) GetDirectMessageGuildCount() uint32 {
if x != nil && x.DirectMessageGuildCount != nil {
return *x.DirectMessageGuildCount
}
return 0
} }
type GuildNode struct { type GuildNode struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
GuildCode *uint64 `protobuf:"varint,2,opt"` GuildCode proto.Option[uint64] `protobuf:"varint,2,opt"`
ChannelNodes []*ChannelNode `protobuf:"bytes,3,rep"` ChannelNodes []*ChannelNode `protobuf:"bytes,3,rep"`
GuildName []byte `protobuf:"bytes,4,opt"` GuildName []byte `protobuf:"bytes,4,opt"`
PeerSource *DirectMessageSource `protobuf:"bytes,5,opt"` PeerSource *DirectMessageSource `protobuf:"bytes,5,opt"`
} }
func (x *GuildNode) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *GuildNode) GetGuildCode() uint64 {
if x != nil && x.GuildCode != nil {
return *x.GuildCode
}
return 0
}
func (x *GuildNode) GetChannelNodes() []*ChannelNode {
if x != nil {
return x.ChannelNodes
}
return nil
}
func (x *GuildNode) GetGuildName() []byte {
if x != nil {
return x.GuildName
}
return nil
}
func (x *GuildNode) GetPeerSource() *DirectMessageSource {
if x != nil {
return x.PeerSource
}
return nil
}
type MsgCond struct { type MsgCond struct {
Seq *uint64 `protobuf:"varint,1,opt"` Seq proto.Option[uint64] `protobuf:"varint,1,opt"`
EventVersion *uint64 `protobuf:"varint,2,opt"` EventVersion proto.Option[uint64] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *MsgCond) GetSeq() uint64 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *MsgCond) GetEventVersion() uint64 {
if x != nil && x.EventVersion != nil {
return *x.EventVersion
}
return 0
} }
type MultiChannelMsg struct { type MultiChannelMsg struct {
PushFlag *uint32 `protobuf:"varint,1,opt"` PushFlag proto.Option[uint32] `protobuf:"varint,1,opt"`
Seq *uint32 `protobuf:"varint,2,opt"` Seq proto.Option[uint32] `protobuf:"varint,2,opt"`
ChannelMsgs []*ChannelMsg `protobuf:"bytes,3,rep"` ChannelMsgs []*ChannelMsg `protobuf:"bytes,3,rep"`
GetMsgTime *uint64 `protobuf:"varint,4,opt"` GetMsgTime proto.Option[uint64] `protobuf:"varint,4,opt"`
}
func (x *MultiChannelMsg) GetPushFlag() uint32 {
if x != nil && x.PushFlag != nil {
return *x.PushFlag
}
return 0
}
func (x *MultiChannelMsg) GetSeq() uint32 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *MultiChannelMsg) GetChannelMsgs() []*ChannelMsg {
if x != nil {
return x.ChannelMsgs
}
return nil
}
func (x *MultiChannelMsg) GetGetMsgTime() uint64 {
if x != nil && x.GetMsgTime != nil {
return *x.GetMsgTime
}
return 0
} }
type MultiChannelMsgReq struct { type MultiChannelMsgReq struct {
ChannelParams []*ChannelParam `protobuf:"bytes,1,rep"` ChannelParams []*ChannelParam `protobuf:"bytes,1,rep"`
Seq *uint32 `protobuf:"varint,2,opt"` Seq proto.Option[uint32] `protobuf:"varint,2,opt"`
DirectMessageFlag *uint32 `protobuf:"varint,3,opt"` DirectMessageFlag proto.Option[uint32] `protobuf:"varint,3,opt"`
}
func (x *MultiChannelMsgReq) GetChannelParams() []*ChannelParam {
if x != nil {
return x.ChannelParams
}
return nil
}
func (x *MultiChannelMsgReq) GetSeq() uint32 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *MultiChannelMsgReq) GetDirectMessageFlag() uint32 {
if x != nil && x.DirectMessageFlag != nil {
return *x.DirectMessageFlag
}
return 0
} }
type MultiChannelMsgRsp struct { type MultiChannelMsgRsp struct {
Result *uint32 `protobuf:"varint,1,opt"` Result proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrMsg []byte `protobuf:"bytes,2,opt"` ErrMsg []byte `protobuf:"bytes,2,opt"`
Seq *uint32 `protobuf:"varint,3,opt"` Seq proto.Option[uint32] `protobuf:"varint,3,opt"`
}
func (x *MultiChannelMsgRsp) GetResult() uint32 {
if x != nil && x.Result != nil {
return *x.Result
}
return 0
}
func (x *MultiChannelMsgRsp) GetErrMsg() []byte {
if x != nil {
return x.ErrMsg
}
return nil
}
func (x *MultiChannelMsgRsp) GetSeq() uint32 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
} }
type ReqBody struct { type ReqBody struct {
ChannelParam *ChannelParam `protobuf:"bytes,1,opt"` ChannelParam *ChannelParam `protobuf:"bytes,1,opt"`
DirectMessageFlag *uint32 `protobuf:"varint,2,opt"` DirectMessageFlag proto.Option[uint32] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *ReqBody) GetChannelParam() *ChannelParam {
if x != nil {
return x.ChannelParam
}
return nil
}
func (x *ReqBody) GetDirectMessageFlag() uint32 {
if x != nil && x.DirectMessageFlag != nil {
return *x.DirectMessageFlag
}
return 0
} }
type RspBody struct { type RspBody struct {
Result *uint32 `protobuf:"varint,1,opt"` Result proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrMsg []byte `protobuf:"bytes,2,opt"` ErrMsg []byte `protobuf:"bytes,2,opt"`
ChannelMsg *ChannelMsg `protobuf:"bytes,3,opt"` ChannelMsg *ChannelMsg `protobuf:"bytes,3,opt"`
}
func (x *RspBody) GetResult() uint32 {
if x != nil && x.Result != nil {
return *x.Result
}
return 0
}
func (x *RspBody) GetErrMsg() []byte {
if x != nil {
return x.ErrMsg
}
return nil
}
func (x *RspBody) GetChannelMsg() *ChannelMsg {
if x != nil {
return x.ChannelMsg
}
return nil
} }

View File

@ -2,7 +2,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/common.proto"; import "pb/channel/common.proto";

View File

@ -3,711 +3,170 @@
package channel package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
// see sub_37628C // see sub_37628C
type ChannelOidb0Xf5BRsp struct { type ChannelOidb0Xf5BRsp struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Bots []*GuildMemberInfo `protobuf:"bytes,4,rep"` Bots []*GuildMemberInfo `protobuf:"bytes,4,rep"`
Members []*GuildMemberInfo `protobuf:"bytes,5,rep"` Members []*GuildMemberInfo `protobuf:"bytes,5,rep"`
NextIndex *uint32 `protobuf:"varint,10,opt"` NextIndex proto.Option[uint32] `protobuf:"varint,10,opt"`
Finished *uint32 `protobuf:"varint,9,opt"` Finished proto.Option[uint32] `protobuf:"varint,9,opt"`
NextQueryParam *string `protobuf:"bytes,24,opt"` NextQueryParam proto.Option[string] `protobuf:"bytes,24,opt"`
MemberWithRoles []*GuildGroupMembersInfo `protobuf:"bytes,25,rep"` MemberWithRoles []*GuildGroupMembersInfo `protobuf:"bytes,25,rep"`
NextRoleIdIndex *uint64 `protobuf:"varint,26,opt"` NextRoleIdIndex proto.Option[uint64] `protobuf:"varint,26,opt"`
}
func (x *ChannelOidb0Xf5BRsp) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *ChannelOidb0Xf5BRsp) GetBots() []*GuildMemberInfo {
if x != nil {
return x.Bots
}
return nil
}
func (x *ChannelOidb0Xf5BRsp) GetMembers() []*GuildMemberInfo {
if x != nil {
return x.Members
}
return nil
}
func (x *ChannelOidb0Xf5BRsp) GetNextIndex() uint32 {
if x != nil && x.NextIndex != nil {
return *x.NextIndex
}
return 0
}
func (x *ChannelOidb0Xf5BRsp) GetFinished() uint32 {
if x != nil && x.Finished != nil {
return *x.Finished
}
return 0
}
func (x *ChannelOidb0Xf5BRsp) GetNextQueryParam() string {
if x != nil && x.NextQueryParam != nil {
return *x.NextQueryParam
}
return ""
}
func (x *ChannelOidb0Xf5BRsp) GetMemberWithRoles() []*GuildGroupMembersInfo {
if x != nil {
return x.MemberWithRoles
}
return nil
}
func (x *ChannelOidb0Xf5BRsp) GetNextRoleIdIndex() uint64 {
if x != nil && x.NextRoleIdIndex != nil {
return *x.NextRoleIdIndex
}
return 0
} }
type ChannelOidb0Xf88Rsp struct { type ChannelOidb0Xf88Rsp struct {
Profile *GuildUserProfile `protobuf:"bytes,1,opt"` Profile *GuildUserProfile `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *ChannelOidb0Xf88Rsp) GetProfile() *GuildUserProfile {
if x != nil {
return x.Profile
}
return nil
} }
type ChannelOidb0Xfc9Rsp struct { type ChannelOidb0Xfc9Rsp struct {
Profile *GuildUserProfile `protobuf:"bytes,1,opt"` Profile *GuildUserProfile `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *ChannelOidb0Xfc9Rsp) GetProfile() *GuildUserProfile {
if x != nil {
return x.Profile
}
return nil
} }
type ChannelOidb0Xf57Rsp struct { type ChannelOidb0Xf57Rsp struct {
Rsp *GuildMetaRsp `protobuf:"bytes,1,opt"` Rsp *GuildMetaRsp `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *ChannelOidb0Xf57Rsp) GetRsp() *GuildMetaRsp {
if x != nil {
return x.Rsp
}
return nil
} }
type ChannelOidb0Xf55Rsp struct { type ChannelOidb0Xf55Rsp struct {
Info *GuildChannelInfo `protobuf:"bytes,1,opt"` Info *GuildChannelInfo `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *ChannelOidb0Xf55Rsp) GetInfo() *GuildChannelInfo {
if x != nil {
return x.Info
}
return nil
} }
type ChannelOidb0Xf5DRsp struct { type ChannelOidb0Xf5DRsp struct {
Rsp *ChannelListRsp `protobuf:"bytes,1,opt"` Rsp *ChannelListRsp `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *ChannelOidb0Xf5DRsp) GetRsp() *ChannelListRsp {
if x != nil {
return x.Rsp
}
return nil
} }
type ChannelOidb0X1017Rsp struct { type ChannelOidb0X1017Rsp struct {
P1 *P10X1017 `protobuf:"bytes,1,opt"` P1 *P10X1017 `protobuf:"bytes,1,opt"`
} _ [0]func()
func (x *ChannelOidb0X1017Rsp) GetP1() *P10X1017 {
if x != nil {
return x.P1
}
return nil
} }
type P10X1017 struct { type P10X1017 struct {
TinyId *uint64 `protobuf:"varint,1,opt"` TinyId proto.Option[uint64] `protobuf:"varint,1,opt"`
Roles []*GuildUserRole `protobuf:"bytes,3,rep"` Roles []*GuildUserRole `protobuf:"bytes,3,rep"`
}
func (x *P10X1017) GetTinyId() uint64 {
if x != nil && x.TinyId != nil {
return *x.TinyId
}
return 0
}
func (x *P10X1017) GetRoles() []*GuildUserRole {
if x != nil {
return x.Roles
}
return nil
} }
type ChannelOidb0X1019Rsp struct { type ChannelOidb0X1019Rsp struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Roles []*GuildRole `protobuf:"bytes,2,rep"` Roles []*GuildRole `protobuf:"bytes,2,rep"`
}
func (x *ChannelOidb0X1019Rsp) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *ChannelOidb0X1019Rsp) GetRoles() []*GuildRole {
if x != nil {
return x.Roles
}
return nil
} }
type ChannelOidb0X1016Rsp struct { type ChannelOidb0X1016Rsp struct {
RoleId *uint64 `protobuf:"varint,2,opt"` RoleId proto.Option[uint64] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *ChannelOidb0X1016Rsp) GetRoleId() uint64 {
if x != nil && x.RoleId != nil {
return *x.RoleId
}
return 0
} }
type GuildMetaRsp struct { type GuildMetaRsp struct {
GuildId *uint64 `protobuf:"varint,3,opt"` GuildId proto.Option[uint64] `protobuf:"varint,3,opt"`
Meta *GuildMeta `protobuf:"bytes,4,opt"` Meta *GuildMeta `protobuf:"bytes,4,opt"`
} _ [0]func()
func (x *GuildMetaRsp) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *GuildMetaRsp) GetMeta() *GuildMeta {
if x != nil {
return x.Meta
}
return nil
} }
type ChannelListRsp struct { type ChannelListRsp struct {
GuildId *uint64 `protobuf:"varint,1,opt"` GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Channels []*GuildChannelInfo `protobuf:"bytes,2,rep"` // 5: Category infos Channels []*GuildChannelInfo `protobuf:"bytes,2,rep"` // 5: Category infos
}
func (x *ChannelListRsp) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *ChannelListRsp) GetChannels() []*GuildChannelInfo {
if x != nil {
return x.Channels
}
return nil
} }
type GuildGroupMembersInfo struct { type GuildGroupMembersInfo struct {
RoleId *uint64 `protobuf:"varint,1,opt"` RoleId proto.Option[uint64] `protobuf:"varint,1,opt"`
Members []*GuildMemberInfo `protobuf:"bytes,2,rep"` Members []*GuildMemberInfo `protobuf:"bytes,2,rep"`
RoleName *string `protobuf:"bytes,3,opt"` RoleName proto.Option[string] `protobuf:"bytes,3,opt"`
Color *uint32 `protobuf:"varint,4,opt"` Color proto.Option[uint32] `protobuf:"varint,4,opt"`
}
func (x *GuildGroupMembersInfo) GetRoleId() uint64 {
if x != nil && x.RoleId != nil {
return *x.RoleId
}
return 0
}
func (x *GuildGroupMembersInfo) GetMembers() []*GuildMemberInfo {
if x != nil {
return x.Members
}
return nil
}
func (x *GuildGroupMembersInfo) GetRoleName() string {
if x != nil && x.RoleName != nil {
return *x.RoleName
}
return ""
}
func (x *GuildGroupMembersInfo) GetColor() uint32 {
if x != nil && x.Color != nil {
return *x.Color
}
return 0
} }
// see sub_374334 // see sub_374334
type GuildMemberInfo struct { type GuildMemberInfo struct {
Title *string `protobuf:"bytes,2,opt"` Title proto.Option[string] `protobuf:"bytes,2,opt"`
Nickname *string `protobuf:"bytes,3,opt"` Nickname proto.Option[string] `protobuf:"bytes,3,opt"`
LastSpeakTime *int64 `protobuf:"varint,4,opt"` // uncertainty LastSpeakTime proto.Option[int64] `protobuf:"varint,4,opt"` // uncertainty
Role *int32 `protobuf:"varint,5,opt"` // uncertainty Role proto.Option[int32] `protobuf:"varint,5,opt"` // uncertainty
TinyId *uint64 `protobuf:"varint,8,opt"` TinyId proto.Option[uint64] `protobuf:"varint,8,opt"`
} _ [0]func()
func (x *GuildMemberInfo) GetTitle() string {
if x != nil && x.Title != nil {
return *x.Title
}
return ""
}
func (x *GuildMemberInfo) GetNickname() string {
if x != nil && x.Nickname != nil {
return *x.Nickname
}
return ""
}
func (x *GuildMemberInfo) GetLastSpeakTime() int64 {
if x != nil && x.LastSpeakTime != nil {
return *x.LastSpeakTime
}
return 0
}
func (x *GuildMemberInfo) GetRole() int32 {
if x != nil && x.Role != nil {
return *x.Role
}
return 0
}
func (x *GuildMemberInfo) GetTinyId() uint64 {
if x != nil && x.TinyId != nil {
return *x.TinyId
}
return 0
} }
// 频道系统用户资料 // 频道系统用户资料
type GuildUserProfile struct { type GuildUserProfile struct {
TinyId *uint64 `protobuf:"varint,2,opt"` TinyId proto.Option[uint64] `protobuf:"varint,2,opt"`
Nickname *string `protobuf:"bytes,3,opt"` Nickname proto.Option[string] `protobuf:"bytes,3,opt"`
AvatarUrl *string `protobuf:"bytes,6,opt"` AvatarUrl proto.Option[string] `protobuf:"bytes,6,opt"`
// 15: avatar url info // 15: avatar url info
JoinTime *int64 `protobuf:"varint,16,opt"` // uncertainty JoinTime proto.Option[int64] `protobuf:"varint,16,opt"` // uncertainty
} _ [0]func()
func (x *GuildUserProfile) GetTinyId() uint64 {
if x != nil && x.TinyId != nil {
return *x.TinyId
}
return 0
}
func (x *GuildUserProfile) GetNickname() string {
if x != nil && x.Nickname != nil {
return *x.Nickname
}
return ""
}
func (x *GuildUserProfile) GetAvatarUrl() string {
if x != nil && x.AvatarUrl != nil {
return *x.AvatarUrl
}
return ""
}
func (x *GuildUserProfile) GetJoinTime() int64 {
if x != nil && x.JoinTime != nil {
return *x.JoinTime
}
return 0
} }
type GuildRole struct { type GuildRole struct {
RoleId *uint64 `protobuf:"varint,1,opt"` RoleId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name *string `protobuf:"bytes,2,opt"` Name proto.Option[string] `protobuf:"bytes,2,opt"`
ArgbColor *uint32 `protobuf:"varint,3,opt"` ArgbColor proto.Option[uint32] `protobuf:"varint,3,opt"`
Independent *int32 `protobuf:"varint,4,opt"` Independent proto.Option[int32] `protobuf:"varint,4,opt"`
Num *int32 `protobuf:"varint,5,opt"` Num proto.Option[int32] `protobuf:"varint,5,opt"`
Owned *int32 `protobuf:"varint,6,opt"` // 是否拥有 存疑 Owned proto.Option[int32] `protobuf:"varint,6,opt"` // 是否拥有 存疑
Disabled *int32 `protobuf:"varint,7,opt"` // 权限不足或不显示 Disabled proto.Option[int32] `protobuf:"varint,7,opt"` // 权限不足或不显示
MaxNum *int32 `protobuf:"varint,8,opt"` // 9: ? MaxNum proto.Option[int32] `protobuf:"varint,8,opt"` // 9: ?
} _ [0]func()
func (x *GuildRole) GetRoleId() uint64 {
if x != nil && x.RoleId != nil {
return *x.RoleId
}
return 0
}
func (x *GuildRole) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *GuildRole) GetArgbColor() uint32 {
if x != nil && x.ArgbColor != nil {
return *x.ArgbColor
}
return 0
}
func (x *GuildRole) GetIndependent() int32 {
if x != nil && x.Independent != nil {
return *x.Independent
}
return 0
}
func (x *GuildRole) GetNum() int32 {
if x != nil && x.Num != nil {
return *x.Num
}
return 0
}
func (x *GuildRole) GetOwned() int32 {
if x != nil && x.Owned != nil {
return *x.Owned
}
return 0
}
func (x *GuildRole) GetDisabled() int32 {
if x != nil && x.Disabled != nil {
return *x.Disabled
}
return 0
}
func (x *GuildRole) GetMaxNum() int32 {
if x != nil && x.MaxNum != nil {
return *x.MaxNum
}
return 0
} }
type GuildUserRole struct { type GuildUserRole struct {
RoleId *uint64 `protobuf:"varint,1,opt"` RoleId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name *string `protobuf:"bytes,2,opt"` Name proto.Option[string] `protobuf:"bytes,2,opt"`
ArgbColor *uint32 `protobuf:"varint,3,opt"` ArgbColor proto.Option[uint32] `protobuf:"varint,3,opt"`
Independent *int32 `protobuf:"varint,4,opt"` Independent proto.Option[int32] `protobuf:"varint,4,opt"`
} _ [0]func()
func (x *GuildUserRole) GetRoleId() uint64 {
if x != nil && x.RoleId != nil {
return *x.RoleId
}
return 0
}
func (x *GuildUserRole) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *GuildUserRole) GetArgbColor() uint32 {
if x != nil && x.ArgbColor != nil {
return *x.ArgbColor
}
return 0
}
func (x *GuildUserRole) GetIndependent() int32 {
if x != nil && x.Independent != nil {
return *x.Independent
}
return 0
} }
type GuildMeta struct { type GuildMeta struct {
GuildCode *uint64 `protobuf:"varint,2,opt"` GuildCode proto.Option[uint64] `protobuf:"varint,2,opt"`
CreateTime *int64 `protobuf:"varint,4,opt"` CreateTime proto.Option[int64] `protobuf:"varint,4,opt"`
MaxMemberCount *int64 `protobuf:"varint,5,opt"` MaxMemberCount proto.Option[int64] `protobuf:"varint,5,opt"`
MemberCount *int64 `protobuf:"varint,6,opt"` MemberCount proto.Option[int64] `protobuf:"varint,6,opt"`
Name *string `protobuf:"bytes,8,opt"` Name proto.Option[string] `protobuf:"bytes,8,opt"`
RobotMaxNum *int32 `protobuf:"varint,11,opt"` RobotMaxNum proto.Option[int32] `protobuf:"varint,11,opt"`
AdminMaxNum *int32 `protobuf:"varint,12,opt"` AdminMaxNum proto.Option[int32] `protobuf:"varint,12,opt"`
Profile *string `protobuf:"bytes,13,opt"` Profile proto.Option[string] `protobuf:"bytes,13,opt"`
AvatarSeq *int64 `protobuf:"varint,14,opt"` AvatarSeq proto.Option[int64] `protobuf:"varint,14,opt"`
OwnerId *uint64 `protobuf:"varint,18,opt"` OwnerId proto.Option[uint64] `protobuf:"varint,18,opt"`
CoverSeq *int64 `protobuf:"varint,19,opt"` CoverSeq proto.Option[int64] `protobuf:"varint,19,opt"`
ClientId *int32 `protobuf:"varint,20,opt"` ClientId proto.Option[int32] `protobuf:"varint,20,opt"`
} _ [0]func()
func (x *GuildMeta) GetGuildCode() uint64 {
if x != nil && x.GuildCode != nil {
return *x.GuildCode
}
return 0
}
func (x *GuildMeta) GetCreateTime() int64 {
if x != nil && x.CreateTime != nil {
return *x.CreateTime
}
return 0
}
func (x *GuildMeta) GetMaxMemberCount() int64 {
if x != nil && x.MaxMemberCount != nil {
return *x.MaxMemberCount
}
return 0
}
func (x *GuildMeta) GetMemberCount() int64 {
if x != nil && x.MemberCount != nil {
return *x.MemberCount
}
return 0
}
func (x *GuildMeta) GetName() string {
if x != nil && x.Name != nil {
return *x.Name
}
return ""
}
func (x *GuildMeta) GetRobotMaxNum() int32 {
if x != nil && x.RobotMaxNum != nil {
return *x.RobotMaxNum
}
return 0
}
func (x *GuildMeta) GetAdminMaxNum() int32 {
if x != nil && x.AdminMaxNum != nil {
return *x.AdminMaxNum
}
return 0
}
func (x *GuildMeta) GetProfile() string {
if x != nil && x.Profile != nil {
return *x.Profile
}
return ""
}
func (x *GuildMeta) GetAvatarSeq() int64 {
if x != nil && x.AvatarSeq != nil {
return *x.AvatarSeq
}
return 0
}
func (x *GuildMeta) GetOwnerId() uint64 {
if x != nil && x.OwnerId != nil {
return *x.OwnerId
}
return 0
}
func (x *GuildMeta) GetCoverSeq() int64 {
if x != nil && x.CoverSeq != nil {
return *x.CoverSeq
}
return 0
}
func (x *GuildMeta) GetClientId() int32 {
if x != nil && x.ClientId != nil {
return *x.ClientId
}
return 0
} }
type GuildChannelInfo struct { type GuildChannelInfo struct {
ChannelId *uint64 `protobuf:"varint,1,opt"` ChannelId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelName *string `protobuf:"bytes,2,opt"` ChannelName proto.Option[string] `protobuf:"bytes,2,opt"`
CreatorUin *int64 `protobuf:"varint,3,opt"` CreatorUin proto.Option[int64] `protobuf:"varint,3,opt"`
CreateTime *int64 `protobuf:"varint,4,opt"` CreateTime proto.Option[int64] `protobuf:"varint,4,opt"`
GuildId *uint64 `protobuf:"varint,5,opt"` GuildId proto.Option[uint64] `protobuf:"varint,5,opt"`
FinalNotifyType *int32 `protobuf:"varint,6,opt"` FinalNotifyType proto.Option[int32] `protobuf:"varint,6,opt"`
ChannelType *int32 `protobuf:"varint,7,opt"` ChannelType proto.Option[int32] `protobuf:"varint,7,opt"`
TalkPermission *int32 `protobuf:"varint,8,opt"` TalkPermission proto.Option[int32] `protobuf:"varint,8,opt"`
// 11 - 14 : MsgInfo // 11 - 14 : MsgInfo
CreatorTinyId *uint64 `protobuf:"varint,15,opt"` CreatorTinyId proto.Option[uint64] `protobuf:"varint,15,opt"`
// 16: Member info ? // 16: Member info ?
VisibleType *int32 `protobuf:"varint,22,opt"` VisibleType proto.Option[int32] `protobuf:"varint,22,opt"`
TopMsg *GuildChannelTopMsgInfo `protobuf:"bytes,28,opt"` TopMsg *GuildChannelTopMsgInfo `protobuf:"bytes,28,opt"`
CurrentSlowModeKey *int32 `protobuf:"varint,31,opt"` CurrentSlowModeKey proto.Option[int32] `protobuf:"varint,31,opt"`
SlowModeInfos []*GuildChannelSlowModeInfo `protobuf:"bytes,32,rep"` SlowModeInfos []*GuildChannelSlowModeInfo `protobuf:"bytes,32,rep"`
} }
func (x *GuildChannelInfo) GetChannelId() uint64 {
if x != nil && x.ChannelId != nil {
return *x.ChannelId
}
return 0
}
func (x *GuildChannelInfo) GetChannelName() string {
if x != nil && x.ChannelName != nil {
return *x.ChannelName
}
return ""
}
func (x *GuildChannelInfo) GetCreatorUin() int64 {
if x != nil && x.CreatorUin != nil {
return *x.CreatorUin
}
return 0
}
func (x *GuildChannelInfo) GetCreateTime() int64 {
if x != nil && x.CreateTime != nil {
return *x.CreateTime
}
return 0
}
func (x *GuildChannelInfo) GetGuildId() uint64 {
if x != nil && x.GuildId != nil {
return *x.GuildId
}
return 0
}
func (x *GuildChannelInfo) GetFinalNotifyType() int32 {
if x != nil && x.FinalNotifyType != nil {
return *x.FinalNotifyType
}
return 0
}
func (x *GuildChannelInfo) GetChannelType() int32 {
if x != nil && x.ChannelType != nil {
return *x.ChannelType
}
return 0
}
func (x *GuildChannelInfo) GetTalkPermission() int32 {
if x != nil && x.TalkPermission != nil {
return *x.TalkPermission
}
return 0
}
func (x *GuildChannelInfo) GetCreatorTinyId() uint64 {
if x != nil && x.CreatorTinyId != nil {
return *x.CreatorTinyId
}
return 0
}
func (x *GuildChannelInfo) GetVisibleType() int32 {
if x != nil && x.VisibleType != nil {
return *x.VisibleType
}
return 0
}
func (x *GuildChannelInfo) GetTopMsg() *GuildChannelTopMsgInfo {
if x != nil {
return x.TopMsg
}
return nil
}
func (x *GuildChannelInfo) GetCurrentSlowModeKey() int32 {
if x != nil && x.CurrentSlowModeKey != nil {
return *x.CurrentSlowModeKey
}
return 0
}
func (x *GuildChannelInfo) GetSlowModeInfos() []*GuildChannelSlowModeInfo {
if x != nil {
return x.SlowModeInfos
}
return nil
}
type GuildChannelSlowModeInfo struct { type GuildChannelSlowModeInfo struct {
SlowModeKey *int32 `protobuf:"varint,1,opt"` SlowModeKey proto.Option[int32] `protobuf:"varint,1,opt"`
SpeakFrequency *int32 `protobuf:"varint,2,opt"` SpeakFrequency proto.Option[int32] `protobuf:"varint,2,opt"`
SlowModeCircle *int32 `protobuf:"varint,3,opt"` SlowModeCircle proto.Option[int32] `protobuf:"varint,3,opt"`
SlowModeText *string `protobuf:"bytes,4,opt"` SlowModeText proto.Option[string] `protobuf:"bytes,4,opt"`
} _ [0]func()
func (x *GuildChannelSlowModeInfo) GetSlowModeKey() int32 {
if x != nil && x.SlowModeKey != nil {
return *x.SlowModeKey
}
return 0
}
func (x *GuildChannelSlowModeInfo) GetSpeakFrequency() int32 {
if x != nil && x.SpeakFrequency != nil {
return *x.SpeakFrequency
}
return 0
}
func (x *GuildChannelSlowModeInfo) GetSlowModeCircle() int32 {
if x != nil && x.SlowModeCircle != nil {
return *x.SlowModeCircle
}
return 0
}
func (x *GuildChannelSlowModeInfo) GetSlowModeText() string {
if x != nil && x.SlowModeText != nil {
return *x.SlowModeText
}
return ""
} }
type GuildChannelTopMsgInfo struct { type GuildChannelTopMsgInfo struct {
TopMsgSeq *uint64 `protobuf:"varint,1,opt"` TopMsgSeq proto.Option[uint64] `protobuf:"varint,1,opt"`
TopMsgTime *int64 `protobuf:"varint,2,opt"` TopMsgTime proto.Option[int64] `protobuf:"varint,2,opt"`
TopMsgOperatorTinyId *uint64 `protobuf:"varint,3,opt"` TopMsgOperatorTinyId proto.Option[uint64] `protobuf:"varint,3,opt"`
} _ [0]func()
func (x *GuildChannelTopMsgInfo) GetTopMsgSeq() uint64 {
if x != nil && x.TopMsgSeq != nil {
return *x.TopMsgSeq
}
return 0
}
func (x *GuildChannelTopMsgInfo) GetTopMsgTime() int64 {
if x != nil && x.TopMsgTime != nil {
return *x.TopMsgTime
}
return 0
}
func (x *GuildChannelTopMsgInfo) GetTopMsgOperatorTinyId() uint64 {
if x != nil && x.TopMsgOperatorTinyId != nil {
return *x.TopMsgOperatorTinyId
}
return 0
} }

View File

@ -3,7 +3,7 @@ syntax = "proto2";
package channel; package channel;
option go_package = "pb/channel;channel"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
// see sub_37628C // see sub_37628C
message ChannelOidb0xf5bRsp { message ChannelOidb0xf5bRsp {

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "./;cmd0x346"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x346";
message ApplyCleanTrafficRsp { message ApplyCleanTrafficRsp {
int32 retCode = 10; int32 retCode = 10;
@ -197,6 +197,8 @@ message ApplyUploadReqV3 {
string localFilepath = 70; string localFilepath = 70;
int32 dangerLevel = 80; int32 dangerLevel = 80;
int64 totalSpace = 90; int64 totalSpace = 90;
bytes md5 = 110;
bytes _3Sha = 120;
} }
message ApplyUploadRsp { message ApplyUploadRsp {
int32 retCode = 10; int32 retCode = 10;
@ -248,6 +250,7 @@ message ApplyUploadRspV3 {
string uploadHttpsDomain = 150; string uploadHttpsDomain = 150;
string uploadDns = 160; string uploadDns = 160;
string uploadLanip = 170; string uploadLanip = 170;
bytes mediaPlateformUploadKey = 220;
} }
message DelMessageReq { message DelMessageReq {
int64 uinSender = 1; int64 uinSender = 1;
@ -361,61 +364,69 @@ message RenewFileRsp {
message C346ReqBody { message C346ReqBody {
int32 cmd = 1; int32 cmd = 1;
int32 seq = 2; int32 seq = 2;
/*
RecvListQueryReq recvListQueryReq = 3; RecvListQueryReq recvListQueryReq = 3;
SendListQueryReq sendListQueryReq = 4; SendListQueryReq sendListQueryReq = 4;
RenewFileReq renewFileReq = 5; RenewFileReq renewFileReq = 5;
RecallFileReq recallFileReq = 6; RecallFileReq recallFileReq = 6;
*/
ApplyUploadReq applyUploadReq = 7; ApplyUploadReq applyUploadReq = 7;
ApplyUploadHitReq applyUploadHitReq = 8; // ApplyUploadHitReq applyUploadHitReq = 8;
ApplyForwardFileReq applyForwardFileReq = 9; // ApplyForwardFileReq applyForwardFileReq = 9;
UploadSuccReq uploadSuccReq = 10; UploadSuccReq uploadSuccReq = 10;
DeleteFileReq deleteFileReq = 11; // DeleteFileReq deleteFileReq = 11;
DownloadSuccReq downloadSuccReq = 12; // DownloadSuccReq downloadSuccReq = 12;
ApplyDownloadAbsReq applyDownloadAbsReq = 13; // ApplyDownloadAbsReq applyDownloadAbsReq = 13;
ApplyDownloadReq applyDownloadReq = 14; ApplyDownloadReq applyDownloadReq = 14;
ApplyListDownloadReq applyListDownloadReq = 15; // ApplyListDownloadReq applyListDownloadReq = 15;
FileQueryReq fileQueryReq = 16; // FileQueryReq fileQueryReq = 16;
ApplyCopyFromReq applyCopyFromReq = 17; // ApplyCopyFromReq applyCopyFromReq = 17;
ApplyUploadReqV2 applyUploadReqV2 = 18; // ApplyUploadReqV2 applyUploadReqV2 = 18;
ApplyUploadReqV3 applyUploadReqV3 = 19; ApplyUploadReqV3 applyUploadReqV3 = 19;
ApplyUploadHitReqV2 applyUploadHitReqV2 = 20; //ApplyUploadHitReqV2 applyUploadHitReqV2 = 20;
ApplyUploadHitReqV3 applyUploadHitReqV3 = 21; //ApplyUploadHitReqV3 applyUploadHitReqV3 = 21;
int32 businessId = 101; int32 businessId = 101;
int32 clientType = 102; int32 clientType = 102;
ApplyCopyToReq applyCopyToReq = 90000; uint32 flagSupportMediaplatform = 200;
//ApplyCopyToReq applyCopyToReq = 90000;
//ApplyCleanTrafficReq applyCleanTrafficReq = 90001; empty message //ApplyCleanTrafficReq applyCleanTrafficReq = 90001; empty message
ApplyGetTrafficReq applyGetTrafficReq = 90002; //ApplyGetTrafficReq applyGetTrafficReq = 90002;
ExtensionReq extensionReq = 99999; ExtensionReq extensionReq = 99999;
} }
message C346RspBody { message C346RspBody {
int32 cmd = 1; int32 cmd = 1;
int32 seq = 2; int32 seq = 2;
/*
RecvListQueryRsp recvListQueryRsp = 3; RecvListQueryRsp recvListQueryRsp = 3;
SendListQueryRsp sendListQueryRsp = 4; SendListQueryRsp sendListQueryRsp = 4;
RenewFileRsp renewFileRsp = 5; RenewFileRsp renewFileRsp = 5;
RecallFileRsp recallFileRsp = 6; RecallFileRsp recallFileRsp = 6;
*/
ApplyUploadRsp applyUploadRsp = 7; ApplyUploadRsp applyUploadRsp = 7;
/*
ApplyUploadHitRsp applyUploadHitRsp = 8; ApplyUploadHitRsp applyUploadHitRsp = 8;
ApplyForwardFileRsp applyForwardFileRsp = 9; ApplyForwardFileRsp applyForwardFileRsp = 9;
UploadSuccRsp uploadSuccRsp = 10; UploadSuccRsp uploadSuccRsp = 10;
DeleteFileRsp deleteFileRsp = 11; DeleteFileRsp deleteFileRsp = 11;
DownloadSuccRsp downloadSuccRsp = 12; DownloadSuccRsp downloadSuccRsp = 12;
ApplyDownloadAbsRsp applyDownloadAbsRsp = 13; ApplyDownloadAbsRsp applyDownloadAbsRsp = 13;
*/
ApplyDownloadRsp applyDownloadRsp = 14; ApplyDownloadRsp applyDownloadRsp = 14;
ApplyListDownloadRsp applyListDownloadRsp = 15; // ApplyListDownloadRsp applyListDownloadRsp = 15;
FileQueryRsp fileQueryRsp = 16; // FileQueryRsp fileQueryRsp = 16;
ApplyCopyFromRsp applyCopyFromRsp = 17; // ApplyCopyFromRsp applyCopyFromRsp = 17;
ApplyUploadRspV2 applyUploadRspV2 = 18; // ApplyUploadRspV2 applyUploadRspV2 = 18;
ApplyUploadRspV3 applyUploadRspV3 = 19; ApplyUploadRspV3 applyUploadRspV3 = 19;
ApplyUploadHitRspV2 applyUploadHitRspV2 = 20; // ApplyUploadHitRspV2 applyUploadHitRspV2 = 20;
ApplyUploadHitRspV3 applyUploadHitRspV3 = 21; // ApplyUploadHitRspV3 applyUploadHitRspV3 = 21;
int32 businessId = 101; int32 businessId = 101;
int32 clientType = 102; int32 clientType = 102;
/*
ApplyCopyToRsp applyCopyToRsp = 90000; ApplyCopyToRsp applyCopyToRsp = 90000;
ApplyCleanTrafficRsp applyCleanTrafficRsp = 90001; ApplyCleanTrafficRsp applyCleanTrafficRsp = 90001;
ApplyGetTrafficRsp applyGetTrafficRsp = 90002; ApplyGetTrafficRsp applyGetTrafficRsp = 90002;
ExtensionRsp extensionRsp = 99999; ExtensionRsp extensionRsp = 99999;
*/
} }
message SendListQueryReq { message SendListQueryReq {
int64 uin = 1; int64 uin = 1;

View File

@ -1,430 +1,77 @@
// Code generated by protoc-gen-golite. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// source: cmd0x352.proto // source: pb/cmd0x352/cmd0x352.proto
package cmd0x352 package cmd0x352
import (
proto "github.com/RomiChan/protobuf/proto"
)
type ReqBody struct { type ReqBody struct {
Subcmd *uint32 `protobuf:"varint,1,opt"` Subcmd proto.Option[uint32] `protobuf:"varint,1,opt"`
TryupImgReq []*D352TryUpImgReq `protobuf:"bytes,2,rep"` TryupImgReq []*D352TryUpImgReq `protobuf:"bytes,2,rep"`
// repeated GetImgUrlReq getimgUrlReq = 3; // repeated GetImgUrlReq getimgUrlReq = 3;
// repeated DelImgReq delImgReq = 4; // repeated DelImgReq delImgReq = 4;
NetType *uint32 `protobuf:"varint,10,opt"` NetType proto.Option[uint32] `protobuf:"varint,10,opt"`
}
func (x *ReqBody) GetSubcmd() uint32 {
if x != nil && x.Subcmd != nil {
return *x.Subcmd
}
return 0
}
func (x *ReqBody) GetTryupImgReq() []*D352TryUpImgReq {
if x != nil {
return x.TryupImgReq
}
return nil
}
func (x *ReqBody) GetNetType() uint32 {
if x != nil && x.NetType != nil {
return *x.NetType
}
return 0
} }
type RspBody struct { type RspBody struct {
Subcmd *uint32 `protobuf:"varint,1,opt"` Subcmd proto.Option[uint32] `protobuf:"varint,1,opt"`
TryupImgRsp []*TryUpImgRsp `protobuf:"bytes,2,rep"` TryupImgRsp []*TryUpImgRsp `protobuf:"bytes,2,rep"`
// repeated GetImgUrlRsp getimgUrlRsp = 3; // repeated GetImgUrlRsp getimgUrlRsp = 3;
NewBigchan *bool `protobuf:"varint,4,opt"` NewBigchan proto.Option[bool] `protobuf:"varint,4,opt"`
// repeated DelImgRsp delImgRsp = 5; // repeated DelImgRsp delImgRsp = 5;
FailMsg []byte `protobuf:"bytes,10,opt"` FailMsg []byte `protobuf:"bytes,10,opt"`
} }
func (x *RspBody) GetSubcmd() uint32 {
if x != nil && x.Subcmd != nil {
return *x.Subcmd
}
return 0
}
func (x *RspBody) GetTryupImgRsp() []*TryUpImgRsp {
if x != nil {
return x.TryupImgRsp
}
return nil
}
func (x *RspBody) GetNewBigchan() bool {
if x != nil && x.NewBigchan != nil {
return *x.NewBigchan
}
return false
}
func (x *RspBody) GetFailMsg() []byte {
if x != nil {
return x.FailMsg
}
return nil
}
type D352TryUpImgReq struct { type D352TryUpImgReq struct {
SrcUin *uint64 `protobuf:"varint,1,opt"` SrcUin proto.Option[uint64] `protobuf:"varint,1,opt"`
DstUin *uint64 `protobuf:"varint,2,opt"` DstUin proto.Option[uint64] `protobuf:"varint,2,opt"`
FileId *uint64 `protobuf:"varint,3,opt"` FileId proto.Option[uint64] `protobuf:"varint,3,opt"`
FileMd5 []byte `protobuf:"bytes,4,opt"` FileMd5 []byte `protobuf:"bytes,4,opt"`
FileSize *uint64 `protobuf:"varint,5,opt"` FileSize proto.Option[uint64] `protobuf:"varint,5,opt"`
FileName []byte `protobuf:"bytes,6,opt"` FileName []byte `protobuf:"bytes,6,opt"`
SrcTerm *uint32 `protobuf:"varint,7,opt"` SrcTerm proto.Option[uint32] `protobuf:"varint,7,opt"`
PlatformType *uint32 `protobuf:"varint,8,opt"` PlatformType proto.Option[uint32] `protobuf:"varint,8,opt"`
InnerIp *uint32 `protobuf:"varint,9,opt"` InnerIp proto.Option[uint32] `protobuf:"varint,9,opt"`
AddressBook *bool `protobuf:"varint,10,opt"` AddressBook proto.Option[bool] `protobuf:"varint,10,opt"`
Retry *uint32 `protobuf:"varint,11,opt"` Retry proto.Option[uint32] `protobuf:"varint,11,opt"`
BuType *uint32 `protobuf:"varint,12,opt"` BuType proto.Option[uint32] `protobuf:"varint,12,opt"`
PicOriginal *bool `protobuf:"varint,13,opt"` PicOriginal proto.Option[bool] `protobuf:"varint,13,opt"`
PicWidth *uint32 `protobuf:"varint,14,opt"` PicWidth proto.Option[uint32] `protobuf:"varint,14,opt"`
PicHeight *uint32 `protobuf:"varint,15,opt"` PicHeight proto.Option[uint32] `protobuf:"varint,15,opt"`
PicType *uint32 `protobuf:"varint,16,opt"` PicType proto.Option[uint32] `protobuf:"varint,16,opt"`
BuildVer []byte `protobuf:"bytes,17,opt"` BuildVer []byte `protobuf:"bytes,17,opt"`
FileIndex []byte `protobuf:"bytes,18,opt"` FileIndex []byte `protobuf:"bytes,18,opt"`
StoreDays *uint32 `protobuf:"varint,19,opt"` StoreDays proto.Option[uint32] `protobuf:"varint,19,opt"`
TryupStepflag *uint32 `protobuf:"varint,20,opt"` TryupStepflag proto.Option[uint32] `protobuf:"varint,20,opt"`
RejectTryfast *bool `protobuf:"varint,21,opt"` RejectTryfast proto.Option[bool] `protobuf:"varint,21,opt"`
SrvUpload *uint32 `protobuf:"varint,22,opt"` SrvUpload proto.Option[uint32] `protobuf:"varint,22,opt"`
TransferUrl []byte `protobuf:"bytes,23,opt"` TransferUrl []byte `protobuf:"bytes,23,opt"`
}
func (x *D352TryUpImgReq) GetSrcUin() uint64 {
if x != nil && x.SrcUin != nil {
return *x.SrcUin
}
return 0
}
func (x *D352TryUpImgReq) GetDstUin() uint64 {
if x != nil && x.DstUin != nil {
return *x.DstUin
}
return 0
}
func (x *D352TryUpImgReq) GetFileId() uint64 {
if x != nil && x.FileId != nil {
return *x.FileId
}
return 0
}
func (x *D352TryUpImgReq) GetFileMd5() []byte {
if x != nil {
return x.FileMd5
}
return nil
}
func (x *D352TryUpImgReq) GetFileSize() uint64 {
if x != nil && x.FileSize != nil {
return *x.FileSize
}
return 0
}
func (x *D352TryUpImgReq) GetFileName() []byte {
if x != nil {
return x.FileName
}
return nil
}
func (x *D352TryUpImgReq) GetSrcTerm() uint32 {
if x != nil && x.SrcTerm != nil {
return *x.SrcTerm
}
return 0
}
func (x *D352TryUpImgReq) GetPlatformType() uint32 {
if x != nil && x.PlatformType != nil {
return *x.PlatformType
}
return 0
}
func (x *D352TryUpImgReq) GetInnerIp() uint32 {
if x != nil && x.InnerIp != nil {
return *x.InnerIp
}
return 0
}
func (x *D352TryUpImgReq) GetAddressBook() bool {
if x != nil && x.AddressBook != nil {
return *x.AddressBook
}
return false
}
func (x *D352TryUpImgReq) GetRetry() uint32 {
if x != nil && x.Retry != nil {
return *x.Retry
}
return 0
}
func (x *D352TryUpImgReq) GetBuType() uint32 {
if x != nil && x.BuType != nil {
return *x.BuType
}
return 0
}
func (x *D352TryUpImgReq) GetPicOriginal() bool {
if x != nil && x.PicOriginal != nil {
return *x.PicOriginal
}
return false
}
func (x *D352TryUpImgReq) GetPicWidth() uint32 {
if x != nil && x.PicWidth != nil {
return *x.PicWidth
}
return 0
}
func (x *D352TryUpImgReq) GetPicHeight() uint32 {
if x != nil && x.PicHeight != nil {
return *x.PicHeight
}
return 0
}
func (x *D352TryUpImgReq) GetPicType() uint32 {
if x != nil && x.PicType != nil {
return *x.PicType
}
return 0
}
func (x *D352TryUpImgReq) GetBuildVer() []byte {
if x != nil {
return x.BuildVer
}
return nil
}
func (x *D352TryUpImgReq) GetFileIndex() []byte {
if x != nil {
return x.FileIndex
}
return nil
}
func (x *D352TryUpImgReq) GetStoreDays() uint32 {
if x != nil && x.StoreDays != nil {
return *x.StoreDays
}
return 0
}
func (x *D352TryUpImgReq) GetTryupStepflag() uint32 {
if x != nil && x.TryupStepflag != nil {
return *x.TryupStepflag
}
return 0
}
func (x *D352TryUpImgReq) GetRejectTryfast() bool {
if x != nil && x.RejectTryfast != nil {
return *x.RejectTryfast
}
return false
}
func (x *D352TryUpImgReq) GetSrvUpload() uint32 {
if x != nil && x.SrvUpload != nil {
return *x.SrvUpload
}
return 0
}
func (x *D352TryUpImgReq) GetTransferUrl() []byte {
if x != nil {
return x.TransferUrl
}
return nil
} }
type TryUpImgRsp struct { type TryUpImgRsp struct {
FileId *uint64 `protobuf:"varint,1,opt"` FileId proto.Option[uint64] `protobuf:"varint,1,opt"`
ClientIp *uint32 `protobuf:"varint,2,opt"` ClientIp proto.Option[uint32] `protobuf:"varint,2,opt"`
Result *uint32 `protobuf:"varint,3,opt"` Result proto.Option[uint32] `protobuf:"varint,3,opt"`
FailMsg []byte `protobuf:"bytes,4,opt"` FailMsg []byte `protobuf:"bytes,4,opt"`
FileExit *bool `protobuf:"varint,5,opt"` FileExit proto.Option[bool] `protobuf:"varint,5,opt"`
// optional ImgInfo imgInfo = 6; // optional ImgInfo imgInfo = 6;
UpIp []uint32 `protobuf:"varint,7,rep"` UpIp []uint32 `protobuf:"varint,7,rep"`
UpPort []uint32 `protobuf:"varint,8,rep"` UpPort []uint32 `protobuf:"varint,8,rep"`
UpUkey []byte `protobuf:"bytes,9,opt"` UpUkey []byte `protobuf:"bytes,9,opt"`
UpResid []byte `protobuf:"bytes,10,opt"` UpResid []byte `protobuf:"bytes,10,opt"`
UpUuid []byte `protobuf:"bytes,11,opt"` UpUuid []byte `protobuf:"bytes,11,opt"`
UpOffset *uint64 `protobuf:"varint,12,opt"` UpOffset proto.Option[uint64] `protobuf:"varint,12,opt"`
BlockSize *uint64 `protobuf:"varint,13,opt"` BlockSize proto.Option[uint64] `protobuf:"varint,13,opt"`
EncryptDstip []byte `protobuf:"bytes,14,opt"` EncryptDstip []byte `protobuf:"bytes,14,opt"`
Roamdays *uint32 `protobuf:"varint,15,opt"` Roamdays proto.Option[uint32] `protobuf:"varint,15,opt"`
// repeated IPv6Info upIp6 = 26; // repeated IPv6Info upIp6 = 26;
ClientIp6 []byte `protobuf:"bytes,27,opt"` ClientIp6 []byte `protobuf:"bytes,27,opt"`
ThumbDownPara []byte `protobuf:"bytes,60,opt"` ThumbDownPara []byte `protobuf:"bytes,60,opt"`
OriginalDownPara []byte `protobuf:"bytes,61,opt"` OriginalDownPara []byte `protobuf:"bytes,61,opt"`
DownDomain []byte `protobuf:"bytes,62,opt"` DownDomain []byte `protobuf:"bytes,62,opt"`
BigDownPara []byte `protobuf:"bytes,64,opt"` BigDownPara []byte `protobuf:"bytes,64,opt"`
BigThumbDownPara []byte `protobuf:"bytes,65,opt"` BigThumbDownPara []byte `protobuf:"bytes,65,opt"`
HttpsUrlFlag *uint32 `protobuf:"varint,66,opt"` // optional TryUpInfo4Busi info4Busi = 1001; HttpsUrlFlag proto.Option[uint32] `protobuf:"varint,66,opt"` // optional TryUpInfo4Busi info4Busi = 1001;
}
func (x *TryUpImgRsp) GetFileId() uint64 {
if x != nil && x.FileId != nil {
return *x.FileId
}
return 0
}
func (x *TryUpImgRsp) GetClientIp() uint32 {
if x != nil && x.ClientIp != nil {
return *x.ClientIp
}
return 0
}
func (x *TryUpImgRsp) GetResult() uint32 {
if x != nil && x.Result != nil {
return *x.Result
}
return 0
}
func (x *TryUpImgRsp) GetFailMsg() []byte {
if x != nil {
return x.FailMsg
}
return nil
}
func (x *TryUpImgRsp) GetFileExit() bool {
if x != nil && x.FileExit != nil {
return *x.FileExit
}
return false
}
func (x *TryUpImgRsp) GetUpIp() []uint32 {
if x != nil {
return x.UpIp
}
return nil
}
func (x *TryUpImgRsp) GetUpPort() []uint32 {
if x != nil {
return x.UpPort
}
return nil
}
func (x *TryUpImgRsp) GetUpUkey() []byte {
if x != nil {
return x.UpUkey
}
return nil
}
func (x *TryUpImgRsp) GetUpResid() []byte {
if x != nil {
return x.UpResid
}
return nil
}
func (x *TryUpImgRsp) GetUpUuid() []byte {
if x != nil {
return x.UpUuid
}
return nil
}
func (x *TryUpImgRsp) GetUpOffset() uint64 {
if x != nil && x.UpOffset != nil {
return *x.UpOffset
}
return 0
}
func (x *TryUpImgRsp) GetBlockSize() uint64 {
if x != nil && x.BlockSize != nil {
return *x.BlockSize
}
return 0
}
func (x *TryUpImgRsp) GetEncryptDstip() []byte {
if x != nil {
return x.EncryptDstip
}
return nil
}
func (x *TryUpImgRsp) GetRoamdays() uint32 {
if x != nil && x.Roamdays != nil {
return *x.Roamdays
}
return 0
}
func (x *TryUpImgRsp) GetClientIp6() []byte {
if x != nil {
return x.ClientIp6
}
return nil
}
func (x *TryUpImgRsp) GetThumbDownPara() []byte {
if x != nil {
return x.ThumbDownPara
}
return nil
}
func (x *TryUpImgRsp) GetOriginalDownPara() []byte {
if x != nil {
return x.OriginalDownPara
}
return nil
}
func (x *TryUpImgRsp) GetDownDomain() []byte {
if x != nil {
return x.DownDomain
}
return nil
}
func (x *TryUpImgRsp) GetBigDownPara() []byte {
if x != nil {
return x.BigDownPara
}
return nil
}
func (x *TryUpImgRsp) GetBigThumbDownPara() []byte {
if x != nil {
return x.BigThumbDownPara
}
return nil
}
func (x *TryUpImgRsp) GetHttpsUrlFlag() uint32 {
if x != nil && x.HttpsUrlFlag != nil {
return *x.HttpsUrlFlag
}
return 0
} }

View File

@ -1,6 +1,6 @@
syntax = "proto2"; syntax = "proto2";
option go_package = "./;cmd0x352"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x352";
/* /*
message DelImgReq { message DelImgReq {
optional uint64 srcUin = 1; optional uint64 srcUin = 1;

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
syntax = "proto2"; syntax = "proto2";
option go_package = "./;cmd0x388"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x388";
message DelImgReq { message DelImgReq {
optional uint64 srcUin = 1; optional uint64 srcUin = 1;

View File

@ -1,446 +1,83 @@
// Code generated by protoc-gen-golite. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// source: cmd0x3f6.proto // source: pb/cmd0x3f6/cmd0x3f6.proto
package cmd0x3f6 package cmd0x3f6
import (
proto "github.com/RomiChan/protobuf/proto"
)
type C3F6ReqBody struct { type C3F6ReqBody struct {
SubCmd *uint32 `protobuf:"varint,1,opt"` SubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
CrmCommonHead *C3F6CRMMsgHead `protobuf:"bytes,2,opt"` CrmCommonHead *C3F6CRMMsgHead `protobuf:"bytes,2,opt"`
SubcmdLoginProcessCompleteReqBody *QDUserLoginProcessCompleteReqBody `protobuf:"bytes,42,opt"` SubcmdLoginProcessCompleteReqBody *QDUserLoginProcessCompleteReqBody `protobuf:"bytes,42,opt"`
} _ [0]func()
func (x *C3F6ReqBody) GetSubCmd() uint32 {
if x != nil && x.SubCmd != nil {
return *x.SubCmd
}
return 0
}
func (x *C3F6ReqBody) GetCrmCommonHead() *C3F6CRMMsgHead {
if x != nil {
return x.CrmCommonHead
}
return nil
}
func (x *C3F6ReqBody) GetSubcmdLoginProcessCompleteReqBody() *QDUserLoginProcessCompleteReqBody {
if x != nil {
return x.SubcmdLoginProcessCompleteReqBody
}
return nil
} }
type C3F6RspBody struct { type C3F6RspBody struct {
SubCmd *uint32 `protobuf:"varint,1,opt"` SubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
CrmCommonHead *C3F6CRMMsgHead `protobuf:"bytes,2,opt"` CrmCommonHead *C3F6CRMMsgHead `protobuf:"bytes,2,opt"`
SubcmdLoginProcessCompleteRspBody *QDUserLoginProcessCompleteRspBody `protobuf:"bytes,42,opt"` SubcmdLoginProcessCompleteRspBody *QDUserLoginProcessCompleteRspBody `protobuf:"bytes,42,opt"`
} _ [0]func()
func (x *C3F6RspBody) GetSubCmd() uint32 {
if x != nil && x.SubCmd != nil {
return *x.SubCmd
}
return 0
}
func (x *C3F6RspBody) GetCrmCommonHead() *C3F6CRMMsgHead {
if x != nil {
return x.CrmCommonHead
}
return nil
}
func (x *C3F6RspBody) GetSubcmdLoginProcessCompleteRspBody() *QDUserLoginProcessCompleteRspBody {
if x != nil {
return x.SubcmdLoginProcessCompleteRspBody
}
return nil
} }
type QDUserLoginProcessCompleteReqBody struct { type QDUserLoginProcessCompleteReqBody struct {
Kfext *uint64 `protobuf:"varint,1,opt"` Kfext proto.Option[uint64] `protobuf:"varint,1,opt"`
Pubno *uint32 `protobuf:"varint,2,opt"` Pubno proto.Option[uint32] `protobuf:"varint,2,opt"`
Buildno *uint32 `protobuf:"varint,3,opt"` Buildno proto.Option[uint32] `protobuf:"varint,3,opt"`
TerminalType *uint32 `protobuf:"varint,4,opt"` TerminalType proto.Option[uint32] `protobuf:"varint,4,opt"`
Status *uint32 `protobuf:"varint,5,opt"` Status proto.Option[uint32] `protobuf:"varint,5,opt"`
LoginTime *uint32 `protobuf:"varint,6,opt"` LoginTime proto.Option[uint32] `protobuf:"varint,6,opt"`
HardwareInfo *string `protobuf:"bytes,7,opt"` HardwareInfo proto.Option[string] `protobuf:"bytes,7,opt"`
SoftwareInfo *string `protobuf:"bytes,8,opt"` SoftwareInfo proto.Option[string] `protobuf:"bytes,8,opt"`
Guid []byte `protobuf:"bytes,9,opt"` Guid []byte `protobuf:"bytes,9,opt"`
AppName *string `protobuf:"bytes,10,opt"` AppName proto.Option[string] `protobuf:"bytes,10,opt"`
SubAppId *uint32 `protobuf:"varint,11,opt"` SubAppId proto.Option[uint32] `protobuf:"varint,11,opt"`
}
func (x *QDUserLoginProcessCompleteReqBody) GetKfext() uint64 {
if x != nil && x.Kfext != nil {
return *x.Kfext
}
return 0
}
func (x *QDUserLoginProcessCompleteReqBody) GetPubno() uint32 {
if x != nil && x.Pubno != nil {
return *x.Pubno
}
return 0
}
func (x *QDUserLoginProcessCompleteReqBody) GetBuildno() uint32 {
if x != nil && x.Buildno != nil {
return *x.Buildno
}
return 0
}
func (x *QDUserLoginProcessCompleteReqBody) GetTerminalType() uint32 {
if x != nil && x.TerminalType != nil {
return *x.TerminalType
}
return 0
}
func (x *QDUserLoginProcessCompleteReqBody) GetStatus() uint32 {
if x != nil && x.Status != nil {
return *x.Status
}
return 0
}
func (x *QDUserLoginProcessCompleteReqBody) GetLoginTime() uint32 {
if x != nil && x.LoginTime != nil {
return *x.LoginTime
}
return 0
}
func (x *QDUserLoginProcessCompleteReqBody) GetHardwareInfo() string {
if x != nil && x.HardwareInfo != nil {
return *x.HardwareInfo
}
return ""
}
func (x *QDUserLoginProcessCompleteReqBody) GetSoftwareInfo() string {
if x != nil && x.SoftwareInfo != nil {
return *x.SoftwareInfo
}
return ""
}
func (x *QDUserLoginProcessCompleteReqBody) GetGuid() []byte {
if x != nil {
return x.Guid
}
return nil
}
func (x *QDUserLoginProcessCompleteReqBody) GetAppName() string {
if x != nil && x.AppName != nil {
return *x.AppName
}
return ""
}
func (x *QDUserLoginProcessCompleteReqBody) GetSubAppId() uint32 {
if x != nil && x.SubAppId != nil {
return *x.SubAppId
}
return 0
} }
type QDUserLoginProcessCompleteRspBody struct { type QDUserLoginProcessCompleteRspBody struct {
Ret *RetInfo `protobuf:"bytes,1,opt"` Ret *RetInfo `protobuf:"bytes,1,opt"`
Url *string `protobuf:"bytes,2,opt"` Url proto.Option[string] `protobuf:"bytes,2,opt"`
Mobile *string `protobuf:"bytes,3,opt"` Mobile proto.Option[string] `protobuf:"bytes,3,opt"`
ExternalMobile *string `protobuf:"bytes,4,opt"` ExternalMobile proto.Option[string] `protobuf:"bytes,4,opt"`
DataAnalysisPriv *bool `protobuf:"varint,5,opt"` DataAnalysisPriv proto.Option[bool] `protobuf:"varint,5,opt"`
DeviceLock *bool `protobuf:"varint,6,opt"` DeviceLock proto.Option[bool] `protobuf:"varint,6,opt"`
ModulePrivilege *uint64 `protobuf:"varint,7,opt"` ModulePrivilege proto.Option[uint64] `protobuf:"varint,7,opt"`
ModuleSubPrivilege []uint32 `protobuf:"varint,8,rep"` ModuleSubPrivilege []uint32 `protobuf:"varint,8,rep"`
MasterSet *uint32 `protobuf:"varint,9,opt"` MasterSet proto.Option[uint32] `protobuf:"varint,9,opt"`
ExtSet *uint32 `protobuf:"varint,10,opt"` ExtSet proto.Option[uint32] `protobuf:"varint,10,opt"`
CorpConfProperty *uint64 `protobuf:"varint,11,opt"` CorpConfProperty proto.Option[uint64] `protobuf:"varint,11,opt"`
Corpuin *uint64 `protobuf:"varint,12,opt"` Corpuin proto.Option[uint64] `protobuf:"varint,12,opt"`
Kfaccount *uint64 `protobuf:"varint,13,opt"` Kfaccount proto.Option[uint64] `protobuf:"varint,13,opt"`
SecurityLevel *uint32 `protobuf:"varint,14,opt"` SecurityLevel proto.Option[uint32] `protobuf:"varint,14,opt"`
MsgTitle *string `protobuf:"bytes,15,opt"` MsgTitle proto.Option[string] `protobuf:"bytes,15,opt"`
SuccNoticeMsg *string `protobuf:"bytes,16,opt"` SuccNoticeMsg proto.Option[string] `protobuf:"bytes,16,opt"`
NameAccount *uint64 `protobuf:"varint,17,opt"` NameAccount proto.Option[uint64] `protobuf:"varint,17,opt"`
CrmMigrateFlag *uint32 `protobuf:"varint,18,opt"` CrmMigrateFlag proto.Option[uint32] `protobuf:"varint,18,opt"`
ExtuinName *string `protobuf:"bytes,19,opt"` ExtuinName proto.Option[string] `protobuf:"bytes,19,opt"`
OpenAccountTime *uint32 `protobuf:"varint,20,opt"` OpenAccountTime proto.Option[uint32] `protobuf:"varint,20,opt"`
}
func (x *QDUserLoginProcessCompleteRspBody) GetRet() *RetInfo {
if x != nil {
return x.Ret
}
return nil
}
func (x *QDUserLoginProcessCompleteRspBody) GetUrl() string {
if x != nil && x.Url != nil {
return *x.Url
}
return ""
}
func (x *QDUserLoginProcessCompleteRspBody) GetMobile() string {
if x != nil && x.Mobile != nil {
return *x.Mobile
}
return ""
}
func (x *QDUserLoginProcessCompleteRspBody) GetExternalMobile() string {
if x != nil && x.ExternalMobile != nil {
return *x.ExternalMobile
}
return ""
}
func (x *QDUserLoginProcessCompleteRspBody) GetDataAnalysisPriv() bool {
if x != nil && x.DataAnalysisPriv != nil {
return *x.DataAnalysisPriv
}
return false
}
func (x *QDUserLoginProcessCompleteRspBody) GetDeviceLock() bool {
if x != nil && x.DeviceLock != nil {
return *x.DeviceLock
}
return false
}
func (x *QDUserLoginProcessCompleteRspBody) GetModulePrivilege() uint64 {
if x != nil && x.ModulePrivilege != nil {
return *x.ModulePrivilege
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetModuleSubPrivilege() []uint32 {
if x != nil {
return x.ModuleSubPrivilege
}
return nil
}
func (x *QDUserLoginProcessCompleteRspBody) GetMasterSet() uint32 {
if x != nil && x.MasterSet != nil {
return *x.MasterSet
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetExtSet() uint32 {
if x != nil && x.ExtSet != nil {
return *x.ExtSet
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetCorpConfProperty() uint64 {
if x != nil && x.CorpConfProperty != nil {
return *x.CorpConfProperty
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetCorpuin() uint64 {
if x != nil && x.Corpuin != nil {
return *x.Corpuin
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetKfaccount() uint64 {
if x != nil && x.Kfaccount != nil {
return *x.Kfaccount
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetSecurityLevel() uint32 {
if x != nil && x.SecurityLevel != nil {
return *x.SecurityLevel
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetMsgTitle() string {
if x != nil && x.MsgTitle != nil {
return *x.MsgTitle
}
return ""
}
func (x *QDUserLoginProcessCompleteRspBody) GetSuccNoticeMsg() string {
if x != nil && x.SuccNoticeMsg != nil {
return *x.SuccNoticeMsg
}
return ""
}
func (x *QDUserLoginProcessCompleteRspBody) GetNameAccount() uint64 {
if x != nil && x.NameAccount != nil {
return *x.NameAccount
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetCrmMigrateFlag() uint32 {
if x != nil && x.CrmMigrateFlag != nil {
return *x.CrmMigrateFlag
}
return 0
}
func (x *QDUserLoginProcessCompleteRspBody) GetExtuinName() string {
if x != nil && x.ExtuinName != nil {
return *x.ExtuinName
}
return ""
}
func (x *QDUserLoginProcessCompleteRspBody) GetOpenAccountTime() uint32 {
if x != nil && x.OpenAccountTime != nil {
return *x.OpenAccountTime
}
return 0
} }
type RetInfo struct { type RetInfo struct {
RetCode *uint32 `protobuf:"varint,1,opt"` RetCode proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrorMsg *string `protobuf:"bytes,2,opt"` ErrorMsg proto.Option[string] `protobuf:"bytes,2,opt"`
} _ [0]func()
func (x *RetInfo) GetRetCode() uint32 {
if x != nil && x.RetCode != nil {
return *x.RetCode
}
return 0
}
func (x *RetInfo) GetErrorMsg() string {
if x != nil && x.ErrorMsg != nil {
return *x.ErrorMsg
}
return ""
} }
type C3F6CRMMsgHead struct { type C3F6CRMMsgHead struct {
CrmSubCmd *uint32 `protobuf:"varint,1,opt"` CrmSubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
HeadLen *uint32 `protobuf:"varint,2,opt"` HeadLen proto.Option[uint32] `protobuf:"varint,2,opt"`
VerNo *uint32 `protobuf:"varint,3,opt"` VerNo proto.Option[uint32] `protobuf:"varint,3,opt"`
KfUin *uint64 `protobuf:"varint,4,opt"` KfUin proto.Option[uint64] `protobuf:"varint,4,opt"`
Seq *uint32 `protobuf:"varint,5,opt"` Seq proto.Option[uint32] `protobuf:"varint,5,opt"`
PackNum *uint32 `protobuf:"varint,6,opt"` PackNum proto.Option[uint32] `protobuf:"varint,6,opt"`
CurPack *uint32 `protobuf:"varint,7,opt"` CurPack proto.Option[uint32] `protobuf:"varint,7,opt"`
BufSig *string `protobuf:"bytes,8,opt"` BufSig proto.Option[string] `protobuf:"bytes,8,opt"`
Clienttype *uint32 `protobuf:"varint,9,opt"` Clienttype proto.Option[uint32] `protobuf:"varint,9,opt"`
LaborUin *uint64 `protobuf:"varint,10,opt"` LaborUin proto.Option[uint64] `protobuf:"varint,10,opt"`
LaborName *string `protobuf:"bytes,11,opt"` LaborName proto.Option[string] `protobuf:"bytes,11,opt"`
Kfaccount *uint64 `protobuf:"varint,12,opt"` Kfaccount proto.Option[uint64] `protobuf:"varint,12,opt"`
TraceId *string `protobuf:"bytes,13,opt"` TraceId proto.Option[string] `protobuf:"bytes,13,opt"`
AppId *uint32 `protobuf:"varint,14,opt"` AppId proto.Option[uint32] `protobuf:"varint,14,opt"`
} _ [0]func()
func (x *C3F6CRMMsgHead) GetCrmSubCmd() uint32 {
if x != nil && x.CrmSubCmd != nil {
return *x.CrmSubCmd
}
return 0
}
func (x *C3F6CRMMsgHead) GetHeadLen() uint32 {
if x != nil && x.HeadLen != nil {
return *x.HeadLen
}
return 0
}
func (x *C3F6CRMMsgHead) GetVerNo() uint32 {
if x != nil && x.VerNo != nil {
return *x.VerNo
}
return 0
}
func (x *C3F6CRMMsgHead) GetKfUin() uint64 {
if x != nil && x.KfUin != nil {
return *x.KfUin
}
return 0
}
func (x *C3F6CRMMsgHead) GetSeq() uint32 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *C3F6CRMMsgHead) GetPackNum() uint32 {
if x != nil && x.PackNum != nil {
return *x.PackNum
}
return 0
}
func (x *C3F6CRMMsgHead) GetCurPack() uint32 {
if x != nil && x.CurPack != nil {
return *x.CurPack
}
return 0
}
func (x *C3F6CRMMsgHead) GetBufSig() string {
if x != nil && x.BufSig != nil {
return *x.BufSig
}
return ""
}
func (x *C3F6CRMMsgHead) GetClienttype() uint32 {
if x != nil && x.Clienttype != nil {
return *x.Clienttype
}
return 0
}
func (x *C3F6CRMMsgHead) GetLaborUin() uint64 {
if x != nil && x.LaborUin != nil {
return *x.LaborUin
}
return 0
}
func (x *C3F6CRMMsgHead) GetLaborName() string {
if x != nil && x.LaborName != nil {
return *x.LaborName
}
return ""
}
func (x *C3F6CRMMsgHead) GetKfaccount() uint64 {
if x != nil && x.Kfaccount != nil {
return *x.Kfaccount
}
return 0
}
func (x *C3F6CRMMsgHead) GetTraceId() string {
if x != nil && x.TraceId != nil {
return *x.TraceId
}
return ""
}
func (x *C3F6CRMMsgHead) GetAppId() uint32 {
if x != nil && x.AppId != nil {
return *x.AppId
}
return 0
} }

View File

@ -1,5 +1,5 @@
syntax = "proto2"; syntax = "proto2";
option go_package = "./;cmd0x3f6"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x3f6";
message C3F6ReqBody { message C3F6ReqBody {
optional uint32 subCmd = 1; optional uint32 subCmd = 1;

View File

@ -1,667 +1,131 @@
// Code generated by protoc-gen-golite. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// source: smbcmd0x519.proto // source: pb/cmd0x6ff/smbcmd0x519.proto
package cmd0x6ff package cmd0x6ff
import (
proto "github.com/RomiChan/protobuf/proto"
)
type C519CRMMsgHead struct { type C519CRMMsgHead struct {
CrmSubCmd *uint32 `protobuf:"varint,1,opt"` CrmSubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
HeadLen *uint32 `protobuf:"varint,2,opt"` HeadLen proto.Option[uint32] `protobuf:"varint,2,opt"`
VerNo *uint32 `protobuf:"varint,3,opt"` VerNo proto.Option[uint32] `protobuf:"varint,3,opt"`
KfUin *uint64 `protobuf:"varint,4,opt"` KfUin proto.Option[uint64] `protobuf:"varint,4,opt"`
Seq *uint32 `protobuf:"varint,5,opt"` Seq proto.Option[uint32] `protobuf:"varint,5,opt"`
PackNum *uint32 `protobuf:"varint,6,opt"` PackNum proto.Option[uint32] `protobuf:"varint,6,opt"`
CurPack *uint32 `protobuf:"varint,7,opt"` CurPack proto.Option[uint32] `protobuf:"varint,7,opt"`
BufSig *string `protobuf:"bytes,8,opt"` BufSig proto.Option[string] `protobuf:"bytes,8,opt"`
PubQq *uint64 `protobuf:"varint,9,opt"` PubQq proto.Option[uint64] `protobuf:"varint,9,opt"`
Clienttype *uint32 `protobuf:"varint,10,opt"` Clienttype proto.Option[uint32] `protobuf:"varint,10,opt"`
LaborUin *uint64 `protobuf:"varint,11,opt"` LaborUin proto.Option[uint64] `protobuf:"varint,11,opt"`
LaborName *string `protobuf:"bytes,12,opt"` LaborName proto.Option[string] `protobuf:"bytes,12,opt"`
Puin *uint64 `protobuf:"varint,13,opt"` Puin proto.Option[uint64] `protobuf:"varint,13,opt"`
} _ [0]func()
func (x *C519CRMMsgHead) GetCrmSubCmd() uint32 {
if x != nil && x.CrmSubCmd != nil {
return *x.CrmSubCmd
}
return 0
}
func (x *C519CRMMsgHead) GetHeadLen() uint32 {
if x != nil && x.HeadLen != nil {
return *x.HeadLen
}
return 0
}
func (x *C519CRMMsgHead) GetVerNo() uint32 {
if x != nil && x.VerNo != nil {
return *x.VerNo
}
return 0
}
func (x *C519CRMMsgHead) GetKfUin() uint64 {
if x != nil && x.KfUin != nil {
return *x.KfUin
}
return 0
}
func (x *C519CRMMsgHead) GetSeq() uint32 {
if x != nil && x.Seq != nil {
return *x.Seq
}
return 0
}
func (x *C519CRMMsgHead) GetPackNum() uint32 {
if x != nil && x.PackNum != nil {
return *x.PackNum
}
return 0
}
func (x *C519CRMMsgHead) GetCurPack() uint32 {
if x != nil && x.CurPack != nil {
return *x.CurPack
}
return 0
}
func (x *C519CRMMsgHead) GetBufSig() string {
if x != nil && x.BufSig != nil {
return *x.BufSig
}
return ""
}
func (x *C519CRMMsgHead) GetPubQq() uint64 {
if x != nil && x.PubQq != nil {
return *x.PubQq
}
return 0
}
func (x *C519CRMMsgHead) GetClienttype() uint32 {
if x != nil && x.Clienttype != nil {
return *x.Clienttype
}
return 0
}
func (x *C519CRMMsgHead) GetLaborUin() uint64 {
if x != nil && x.LaborUin != nil {
return *x.LaborUin
}
return 0
}
func (x *C519CRMMsgHead) GetLaborName() string {
if x != nil && x.LaborName != nil {
return *x.LaborName
}
return ""
}
func (x *C519CRMMsgHead) GetPuin() uint64 {
if x != nil && x.Puin != nil {
return *x.Puin
}
return 0
} }
type GetNavigationMenuReqBody struct { type GetNavigationMenuReqBody struct {
Puin *uint64 `protobuf:"varint,1,opt"` Puin proto.Option[uint64] `protobuf:"varint,1,opt"`
Uin *uint64 `protobuf:"varint,2,opt"` Uin proto.Option[uint64] `protobuf:"varint,2,opt"`
VerNo *uint32 `protobuf:"varint,3,opt"` VerNo proto.Option[uint32] `protobuf:"varint,3,opt"`
} _ [0]func()
func (x *GetNavigationMenuReqBody) GetPuin() uint64 {
if x != nil && x.Puin != nil {
return *x.Puin
}
return 0
}
func (x *GetNavigationMenuReqBody) GetUin() uint64 {
if x != nil && x.Uin != nil {
return *x.Uin
}
return 0
}
func (x *GetNavigationMenuReqBody) GetVerNo() uint32 {
if x != nil && x.VerNo != nil {
return *x.VerNo
}
return 0
} }
type GetNavigationMenuRspBody struct { type GetNavigationMenuRspBody struct {
Ret *C519RetInfo `protobuf:"bytes,1,opt"` Ret *C519RetInfo `protobuf:"bytes,1,opt"`
IsShow *int32 `protobuf:"varint,2,opt"` IsShow proto.Option[int32] `protobuf:"varint,2,opt"`
UctMsg *string `protobuf:"bytes,3,opt"` UctMsg proto.Option[string] `protobuf:"bytes,3,opt"`
VerNo *uint32 `protobuf:"varint,4,opt"` VerNo proto.Option[uint32] `protobuf:"varint,4,opt"`
} _ [0]func()
func (x *GetNavigationMenuRspBody) GetRet() *C519RetInfo {
if x != nil {
return x.Ret
}
return nil
}
func (x *GetNavigationMenuRspBody) GetIsShow() int32 {
if x != nil && x.IsShow != nil {
return *x.IsShow
}
return 0
}
func (x *GetNavigationMenuRspBody) GetUctMsg() string {
if x != nil && x.UctMsg != nil {
return *x.UctMsg
}
return ""
}
func (x *GetNavigationMenuRspBody) GetVerNo() uint32 {
if x != nil && x.VerNo != nil {
return *x.VerNo
}
return 0
} }
type C519ReqBody struct { type C519ReqBody struct {
SubCmd *uint32 `protobuf:"varint,1,opt"` SubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
CrmCommonHead *C519CRMMsgHead `protobuf:"bytes,2,opt"` CrmCommonHead *C519CRMMsgHead `protobuf:"bytes,2,opt"`
GetAddressDetailListReqBody *GetAddressDetailListReqBody `protobuf:"bytes,33,opt"` GetAddressDetailListReqBody *GetAddressDetailListReqBody `protobuf:"bytes,33,opt"` // optional GetNavigationMenuReqBody getNavigationMenuReq = 35;
GetNavigationMenuReq *GetNavigationMenuReqBody `protobuf:"bytes,35,opt"` _ [0]func()
}
func (x *C519ReqBody) GetSubCmd() uint32 {
if x != nil && x.SubCmd != nil {
return *x.SubCmd
}
return 0
}
func (x *C519ReqBody) GetCrmCommonHead() *C519CRMMsgHead {
if x != nil {
return x.CrmCommonHead
}
return nil
}
func (x *C519ReqBody) GetGetAddressDetailListReqBody() *GetAddressDetailListReqBody {
if x != nil {
return x.GetAddressDetailListReqBody
}
return nil
}
func (x *C519ReqBody) GetGetNavigationMenuReq() *GetNavigationMenuReqBody {
if x != nil {
return x.GetNavigationMenuReq
}
return nil
} }
type C519RetInfo struct { type C519RetInfo struct {
RetCode *uint32 `protobuf:"varint,1,opt"` RetCode proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrorMsg *string `protobuf:"bytes,2,opt"` ErrorMsg proto.Option[string] `protobuf:"bytes,2,opt"`
} _ [0]func()
func (x *C519RetInfo) GetRetCode() uint32 {
if x != nil && x.RetCode != nil {
return *x.RetCode
}
return 0
}
func (x *C519RetInfo) GetErrorMsg() string {
if x != nil && x.ErrorMsg != nil {
return *x.ErrorMsg
}
return ""
} }
type C519RspBody struct { type C519RspBody struct {
SubCmd *uint32 `protobuf:"varint,1,opt"` SubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
CrmCommonHead *C519CRMMsgHead `protobuf:"bytes,2,opt"` // optional C519CRMMsgHead crmCommonHead = 2;
GetAddressDetailListRspBody *GetAddressDetailListRspBody `protobuf:"bytes,33,opt"` GetAddressDetailListRspBody *GetAddressDetailListRspBody `protobuf:"bytes,33,opt"` //optional GetNavigationMenuRspBody getNavigationMenuRsp = 35;
GetNavigationMenuRsp *GetNavigationMenuRspBody `protobuf:"bytes,35,opt"` _ [0]func()
}
func (x *C519RspBody) GetSubCmd() uint32 {
if x != nil && x.SubCmd != nil {
return *x.SubCmd
}
return 0
}
func (x *C519RspBody) GetCrmCommonHead() *C519CRMMsgHead {
if x != nil {
return x.CrmCommonHead
}
return nil
}
func (x *C519RspBody) GetGetAddressDetailListRspBody() *GetAddressDetailListRspBody {
if x != nil {
return x.GetAddressDetailListRspBody
}
return nil
}
func (x *C519RspBody) GetGetNavigationMenuRsp() *GetNavigationMenuRspBody {
if x != nil {
return x.GetNavigationMenuRsp
}
return nil
} }
type GetAddressDetailListReqBody struct { type GetAddressDetailListReqBody struct {
Timestamp *uint32 `protobuf:"fixed32,1,opt"` Timestamp proto.Option[uint32] `protobuf:"fixed32,1,opt"`
Timestamp2 *uint64 `protobuf:"fixed64,2,opt"` Timestamp2 proto.Option[uint64] `protobuf:"fixed64,2,opt"`
} _ [0]func()
func (x *GetAddressDetailListReqBody) GetTimestamp() uint32 {
if x != nil && x.Timestamp != nil {
return *x.Timestamp
}
return 0
}
func (x *GetAddressDetailListReqBody) GetTimestamp2() uint64 {
if x != nil && x.Timestamp2 != nil {
return *x.Timestamp2
}
return 0
} }
type GetAddressDetailListRspBody struct { type GetAddressDetailListRspBody struct {
Ret *C519RetInfo `protobuf:"bytes,1,opt"` // optional C519RetInfo ret = 1;
Timestamp *uint32 `protobuf:"fixed32,2,opt"` Timestamp proto.Option[uint32] `protobuf:"fixed32,2,opt"`
Full *bool `protobuf:"varint,3,opt"` Full proto.Option[bool] `protobuf:"varint,3,opt"`
AddressDetail []*AddressDetail `protobuf:"bytes,4,rep"` AddressDetail []*AddressDetail `protobuf:"bytes,4,rep"`
Timestamp2 *uint64 `protobuf:"fixed64,5,opt"` Timestamp2 proto.Option[uint64] `protobuf:"fixed64,5,opt"`
}
func (x *GetAddressDetailListRspBody) GetRet() *C519RetInfo {
if x != nil {
return x.Ret
}
return nil
}
func (x *GetAddressDetailListRspBody) GetTimestamp() uint32 {
if x != nil && x.Timestamp != nil {
return *x.Timestamp
}
return 0
}
func (x *GetAddressDetailListRspBody) GetFull() bool {
if x != nil && x.Full != nil {
return *x.Full
}
return false
}
func (x *GetAddressDetailListRspBody) GetAddressDetail() []*AddressDetail {
if x != nil {
return x.AddressDetail
}
return nil
}
func (x *GetAddressDetailListRspBody) GetTimestamp2() uint64 {
if x != nil && x.Timestamp2 != nil {
return *x.Timestamp2
}
return 0
} }
type AddressDetail struct { type AddressDetail struct {
Aid *uint32 `protobuf:"varint,1,opt"` Aid proto.Option[uint32] `protobuf:"varint,1,opt"`
ModifyTime *uint32 `protobuf:"fixed32,2,opt"` ModifyTime proto.Option[uint32] `protobuf:"fixed32,2,opt"`
CreateTime *uint32 `protobuf:"fixed32,3,opt"` CreateTime proto.Option[uint32] `protobuf:"fixed32,3,opt"`
Status *uint32 `protobuf:"varint,4,opt"` Status proto.Option[uint32] `protobuf:"varint,4,opt"`
Groupid *uint32 `protobuf:"varint,5,opt"` Groupid proto.Option[uint32] `protobuf:"varint,5,opt"`
AddGroupName []byte `protobuf:"bytes,6,opt"` AddGroupName []byte `protobuf:"bytes,6,opt"`
Name []byte `protobuf:"bytes,7,opt"` Name []byte `protobuf:"bytes,7,opt"`
Gender *uint32 `protobuf:"varint,8,opt"` Gender proto.Option[uint32] `protobuf:"varint,8,opt"`
Birthday *uint32 `protobuf:"fixed32,9,opt"` Birthday proto.Option[uint32] `protobuf:"fixed32,9,opt"`
Company0 []byte `protobuf:"bytes,10,opt"` Company0 []byte `protobuf:"bytes,10,opt"`
CompanyPosition0 []byte `protobuf:"bytes,11,opt"` CompanyPosition0 []byte `protobuf:"bytes,11,opt"`
Company1 []byte `protobuf:"bytes,12,opt"` Company1 []byte `protobuf:"bytes,12,opt"`
CompanyPosition1 []byte `protobuf:"bytes,13,opt"` CompanyPosition1 []byte `protobuf:"bytes,13,opt"`
FixedPhone0 []byte `protobuf:"bytes,14,opt"` FixedPhone0 []byte `protobuf:"bytes,14,opt"`
FixedPhone1 []byte `protobuf:"bytes,15,opt"` FixedPhone1 []byte `protobuf:"bytes,15,opt"`
Email0 []byte `protobuf:"bytes,16,opt"` Email0 []byte `protobuf:"bytes,16,opt"`
Email1 []byte `protobuf:"bytes,17,opt"` Email1 []byte `protobuf:"bytes,17,opt"`
Fax0 []byte `protobuf:"bytes,18,opt"` Fax0 []byte `protobuf:"bytes,18,opt"`
Fax1 []byte `protobuf:"bytes,19,opt"` Fax1 []byte `protobuf:"bytes,19,opt"`
Comment []byte `protobuf:"bytes,20,opt"` Comment []byte `protobuf:"bytes,20,opt"`
HeadUrl []byte `protobuf:"bytes,21,opt"` HeadUrl []byte `protobuf:"bytes,21,opt"`
MobilePhone []*AddressMobileInfo `protobuf:"bytes,22,rep"` // repeated AddressMobileInfo mobilePhone = 22;
MobilePhoneUpdated *bool `protobuf:"varint,23,opt"` MobilePhoneUpdated proto.Option[bool] `protobuf:"varint,23,opt"`
Qq []*AddressQQinfo `protobuf:"bytes,24,rep"` Qq []*AddressQQinfo `protobuf:"bytes,24,rep"`
QqPhoneUpdated *bool `protobuf:"varint,25,opt"` QqPhoneUpdated proto.Option[bool] `protobuf:"varint,25,opt"`
ModifyTime2 *uint64 `protobuf:"fixed64,26,opt"` ModifyTime2 proto.Option[uint64] `protobuf:"fixed64,26,opt"`
ClientRegion *NewBizClientRegion `protobuf:"bytes,27,opt"`
ClientRegionCode *NewBizClientRegionCode `protobuf:"bytes,28,opt"`
}
func (x *AddressDetail) GetAid() uint32 {
if x != nil && x.Aid != nil {
return *x.Aid
}
return 0
}
func (x *AddressDetail) GetModifyTime() uint32 {
if x != nil && x.ModifyTime != nil {
return *x.ModifyTime
}
return 0
}
func (x *AddressDetail) GetCreateTime() uint32 {
if x != nil && x.CreateTime != nil {
return *x.CreateTime
}
return 0
}
func (x *AddressDetail) GetStatus() uint32 {
if x != nil && x.Status != nil {
return *x.Status
}
return 0
}
func (x *AddressDetail) GetGroupid() uint32 {
if x != nil && x.Groupid != nil {
return *x.Groupid
}
return 0
}
func (x *AddressDetail) GetAddGroupName() []byte {
if x != nil {
return x.AddGroupName
}
return nil
}
func (x *AddressDetail) GetName() []byte {
if x != nil {
return x.Name
}
return nil
}
func (x *AddressDetail) GetGender() uint32 {
if x != nil && x.Gender != nil {
return *x.Gender
}
return 0
}
func (x *AddressDetail) GetBirthday() uint32 {
if x != nil && x.Birthday != nil {
return *x.Birthday
}
return 0
}
func (x *AddressDetail) GetCompany0() []byte {
if x != nil {
return x.Company0
}
return nil
}
func (x *AddressDetail) GetCompanyPosition0() []byte {
if x != nil {
return x.CompanyPosition0
}
return nil
}
func (x *AddressDetail) GetCompany1() []byte {
if x != nil {
return x.Company1
}
return nil
}
func (x *AddressDetail) GetCompanyPosition1() []byte {
if x != nil {
return x.CompanyPosition1
}
return nil
}
func (x *AddressDetail) GetFixedPhone0() []byte {
if x != nil {
return x.FixedPhone0
}
return nil
}
func (x *AddressDetail) GetFixedPhone1() []byte {
if x != nil {
return x.FixedPhone1
}
return nil
}
func (x *AddressDetail) GetEmail0() []byte {
if x != nil {
return x.Email0
}
return nil
}
func (x *AddressDetail) GetEmail1() []byte {
if x != nil {
return x.Email1
}
return nil
}
func (x *AddressDetail) GetFax0() []byte {
if x != nil {
return x.Fax0
}
return nil
}
func (x *AddressDetail) GetFax1() []byte {
if x != nil {
return x.Fax1
}
return nil
}
func (x *AddressDetail) GetComment() []byte {
if x != nil {
return x.Comment
}
return nil
}
func (x *AddressDetail) GetHeadUrl() []byte {
if x != nil {
return x.HeadUrl
}
return nil
}
func (x *AddressDetail) GetMobilePhone() []*AddressMobileInfo {
if x != nil {
return x.MobilePhone
}
return nil
}
func (x *AddressDetail) GetMobilePhoneUpdated() bool {
if x != nil && x.MobilePhoneUpdated != nil {
return *x.MobilePhoneUpdated
}
return false
}
func (x *AddressDetail) GetQq() []*AddressQQinfo {
if x != nil {
return x.Qq
}
return nil
}
func (x *AddressDetail) GetQqPhoneUpdated() bool {
if x != nil && x.QqPhoneUpdated != nil {
return *x.QqPhoneUpdated
}
return false
}
func (x *AddressDetail) GetModifyTime2() uint64 {
if x != nil && x.ModifyTime2 != nil {
return *x.ModifyTime2
}
return 0
}
func (x *AddressDetail) GetClientRegion() *NewBizClientRegion {
if x != nil {
return x.ClientRegion
}
return nil
}
func (x *AddressDetail) GetClientRegionCode() *NewBizClientRegionCode {
if x != nil {
return x.ClientRegionCode
}
return nil
} }
type AddressMobileInfo struct { type AddressMobileInfo struct {
Index *uint32 `protobuf:"varint,1,opt"` Index proto.Option[uint32] `protobuf:"varint,1,opt"`
Account []byte `protobuf:"bytes,2,opt"` Account []byte `protobuf:"bytes,2,opt"`
FormattedAccount []byte `protobuf:"bytes,5,opt"` FormattedAccount []byte `protobuf:"bytes,5,opt"`
}
func (x *AddressMobileInfo) GetIndex() uint32 {
if x != nil && x.Index != nil {
return *x.Index
}
return 0
}
func (x *AddressMobileInfo) GetAccount() []byte {
if x != nil {
return x.Account
}
return nil
}
func (x *AddressMobileInfo) GetFormattedAccount() []byte {
if x != nil {
return x.FormattedAccount
}
return nil
} }
type AddressQQinfo struct { type AddressQQinfo struct {
Index *uint32 `protobuf:"varint,1,opt"` Index proto.Option[uint32] `protobuf:"varint,1,opt"`
Account *uint64 `protobuf:"varint,2,opt"` Account proto.Option[uint64] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *AddressQQinfo) GetIndex() uint32 {
if x != nil && x.Index != nil {
return *x.Index
}
return 0
}
func (x *AddressQQinfo) GetAccount() uint64 {
if x != nil && x.Account != nil {
return *x.Account
}
return 0
} }
type NewBizClientRegion struct { type NewBizClientRegion struct {
ClientNation *string `protobuf:"bytes,1,opt"` ClientNation proto.Option[string] `protobuf:"bytes,1,opt"`
ClientProvince *string `protobuf:"bytes,2,opt"` ClientProvince proto.Option[string] `protobuf:"bytes,2,opt"`
ClientCity *string `protobuf:"bytes,3,opt"` ClientCity proto.Option[string] `protobuf:"bytes,3,opt"`
ClientRegion *string `protobuf:"bytes,4,opt"` ClientRegion proto.Option[string] `protobuf:"bytes,4,opt"`
} _ [0]func()
func (x *NewBizClientRegion) GetClientNation() string {
if x != nil && x.ClientNation != nil {
return *x.ClientNation
}
return ""
}
func (x *NewBizClientRegion) GetClientProvince() string {
if x != nil && x.ClientProvince != nil {
return *x.ClientProvince
}
return ""
}
func (x *NewBizClientRegion) GetClientCity() string {
if x != nil && x.ClientCity != nil {
return *x.ClientCity
}
return ""
}
func (x *NewBizClientRegion) GetClientRegion() string {
if x != nil && x.ClientRegion != nil {
return *x.ClientRegion
}
return ""
} }
type NewBizClientRegionCode struct { type NewBizClientRegionCode struct {
Nationid *uint64 `protobuf:"varint,1,opt"` Nationid proto.Option[uint64] `protobuf:"varint,1,opt"`
Provinceid *uint64 `protobuf:"varint,2,opt"` Provinceid proto.Option[uint64] `protobuf:"varint,2,opt"`
Cityid *uint64 `protobuf:"varint,3,opt"` Cityid proto.Option[uint64] `protobuf:"varint,3,opt"`
Regionid *uint64 `protobuf:"varint,4,opt"` Regionid proto.Option[uint64] `protobuf:"varint,4,opt"`
} _ [0]func()
func (x *NewBizClientRegionCode) GetNationid() uint64 {
if x != nil && x.Nationid != nil {
return *x.Nationid
}
return 0
}
func (x *NewBizClientRegionCode) GetProvinceid() uint64 {
if x != nil && x.Provinceid != nil {
return *x.Provinceid
}
return 0
}
func (x *NewBizClientRegionCode) GetCityid() uint64 {
if x != nil && x.Cityid != nil {
return *x.Cityid
}
return 0
}
func (x *NewBizClientRegionCode) GetRegionid() uint64 {
if x != nil && x.Regionid != nil {
return *x.Regionid
}
return 0
} }

View File

@ -1,5 +1,5 @@
syntax = "proto2"; syntax = "proto2";
option go_package = "./;cmd0x6ff"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff";
message C519CRMMsgHead { message C519CRMMsgHead {
optional uint32 crmSubCmd = 1; optional uint32 crmSubCmd = 1;
@ -34,7 +34,7 @@ message C519ReqBody {
optional uint32 subCmd = 1; optional uint32 subCmd = 1;
optional C519CRMMsgHead crmCommonHead = 2; optional C519CRMMsgHead crmCommonHead = 2;
optional GetAddressDetailListReqBody getAddressDetailListReqBody = 33; optional GetAddressDetailListReqBody getAddressDetailListReqBody = 33;
optional GetNavigationMenuReqBody getNavigationMenuReq = 35; // optional GetNavigationMenuReqBody getNavigationMenuReq = 35;
} }
message C519RetInfo { message C519RetInfo {
@ -44,9 +44,9 @@ message C519RetInfo {
message C519RspBody { message C519RspBody {
optional uint32 subCmd = 1; optional uint32 subCmd = 1;
optional C519CRMMsgHead crmCommonHead = 2; // optional C519CRMMsgHead crmCommonHead = 2;
optional GetAddressDetailListRspBody getAddressDetailListRspBody = 33; optional GetAddressDetailListRspBody getAddressDetailListRspBody = 33;
optional GetNavigationMenuRspBody getNavigationMenuRsp = 35; //optional GetNavigationMenuRspBody getNavigationMenuRsp = 35;
} }
message GetAddressDetailListReqBody { message GetAddressDetailListReqBody {
@ -55,7 +55,7 @@ message GetAddressDetailListReqBody {
} }
message GetAddressDetailListRspBody { message GetAddressDetailListRspBody {
optional C519RetInfo ret = 1; //optional C519RetInfo ret = 1;
optional fixed32 timestamp = 2; optional fixed32 timestamp = 2;
optional bool full = 3; optional bool full = 3;
repeated AddressDetail addressDetail = 4; repeated AddressDetail addressDetail = 4;
@ -84,13 +84,13 @@ message AddressDetail {
optional bytes fax1 = 19; optional bytes fax1 = 19;
optional bytes comment = 20; optional bytes comment = 20;
optional bytes headUrl = 21; optional bytes headUrl = 21;
repeated AddressMobileInfo mobilePhone = 22; //repeated AddressMobileInfo mobilePhone = 22;
optional bool mobilePhoneUpdated = 23; optional bool mobilePhoneUpdated = 23;
repeated AddressQQinfo qq = 24; repeated AddressQQinfo qq = 24;
optional bool qqPhoneUpdated = 25; optional bool qqPhoneUpdated = 25;
optional fixed64 modifyTime2 = 26; optional fixed64 modifyTime2 = 26;
optional NewBizClientRegion clientRegion = 27; //optional NewBizClientRegion clientRegion = 27;
optional NewBizClientRegionCode clientRegionCode = 28; //optional NewBizClientRegionCode clientRegionCode = 28;
} }
message AddressMobileInfo { message AddressMobileInfo {

View File

@ -1,95 +1,31 @@
// Code generated by protoc-gen-golite. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// source: subcmd0x501.proto // source: pb/cmd0x6ff/subcmd0x501.proto
package cmd0x6ff package cmd0x6ff
import (
proto "github.com/RomiChan/protobuf/proto"
)
type C501ReqBody struct { type C501ReqBody struct {
ReqBody *SubCmd0X501ReqBody `protobuf:"bytes,1281,opt"` ReqBody *SubCmd0X501ReqBody `protobuf:"bytes,1281,opt"`
} _ [0]func()
func (x *C501ReqBody) GetReqBody() *SubCmd0X501ReqBody {
if x != nil {
return x.ReqBody
}
return nil
} }
type C501RspBody struct { type C501RspBody struct {
RspBody *SubCmd0X501RspBody `protobuf:"bytes,1281,opt"` RspBody *SubCmd0X501RspBody `protobuf:"bytes,1281,opt"`
} _ [0]func()
func (x *C501RspBody) GetRspBody() *SubCmd0X501RspBody {
if x != nil {
return x.RspBody
}
return nil
} }
type SubCmd0X501ReqBody struct { type SubCmd0X501ReqBody struct {
Uin *uint64 `protobuf:"varint,1,opt"` Uin proto.Option[uint64] `protobuf:"varint,1,opt"`
IdcId *uint32 `protobuf:"varint,2,opt"` IdcId proto.Option[uint32] `protobuf:"varint,2,opt"`
Appid *uint32 `protobuf:"varint,3,opt"` Appid proto.Option[uint32] `protobuf:"varint,3,opt"`
LoginSigType *uint32 `protobuf:"varint,4,opt"` LoginSigType proto.Option[uint32] `protobuf:"varint,4,opt"`
LoginSigTicket []byte `protobuf:"bytes,5,opt"` LoginSigTicket []byte `protobuf:"bytes,5,opt"`
RequestFlag *uint32 `protobuf:"varint,6,opt"` RequestFlag proto.Option[uint32] `protobuf:"varint,6,opt"`
ServiceTypes []uint32 `protobuf:"varint,7,rep"` ServiceTypes []uint32 `protobuf:"varint,7,rep"`
Bid *uint32 `protobuf:"varint,8,opt"` Bid proto.Option[uint32] `protobuf:"varint,8,opt"`
}
func (x *SubCmd0X501ReqBody) GetUin() uint64 {
if x != nil && x.Uin != nil {
return *x.Uin
}
return 0
}
func (x *SubCmd0X501ReqBody) GetIdcId() uint32 {
if x != nil && x.IdcId != nil {
return *x.IdcId
}
return 0
}
func (x *SubCmd0X501ReqBody) GetAppid() uint32 {
if x != nil && x.Appid != nil {
return *x.Appid
}
return 0
}
func (x *SubCmd0X501ReqBody) GetLoginSigType() uint32 {
if x != nil && x.LoginSigType != nil {
return *x.LoginSigType
}
return 0
}
func (x *SubCmd0X501ReqBody) GetLoginSigTicket() []byte {
if x != nil {
return x.LoginSigTicket
}
return nil
}
func (x *SubCmd0X501ReqBody) GetRequestFlag() uint32 {
if x != nil && x.RequestFlag != nil {
return *x.RequestFlag
}
return 0
}
func (x *SubCmd0X501ReqBody) GetServiceTypes() []uint32 {
if x != nil {
return x.ServiceTypes
}
return nil
}
func (x *SubCmd0X501ReqBody) GetBid() uint32 {
if x != nil && x.Bid != nil {
return *x.Bid
}
return 0
} }
type SubCmd0X501RspBody struct { type SubCmd0X501RspBody struct {
@ -98,77 +34,15 @@ type SubCmd0X501RspBody struct {
Addrs []*SrvAddrs `protobuf:"bytes,3,rep"` Addrs []*SrvAddrs `protobuf:"bytes,3,rep"`
} }
func (x *SubCmd0X501RspBody) GetSigSession() []byte {
if x != nil {
return x.SigSession
}
return nil
}
func (x *SubCmd0X501RspBody) GetSessionKey() []byte {
if x != nil {
return x.SessionKey
}
return nil
}
func (x *SubCmd0X501RspBody) GetAddrs() []*SrvAddrs {
if x != nil {
return x.Addrs
}
return nil
}
type SrvAddrs struct { type SrvAddrs struct {
ServiceType *uint32 `protobuf:"varint,1,opt"` ServiceType proto.Option[uint32] `protobuf:"varint,1,opt"`
Addrs []*IpAddr `protobuf:"bytes,2,rep"` Addrs []*IpAddr `protobuf:"bytes,2,rep"`
}
func (x *SrvAddrs) GetServiceType() uint32 {
if x != nil && x.ServiceType != nil {
return *x.ServiceType
}
return 0
}
func (x *SrvAddrs) GetAddrs() []*IpAddr {
if x != nil {
return x.Addrs
}
return nil
} }
type IpAddr struct { type IpAddr struct {
Type *uint32 `protobuf:"varint,1,opt"` Type proto.Option[uint32] `protobuf:"varint,1,opt"`
Ip *uint32 `protobuf:"fixed32,2,opt"` Ip proto.Option[uint32] `protobuf:"fixed32,2,opt"`
Port *uint32 `protobuf:"varint,3,opt"` Port proto.Option[uint32] `protobuf:"varint,3,opt"`
Area *uint32 `protobuf:"varint,4,opt"` Area proto.Option[uint32] `protobuf:"varint,4,opt"`
} _ [0]func()
func (x *IpAddr) GetType() uint32 {
if x != nil && x.Type != nil {
return *x.Type
}
return 0
}
func (x *IpAddr) GetIp() uint32 {
if x != nil && x.Ip != nil {
return *x.Ip
}
return 0
}
func (x *IpAddr) GetPort() uint32 {
if x != nil && x.Port != nil {
return *x.Port
}
return 0
}
func (x *IpAddr) GetArea() uint32 {
if x != nil && x.Area != nil {
return *x.Area
}
return 0
} }

View File

@ -1,5 +1,5 @@
syntax = "proto2"; syntax = "proto2";
option go_package = "./;cmd0x6ff"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff";
message C501ReqBody { message C501ReqBody {
optional SubCmd0x501ReqBody ReqBody = 1281; optional SubCmd0x501ReqBody ReqBody = 1281;

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,25 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "./;pb"; option go_package = "github.com/Mrs4s/MiraiGo/client/pb";
message SSOReserveField {
int32 flag = 9;
string qimei = 12;
int32 newconn_flag = 14;
string uid = 16;
int32 imsi = 18;
int32 network_type = 19;
int32 ip_stack_type = 20;
int32 message_type = 21;
SsoSecureInfo sec_info = 24;
int32 sso_ip_origin = 28;
}
message SsoSecureInfo {
bytes sec_sig = 1;
bytes sec_device_token = 2;
bytes sec_extra = 3;
}
message DeviceInfo { message DeviceInfo {
string bootloader = 1; string bootloader = 1;

View File

@ -1,263 +1,72 @@
// Code generated by protoc-gen-golite. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// source: group.proto // source: pb/exciting/group.proto
package exciting package exciting
type GroupFileUploadExt struct { import (
Unknown1 *int32 `protobuf:"varint,1,opt"` proto "github.com/RomiChan/protobuf/proto"
Unknown2 *int32 `protobuf:"varint,2,opt"` )
Entry *GroupFileUploadEntry `protobuf:"bytes,100,opt"`
Unknown3 *int32 `protobuf:"varint,3,opt"` type FileUploadExt struct {
Unknown1 proto.Option[int32] `protobuf:"varint,1,opt"`
Unknown2 proto.Option[int32] `protobuf:"varint,2,opt"`
Unknown3 proto.Option[int32] `protobuf:"varint,3,opt"`
Entry *FileUploadEntry `protobuf:"bytes,100,opt"`
Unknown200 proto.Option[int32] `protobuf:"varint,200,opt"`
_ [0]func()
} }
func (x *GroupFileUploadExt) GetUnknown1() int32 { type FileUploadEntry struct {
if x != nil && x.Unknown1 != nil {
return *x.Unknown1
}
return 0
}
func (x *GroupFileUploadExt) GetUnknown2() int32 {
if x != nil && x.Unknown2 != nil {
return *x.Unknown2
}
return 0
}
func (x *GroupFileUploadExt) GetEntry() *GroupFileUploadEntry {
if x != nil {
return x.Entry
}
return nil
}
func (x *GroupFileUploadExt) GetUnknown3() int32 {
if x != nil && x.Unknown3 != nil {
return *x.Unknown3
}
return 0
}
type GroupFileUploadEntry struct {
BusiBuff *ExcitingBusiInfo `protobuf:"bytes,100,opt"` BusiBuff *ExcitingBusiInfo `protobuf:"bytes,100,opt"`
FileEntry *ExcitingFileEntry `protobuf:"bytes,200,opt"` FileEntry *ExcitingFileEntry `protobuf:"bytes,200,opt"`
ClientInfo *ExcitingClientInfo `protobuf:"bytes,300,opt"` ClientInfo *ExcitingClientInfo `protobuf:"bytes,300,opt"`
FileNameInfo *ExcitingFileNameInfo `protobuf:"bytes,400,opt"` FileNameInfo *ExcitingFileNameInfo `protobuf:"bytes,400,opt"`
Host *ExcitingHostConfig `protobuf:"bytes,500,opt"` Host *ExcitingHostConfig `protobuf:"bytes,500,opt"`
} _ [0]func()
func (x *GroupFileUploadEntry) GetBusiBuff() *ExcitingBusiInfo {
if x != nil {
return x.BusiBuff
}
return nil
}
func (x *GroupFileUploadEntry) GetFileEntry() *ExcitingFileEntry {
if x != nil {
return x.FileEntry
}
return nil
}
func (x *GroupFileUploadEntry) GetClientInfo() *ExcitingClientInfo {
if x != nil {
return x.ClientInfo
}
return nil
}
func (x *GroupFileUploadEntry) GetFileNameInfo() *ExcitingFileNameInfo {
if x != nil {
return x.FileNameInfo
}
return nil
}
func (x *GroupFileUploadEntry) GetHost() *ExcitingHostConfig {
if x != nil {
return x.Host
}
return nil
} }
type ExcitingBusiInfo struct { type ExcitingBusiInfo struct {
BusId *int32 `protobuf:"varint,1,opt"` BusId proto.Option[int32] `protobuf:"varint,1,opt"`
SenderUin *int64 `protobuf:"varint,100,opt"` SenderUin proto.Option[int64] `protobuf:"varint,100,opt"`
ReceiverUin *int64 `protobuf:"varint,200,opt"` // probable ReceiverUin proto.Option[int64] `protobuf:"varint,200,opt"` // probable
GroupCode *int64 `protobuf:"varint,400,opt"` // probable GroupCode proto.Option[int64] `protobuf:"varint,400,opt"` // probable
} _ [0]func()
func (x *ExcitingBusiInfo) GetBusId() int32 {
if x != nil && x.BusId != nil {
return *x.BusId
}
return 0
}
func (x *ExcitingBusiInfo) GetSenderUin() int64 {
if x != nil && x.SenderUin != nil {
return *x.SenderUin
}
return 0
}
func (x *ExcitingBusiInfo) GetReceiverUin() int64 {
if x != nil && x.ReceiverUin != nil {
return *x.ReceiverUin
}
return 0
}
func (x *ExcitingBusiInfo) GetGroupCode() int64 {
if x != nil && x.GroupCode != nil {
return *x.GroupCode
}
return 0
} }
type ExcitingFileEntry struct { type ExcitingFileEntry struct {
FileSize *int64 `protobuf:"varint,100,opt"` FileSize proto.Option[int64] `protobuf:"varint,100,opt"`
Md5 []byte `protobuf:"bytes,200,opt"` Md5 []byte `protobuf:"bytes,200,opt"`
Sha1 []byte `protobuf:"bytes,300,opt"` Sha1 []byte `protobuf:"bytes,300,opt"`
FileId []byte `protobuf:"bytes,600,opt"` FileId []byte `protobuf:"bytes,600,opt"`
UploadKey []byte `protobuf:"bytes,700,opt"` UploadKey []byte `protobuf:"bytes,700,opt"`
}
func (x *ExcitingFileEntry) GetFileSize() int64 {
if x != nil && x.FileSize != nil {
return *x.FileSize
}
return 0
}
func (x *ExcitingFileEntry) GetMd5() []byte {
if x != nil {
return x.Md5
}
return nil
}
func (x *ExcitingFileEntry) GetSha1() []byte {
if x != nil {
return x.Sha1
}
return nil
}
func (x *ExcitingFileEntry) GetFileId() []byte {
if x != nil {
return x.FileId
}
return nil
}
func (x *ExcitingFileEntry) GetUploadKey() []byte {
if x != nil {
return x.UploadKey
}
return nil
} }
type ExcitingClientInfo struct { type ExcitingClientInfo struct {
ClientType *int32 `protobuf:"varint,100,opt"` // probable ClientType proto.Option[int32] `protobuf:"varint,100,opt"` // probable
AppId *string `protobuf:"bytes,200,opt"` AppId proto.Option[string] `protobuf:"bytes,200,opt"`
TerminalType *int32 `protobuf:"varint,300,opt"` // probable TerminalType proto.Option[int32] `protobuf:"varint,300,opt"` // probable
ClientVer *string `protobuf:"bytes,400,opt"` ClientVer proto.Option[string] `protobuf:"bytes,400,opt"`
Unknown *int32 `protobuf:"varint,600,opt"` Unknown proto.Option[int32] `protobuf:"varint,600,opt"`
} _ [0]func()
func (x *ExcitingClientInfo) GetClientType() int32 {
if x != nil && x.ClientType != nil {
return *x.ClientType
}
return 0
}
func (x *ExcitingClientInfo) GetAppId() string {
if x != nil && x.AppId != nil {
return *x.AppId
}
return ""
}
func (x *ExcitingClientInfo) GetTerminalType() int32 {
if x != nil && x.TerminalType != nil {
return *x.TerminalType
}
return 0
}
func (x *ExcitingClientInfo) GetClientVer() string {
if x != nil && x.ClientVer != nil {
return *x.ClientVer
}
return ""
}
func (x *ExcitingClientInfo) GetUnknown() int32 {
if x != nil && x.Unknown != nil {
return *x.Unknown
}
return 0
} }
type ExcitingFileNameInfo struct { type ExcitingFileNameInfo struct {
FileName *string `protobuf:"bytes,100,opt"` FileName proto.Option[string] `protobuf:"bytes,100,opt"`
} _ [0]func()
func (x *ExcitingFileNameInfo) GetFileName() string {
if x != nil && x.FileName != nil {
return *x.FileName
}
return ""
} }
type ExcitingHostConfig struct { type ExcitingHostConfig struct {
Hosts []*ExcitingHostInfo `protobuf:"bytes,200,rep"` Hosts []*ExcitingHostInfo `protobuf:"bytes,200,rep"`
} }
func (x *ExcitingHostConfig) GetHosts() []*ExcitingHostInfo {
if x != nil {
return x.Hosts
}
return nil
}
type ExcitingHostInfo struct { type ExcitingHostInfo struct {
Url *ExcitingUrlInfo `protobuf:"bytes,1,opt"` Url *ExcitingUrlInfo `protobuf:"bytes,1,opt"`
Port *int32 `protobuf:"varint,2,opt"` Port proto.Option[int32] `protobuf:"varint,2,opt"`
} _ [0]func()
func (x *ExcitingHostInfo) GetUrl() *ExcitingUrlInfo {
if x != nil {
return x.Url
}
return nil
}
func (x *ExcitingHostInfo) GetPort() int32 {
if x != nil && x.Port != nil {
return *x.Port
}
return 0
} }
type ExcitingUrlInfo struct { type ExcitingUrlInfo struct {
Unknown *int32 `protobuf:"varint,1,opt"` // not https? Unknown proto.Option[int32] `protobuf:"varint,1,opt"` // not https?
Host *string `protobuf:"bytes,2,opt"` Host proto.Option[string] `protobuf:"bytes,2,opt"`
} _ [0]func()
func (x *ExcitingUrlInfo) GetUnknown() int32 {
if x != nil && x.Unknown != nil {
return *x.Unknown
}
return 0
}
func (x *ExcitingUrlInfo) GetHost() string {
if x != nil && x.Host != nil {
return *x.Host
}
return ""
} }

Some files were not shown because too many files have changed in this diff Show More