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

Compare commits

415 Commits

Author SHA1 Message Date
9ee41fc5f9 update 2024-09-04 21:05:51 +08:00
cd753c1953 update 2024-09-04 20:55:17 +08:00
f4a63a83cd fix: panic occurred when receive voice record in group message 2024-09-03 15:11:22 +08:00
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
3b461554b7 fix highway memory leak 2022-01-18 00:50:08 +08:00
38bb31b5a6 feat(image): drop redundant bytewise comparing of gif (#243)
* feat(image): drop rebundant bytewise comparing of gif

* feat(image): drop redundant bytewise comparing of gif
2022-01-15 18:37:26 +08:00
c000bf3596 Merge pull request #230 from fumiama/master
fix(Mrs4s/go-cqhttp#1267): icmp need su permission
2022-01-15 17:17:29 +08:00
49cec45733 fix: rename icmp to tcp 2022-01-15 17:15:27 +08:00
82a55321e5 Merge branch 'master' of https://github.com/Mrs4s/MiraiGo 2022-01-15 11:12:02 +08:00
Lin
4f3862fd87 fix socket block (#236) 2022-01-15 11:11:48 +08:00
7bec167fa7 Revert "message: remove GroupImage Width & Height"
This reverts commit 9d618e2d9f.

handle zero width and height in another way.
2022-01-15 11:11:48 +08:00
e22965eb0c message: remove GroupImage Width & Height
sometimes these fields may be zero causing send failures
2022-01-15 11:11:48 +08:00
a5670ccc43 feat: use the decoder which only parses size info (#238)
* fix: add GroupImage Width & Height for webp

* fix: 简化写法

* feat: use imgsz package and drop image
2022-01-14 20:21:06 +08:00
9a45e430f1 fix 0x545 (#242) 2022-01-14 20:16:41 +08:00
Lin
41a6cb05a7 fix socket block (#236) 2022-01-06 20:52:09 +08:00
ab72f3dc28 Revert "message: remove GroupImage Width & Height"
This reverts commit 9d618e2d9f.

handle zero width and height in another way.
2021-12-28 22:09:53 +08:00
9d618e2d9f message: remove GroupImage Width & Height
sometimes these fields may be zero causing send failures
2021-12-28 21:48:23 +08:00
2c56abf4d8 fix: add wait 2021-12-26 14:15:10 +08:00
b8023f445a feat: use tcp handshake to simulate icmp echo 2021-12-26 13:32:51 +08:00
d72696a0c8 feat: use udp to simulate icmp echo 2021-12-26 13:08:38 +08:00
9946b404ae fix: heartbeat command name 2021-12-25 23:00:59 +08:00
8ba8d118ae Merge pull request #228 from wfjsw/patch-2
GuildSender的nickname给“我的频道昵称”
2021-12-25 17:56:25 +08:00
a154a63772 GuildSender的nickname给“我的频道昵称” 2021-12-25 11:15:34 +08:00
a3b4e1b994 client: move parse packet logic to transport 2021-12-24 17:18:42 +08:00
dd17e12e1b Merge pull request #227 from LXY1226/qr-param
feat: 现可使用不同的二维码生成参数获取二维码
2021-12-24 14:38:15 +08:00
LXY
6dd575b200 fix: FetchQRCode 2021-12-24 14:36:51 +08:00
LXY
1b3e84baf0 feat: change to FetchQRCodeCustomSize 2021-12-24 14:34:30 +08:00
Lin
b5697b24c5 feat: 现可使用不同的二维码生成参数获取二维码 2021-12-24 14:28:00 +08:00
254eef3a5a internal/oicq: reduce lambda 2021-12-24 14:22:10 +08:00
cf864799cb fix: WriteIntLvPacket length error (#226) 2021-12-23 23:04:13 +08:00
3e84f279c1 fix: use general write at method (#225)
* fix: use general write at method

* fix: drop WriteShortBufLenExcludeSelfAfterPos
2021-12-23 22:09:04 +08:00
b1279cf08f perf(writer): drop some lambda expressions (#224)
* perf(writer): drop some lambda expressions

* perf(writer): drop more lambda expressions

* fix: resolve conflicts

* perf(writer): drop more lambda expressions

* fix: modify function name

* perf(writer): drop more lambda expressions

* fix: some error
2021-12-23 21:04:15 +08:00
24b75e45c7 internal/oicq: refactor & support unmarshal 2021-12-23 17:04:37 +08:00
5dfa4528dd fix: fix group unmute all 2021-12-23 14:03:09 +08:00
c51e1956e8 feat: add util function to split long message (#184)
* feat: add util function to split long message

* fix: move util to message/message.go to avoid import cycle

* fix: review opinions and add test
2021-12-23 12:57:18 +08:00
ffc6cc1861 return init error (#208) 2021-12-23 12:56:04 +08:00
d2dc2e472d client: pack packet by transport 2021-12-22 22:47:00 +08:00
3e2b57eab5 internal/network: first version of transport send 2021-12-22 20:13:23 +08:00
21f8feb419 fix sso frame panic 2021-12-21 13:15:59 +08:00
afb81a32aa fix: fix private ptt 2021-12-19 15:29:30 +08:00
72ffc08b72 ci(chore): Fix stylings 2021-12-18 09:39:50 +00:00
39bea4b399 Merge pull request #222 from icarus-ai/master
石头剪子布&随机骰子
2021-12-18 17:39:29 +08:00
e700751008 解析猜拳&随机骰子
安卓QQ发来的骰子Name值是随机骰子
2021-12-18 17:35:32 +08:00
75d9c4deb4 石头剪子布
生成石头剪子布
2021-12-18 17:31:11 +08:00
950567e4b7 refactor: move Device&loginSigInfo to internal/auth 2021-12-18 15:36:52 +08:00
6b5ba9f9c4 refactor: move c.nextSeq to c.uniPacket 2021-12-17 23:50:30 +08:00
1c29ac7d96 refactor: packets.BuildUniPacket -> c.uniPacket
mostly auto-generated by

```
rf '
ex {
    var c QQClient
    var seq uint16
    var payload []byte
    var command string
    BuildUniPacket(c.Uin, seq, command, 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) -> c.uniPacket(seq, command, payload)
}
'
```
2021-12-17 22:58:41 +08:00
f464a0d474 fix: highway upload
caused by uin didn't set
2021-12-17 22:19:20 +08:00
6179d2d351 fix: ignore guild msg sending by self 2021-12-17 21:43:42 +08:00
cf49727531 fix: use go.uber.org/atomic to force atomic 2021-12-17 21:01:10 +08:00
10bac416ce Merge pull request #214 from lz1998/protocol
update protocol id
2021-12-17 17:48:30 +08:00
dc66c61d57 feat: link nested forward message 2021-12-17 12:27:14 +08:00
a5410846cb fix: decodeSidExpiredPacket response 2021-12-17 01:12:28 +08:00
6650184a1b feat: decodeSidExpiredPacket 2021-12-17 00:38:46 +08:00
d8cae988c6 Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2021-12-17 00:20:48 +08:00
68e387e91b feat: at channel support 2021-12-17 00:20:27 +08:00
ff9ef5135c remove: stash.go
these function seems not useful
2021-12-16 21:47:48 +08:00
8b99d3147e style: fix issues reported by golangci-lint 2021-12-16 20:21:49 +08:00
58e81648e5 Merge pull request #218 from LXY1226/chore-examp
chore: 将exchange_emp的错误码塞在error中
2021-12-16 18:37:33 +08:00
66ad12db03 fix: panic on PushNotify decoder 2021-12-16 18:35:20 +08:00
5a59cc656c feat: dump packet on decoder panic 2021-12-16 15:26:07 +08:00
6c1b622107 remove PttStore.GroupPttUp 2021-12-16 14:27:26 +08:00
Lin
b929afd424 chore: 将exchange_emp的错误码塞在error中 2021-12-15 19:34:03 +08:00
2e6b21bfef remove 256k & 128k pool 2021-12-15 19:20:30 +08:00
b398cec6a5 refactor: move highway.go to client/internal/highway 2021-12-15 19:13:52 +08:00
ed979508cf proto: update channel/common.proto 2021-12-14 16:43:25 +08:00
5eb78e427c fix: nil pointer error on guild channel message parser 2021-12-14 16:37:05 +08:00
d55929cf12 feat: PullGuildChannelMessage 2021-12-13 20:53:55 +08:00
f66f2b7d78 fix: fix GroupFileSystem.GetDownloadUrl error 2021-12-13 16:51:29 +08:00
5d7cb85b1e update: unknown group system msg log level updated Error -> Debug 2021-12-13 01:42:29 +08:00
06bc0bba17 feat: GuildMessageRecalledEvent 2021-12-13 01:34:54 +08:00
abe1fe7770 feat: support send guild music share 2021-12-12 17:12:47 +08:00
503db83375 fix: drop oneof in oidb.D89AGroupinfo
RomiChan/protobuf don't support oneof now
2021-12-12 14:58:30 +08:00
5ff69f3c3a binary/jce: use go/parser generator 2021-12-12 14:38:03 +08:00
45692b75f8 fix(jce): ToByes lost some fields of SvcReqRegisterNew (#217) 2021-12-12 00:04:15 +08:00
416a6c17be fix build 2021-12-11 02:36:55 +08:00
a206b0077c feat: update FetchGuildMemberProfileInfo api 2021-12-11 02:34:32 +08:00
be7293b1c0 Merge pull request #215 from fumiama/master
perf(jce): drop reflect in writer
2021-12-11 02:16:01 +08:00
bcde705a75 fix: codegen error & drop all reflect in jce 2021-12-10 14:49:53 +08:00
ed7199def8 fix: gen rely on jce 2021-12-09 23:30:12 +08:00
f1d2259956 fix(jce): replacment not equivalent 2021-12-09 22:47:16 +08:00
b041fc20e8 perf(jce): drop most reflect in writer
name                           old time/op    new time/op     delta
JceWriter_WriteMap-8             2.34µs ± 2%     0.69µs ± 4%   -70.63%  (p=0.008 n=5+5)
JceWriter_WriteJceStructRaw-8    1.28µs ± 1%     0.42µs ± 1%   -66.86%  (p=0.008 n=5+5)

name                           old speed      new speed       delta
JceWriter_WriteMap-8           39.7MB/s ± 2%  135.4MB/s ± 4%  +240.56%  (p=0.008 n=5+5)
JceWriter_WriteJceStructRaw-8  82.3MB/s ± 1%  219.9MB/s ± 1%  +167.32%  (p=0.008 n=5+5)

name                           old alloc/op   new alloc/op    delta
JceWriter_WriteMap-8             1.30kB ± 0%     0.21kB ± 0%   -84.05%  (p=0.008 n=5+5)
JceWriter_WriteJceStructRaw-8      640B ± 0%       208B ± 0%   -67.50%  (p=0.008 n=5+5)

name                           old allocs/op  new allocs/op   delta
JceWriter_WriteMap-8               30.0 ± 0%        2.0 ± 0%   -93.33%  (p=0.008 n=5+5)
JceWriter_WriteJceStructRaw-8      15.0 ± 0%        2.0 ± 0%   -86.67%  (p=0.008 n=5+5)
2021-12-09 22:25:53 +08:00
8cd25e02fc feat: FetchGuildMemberListWithRole api 2021-12-08 21:25:33 +08:00
25c67a3ee1 fix: MessageSvc.PushNotify decode error 2021-12-08 16:02:34 +08:00
2caf71a0bb fix: sso frame head parse error 2021-12-08 13:47:42 +08:00
59471c51cf update protocol id 2021-12-08 00:48:28 +08:00
2d17133a7b fix: reconnect on decrypt flag error 2021-12-07 13:29:01 +08:00
355fe4a410 Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2021-12-06 21:57:01 +08:00
d094d0483d Merge pull request #210 from fumiama/master
fix(#209): OpenWriterF encode error
2021-12-04 20:40:55 +08:00
d3ab5965d5 fix(#209): OpenWriterF encode error 2021-12-04 20:31:11 +08:00
c73026ce09 update dep 2021-12-04 18:08:59 +08:00
a2e7548a2b ref: remove guild FindMember func & remove guild members cache 2021-12-04 18:08:20 +08:00
34bb74d1d1 fix: lz4 decode error 2021-12-04 17:43:00 +08:00
e043181fc1 Merge pull request #203 from fumiama/master
perf: on jce & tea & pool & writer
2021-12-03 12:30:19 +08:00
96fd4d32e7 fix: nil pointer panic on guild event flow decoder 2021-12-02 12:00:17 +08:00
c0713ba75d ci(chore): Fix stylings 2021-12-01 17:04:11 +00:00
d08d9dbddd feat: decode compressed guild event packet. close #207 2021-12-02 01:03:10 +08:00
9778dd2369 feat: MemberJoinedGuildEvent 2021-11-27 20:28:46 +08:00
524b2b7881 update protobuf 2021-11-27 17:25:29 +08:00
5c5e61d279 fix build 2021-11-27 02:04:57 +08:00
14bb70f1f0 Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2021-11-27 02:02:32 +08:00
67bf336f22 feat: PostTopicChannelFeed 2021-11-27 02:02:13 +08:00
88279a654a ci(chore): Fix stylings 2021-11-26 17:21:51 +00:00
8b4b8269f3 feat: GetTopicChannelFeeds 2021-11-27 01:21:04 +08:00
b27ab903f1 Merge branch 'master' into master 2021-11-26 15:06:02 +08:00
13a9d087e7 perf(jce): drop reflect in reader
name                   old time/op    new time/op    delta
JceReader_ReadSlice-8    1.53ms ±90%    0.82ms ±86%  -46.30%  (p=0.017 n=16+16)

name                   old speed      new speed      delta
JceReader_ReadSlice-8   117MB/s ± 3%   228MB/s ± 4%  +94.43%  (p=0.000 n=16+16)

name                   old alloc/op   new alloc/op   delta
JceReader_ReadSlice-8     516kB ±88%     536kB ±85%     ~     (p=0.780 n=16+16)

name                   old allocs/op  new allocs/op  delta
JceReader_ReadSlice-8     25.6k ±88%     26.6k ±85%     ~     (p=0.780 n=16+16)
2021-11-26 15:02:44 +08:00
e8e50b5062 ci(chore): Fix stylings 2021-11-26 02:22:51 +00:00
915d2da4cc Merge remote-tracking branch 'origin/master'
# Conflicts:
#	client/guild.go
#	client/pb/channel/GuildChannelBase.pb.go
#	client/pb/channel/GuildFeedCloudMeta.pb.go
#	client/pb/channel/GuildFeedCloudRead.pb.go
#	client/pb/channel/MsgResponsesSvr.pb.go
#	client/pb/qweb/protocol.pb.go
2021-11-26 10:21:59 +08:00
6514cd144e protocol: update protobuf files 2021-11-26 10:19:08 +08:00
59a3a3b2c0 ci(chore): Fix stylings 2021-11-26 01:58:28 +00:00
ce23185cde protocol: update protobuf files & save work progress 2021-11-26 09:57:41 +08:00
797a912b6d protocol: update protobuf files & save work progress 2021-11-26 09:54:46 +08:00
b483e44f9c proto: use protoc-gen-golite generated files 2021-11-25 21:23:49 +08:00
4bdb791334 dep: use RomiChan/protobuf 2021-11-24 21:57:13 +08:00
4c60c8ee47 Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2021-11-24 00:04:12 +08:00
9c5515ca9f fix typo 2021-11-24 00:03:56 +08:00
a70e0f20de perf: add 0 alloc func OpenWriterF 2021-11-23 16:21:06 +08:00
26579583cc drop(pool): unnecessay nil judge 2021-11-23 15:35:52 +08:00
ec053573f1 perf(tea): use runtime.fastrand
name          old time/op    new time/op    delta
TEAen/16-8       242ns ± 1%     225ns ± 2%  -6.98%  (p=0.008 n=5+5)
TEAen/256-8     1.71µs ± 0%    1.68µs ± 0%  -1.58%  (p=0.008 n=5+5)
TEAen/4K-8      25.0µs ± 1%    25.0µs ± 0%    ~     (p=1.000 n=5+5)
TEAen/32K-8      202µs ± 1%     202µs ± 0%    ~     (p=0.548 n=5+5)
TEAde/16-8       208ns ± 0%     207ns ± 0%    ~     (p=0.198 n=5+5)
TEAde/256-8     1.65µs ± 0%    1.64µs ± 0%  -0.39%  (p=0.048 n=5+5)
TEAde/4K-8      24.6µs ± 0%    24.6µs ± 1%    ~     (p=1.000 n=5+5)
TEAde/32K-8      199µs ± 0%     199µs ± 0%    ~     (p=0.905 n=4+5)

name          old speed      new speed      delta
TEAen/16-8    66.2MB/s ± 1%  71.2MB/s ± 2%  +7.51%  (p=0.008 n=5+5)
TEAen/256-8    150MB/s ± 0%   152MB/s ± 0%  +1.61%  (p=0.008 n=5+5)
TEAen/4K-8     164MB/s ± 1%   164MB/s ± 0%    ~     (p=1.000 n=5+5)
TEAen/32K-8    162MB/s ± 1%   162MB/s ± 0%    ~     (p=0.548 n=5+5)
TEAde/16-8     154MB/s ± 0%   154MB/s ± 0%    ~     (p=0.222 n=5+5)
TEAde/256-8    165MB/s ± 0%   165MB/s ± 0%    ~     (p=0.056 n=5+5)
TEAde/4K-8     167MB/s ± 0%   167MB/s ± 1%    ~     (p=1.000 n=5+5)
TEAde/32K-8    165MB/s ± 0%   165MB/s ± 0%    ~     (p=0.825 n=4+5)
2021-11-23 13:18:58 +08:00
ec4cd4a6f3 style: internal/proto
use this package for conveniently replacing proto package
in the future.
2021-11-22 22:05:19 +08:00
6e33d2fc34 feat: stash client functions
when we restart a client, it takes too long to sync the friend & group lists. This stash functions will help us to reduce sync time when in development.

DON'T USE THIS, this is not checked.
2021-11-21 23:25:52 +08:00
e50f46a111 ci(chore): Fix stylings 2021-11-21 13:20:37 +00:00
dd5435de62 Merge pull request #202 from Kengxxiao/upstream-dev
feat: GetUserRoles
2021-11-21 21:20:10 +08:00
0ac2dad0ee Merge pull request #201 from Blackjack200/master
rename NewWriter to SelectWriter
2021-11-21 21:19:26 +08:00
56d2280cde feat: GetUserRoles 2021-11-21 17:10:44 +08:00
70af1fee57 rename NewWriter to SelectWriter 2021-11-21 12:58:34 +08:00
7e6eaa7627 Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2021-11-21 01:13:23 +08:00
a807e8ea7e feat: new channel type 2021-11-21 01:13:01 +08:00
eb705deb07 ci(chore): Fix stylings 2021-11-20 14:43:41 +00:00
0329290276 feat: GetGuildMembers page up 2021-11-20 22:42:54 +08:00
f5ff7695da Merge pull request #199 from LXY1226/md5-on-newclient
perf: no md5 on EmptyClient
2021-11-20 21:09:51 +08:00
97dcddb6d4 jce: speed up read
don't use bytes.Buffer to inline more functions

name                            old time/op    new time/op    delta
JceReader_ReadSlice-8              614µs ±68%     523µs ±70%     ~     (p=0.690 n=5+5)
RequestDataVersion2_ReadFrom-8    3.98µs ± 1%    3.54µs ± 2%  -10.96%  (p=0.008 n=5+5)

name                            old speed      new speed      delta
JceReader_ReadSlice-8            104MB/s ± 7%   121MB/s ± 2%  +16.68%  (p=0.008 n=5+5)
RequestDataVersion2_ReadFrom-8  23.4MB/s ± 1%  26.3MB/s ± 2%  +12.33%  (p=0.008 n=5+5)

name                            old alloc/op   new alloc/op   delta
JceReader_ReadSlice-8              196kB ±67%     184kB ±67%     ~     (p=0.690 n=5+5)
RequestDataVersion2_ReadFrom-8    2.59kB ± 0%    2.62kB ± 0%   +1.04%  (p=0.008 n=5+5)

name                            old allocs/op  new allocs/op  delta
JceReader_ReadSlice-8              9.02k ±67%     9.02k ±67%     ~     (p=1.000 n=5+5)
RequestDataVersion2_ReadFrom-8      46.0 ± 0%      44.0 ± 0%   -4.35%  (p=0.008 n=5+5)
2021-11-20 20:54:19 +08:00
Lin
0cb45e1d20 perf: no md5 on EmptyClient 2021-11-20 19:07:24 +08:00
43b23f4e6f jce: speed up write
name                           old time/op    new time/op    delta
JceWriter_WriteMap-8             2.46µs ± 1%    2.37µs ± 1%   -3.88%  (p=0.000 n=9+9)
JceWriter_WriteJceStructRaw-8    1.78µs ± 1%    1.29µs ± 1%  -27.38%  (p=0.000 n=9+10)

name                           old speed      new speed      delta
JceWriter_WriteMap-8           37.7MB/s ± 1%  39.3MB/s ± 1%   +4.04%  (p=0.000 n=9+9)
JceWriter_WriteJceStructRaw-8  59.0MB/s ± 1%  81.3MB/s ± 1%  +37.70%  (p=0.000 n=9+10)

name                           old alloc/op   new alloc/op   delta
JceWriter_WriteMap-8             1.30kB ± 0%    1.30kB ± 0%     ~     (all equal)
JceWriter_WriteJceStructRaw-8      724B ± 0%      640B ± 0%  -11.60%  (p=0.000 n=10+10)

name                           old allocs/op  new allocs/op  delta
JceWriter_WriteMap-8               30.0 ± 0%      30.0 ± 0%     ~     (all equal)
JceWriter_WriteJceStructRaw-8      29.0 ± 0%      15.0 ± 0%  -48.28%  (p=0.000 n=10+10)
2021-11-20 11:38:24 +08:00
3216c69d46 feat: support receive Animated Sticker 2021-11-19 23:02:45 +08:00
b9f70f7d4c ci(chore): Fix stylings 2021-11-19 14:08:44 +00:00
aa92a8bb6c feat: support send Animated Stickers 2021-11-19 22:07:46 +08:00
1f8edc2de3 update some tlv packet 2021-11-19 20:12:56 +08:00
19e2050004 feat: upload guild short video 2021-11-18 21:28:17 +08:00
574c4e57b1 perf(tea): unrolling encode
name         old time/op    new time/op    delta
TEAen/16-8      237ns ± 0%     227ns ± 1%  -4.03%  (p=0.008 n=5+5)
TEAen/256-8    1.69µs ± 0%    1.60µs ± 1%  -4.83%  (p=0.008 n=5+5)
TEAen/4K-8     25.0µs ± 1%    23.8µs ± 2%  -4.73%  (p=0.008 n=5+5)
TEAen/32K-8     199µs ± 1%     188µs ± 1%  -5.30%  (p=0.008 n=5+5)
TEAde/16-8      196ns ± 1%     194ns ± 1%  -0.89%  (p=0.016 n=5+5)
TEAde/256-8    1.56µs ± 0%    1.54µs ± 0%  -1.50%  (p=0.008 n=5+5)
TEAde/4K-8     23.5µs ± 0%    23.2µs ± 0%  -1.46%  (p=0.008 n=5+5)
TEAde/32K-8     187µs ± 1%     184µs ± 1%  -1.50%  (p=0.008 n=5+5)

name         old speed      new speed      delta
TEAen/16-8   67.6MB/s ± 1%  70.5MB/s ± 1%  +4.20%  (p=0.008 n=5+5)
TEAen/256-8   152MB/s ± 0%   160MB/s ± 1%  +5.07%  (p=0.008 n=5+5)
TEAen/4K-8    164MB/s ± 1%   172MB/s ± 2%  +4.98%  (p=0.008 n=5+5)
TEAen/32K-8   165MB/s ± 1%   174MB/s ± 1%  +5.60%  (p=0.008 n=5+5)
TEAde/16-8    163MB/s ± 1%   165MB/s ± 1%  +0.90%  (p=0.016 n=5+5)
TEAde/256-8   174MB/s ± 0%   177MB/s ± 0%  +1.51%  (p=0.008 n=5+5)
TEAde/4K-8    175MB/s ± 0%   177MB/s ± 0%  +1.48%  (p=0.008 n=5+5)
TEAde/32K-8   175MB/s ± 1%   178MB/s ± 1%  +1.52%  (p=0.008 n=5+5)
2021-11-17 13:33:25 +08:00
95364f1ff8 ci(chore): Fix stylings 2021-11-16 15:55:14 +00:00
782422fca1 Merge pull request #196 from Kengxxiao/master
频道身份组操作
2021-11-16 23:54:45 +08:00
13d2fd51da fix 2021-11-16 23:48:04 +08:00
273dac82f8 remove unused struct 2021-11-16 23:21:41 +08:00
870922b5b6 feat: ModifyRoleInGuild 2021-11-16 23:19:05 +08:00
5181282bc5 feat: support roles management 2021-11-16 23:03:10 +08:00
5ba00392ba add create role packet 2021-11-16 22:10:18 +08:00
a69487056d drop unsafe in TEA encryption
name         old time/op    new time/op    delta
TEAen/16-8      245ns ± 0%     237ns ± 0%  -3.29%  (p=0.008 n=5+5)
TEAen/256-8    1.73µs ± 0%    1.69µs ± 0%  -2.77%  (p=0.008 n=5+5)
TEAen/4K-8     24.9µs ± 0%    25.0µs ± 1%    ~     (p=0.690 n=5+5)
TEAen/32K-8     198µs ± 0%     199µs ± 1%  +0.73%  (p=0.008 n=5+5)
TEAde/16-8      215ns ± 1%     196ns ± 1%  -8.93%  (p=0.008 n=5+5)
TEAde/256-8    1.70µs ± 0%    1.56µs ± 0%  -8.08%  (p=0.008 n=5+5)
TEAde/4K-8     24.9µs ± 1%    23.5µs ± 0%  -5.50%  (p=0.008 n=5+5)
TEAde/32K-8     198µs ± 0%     187µs ± 1%  -5.34%  (p=0.008 n=5+5)

name         old speed      new speed      delta
TEAen/16-8   65.4MB/s ± 0%  67.6MB/s ± 1%  +3.39%  (p=0.008 n=5+5)
TEAen/256-8   148MB/s ± 0%   152MB/s ± 0%  +2.84%  (p=0.008 n=5+5)
TEAen/4K-8    164MB/s ± 0%   164MB/s ± 1%    ~     (p=0.587 n=5+5)
TEAen/32K-8   166MB/s ± 0%   165MB/s ± 1%  -0.72%  (p=0.008 n=5+5)
TEAde/16-8    149MB/s ± 1%   163MB/s ± 1%  +9.81%  (p=0.008 n=5+5)
TEAde/256-8   160MB/s ± 0%   174MB/s ± 0%  +8.80%  (p=0.008 n=5+5)
TEAde/4K-8    165MB/s ± 1%   175MB/s ± 0%  +5.81%  (p=0.008 n=5+5)
TEAde/32K-8   166MB/s ± 0%   175MB/s ± 1%  +5.70%  (p=0.008 n=5+5)
2021-11-16 20:54:40 +08:00
96489d00f6 add role packet 2021-11-16 20:21:18 +08:00
511e8c41ed feat: guild at encode 2021-11-15 01:08:54 +08:00
55ba62e4b8 Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2021-11-14 22:51:40 +08:00
28e93a7e2c fix: sync logic error 2021-11-14 22:50:54 +08:00
2e441ed925 drop SendGroupGift support
QQ no longer provide free group gifts, so we should drop it
2021-11-14 21:52:09 +08:00
54b035657c ci(chore): Fix stylings 2021-11-14 09:17:54 +00:00
baf792f68a Merge branch 'master' of github.com:/Mrs4s/MiraiGo 2021-11-14 17:17:16 +08:00
8427db5347 feat: support guild at message decode 2021-11-14 17:16:01 +08:00
1ea4f641d4 fix: better error handle for upload long message 2021-11-14 12:06:50 +08:00
c0ade34f1d fix: panic on get non admin-role-group guild members 2021-11-14 04:53:13 +08:00
e8cf25f6f4 fix: send guild channel message response decoder panic error 2021-11-14 04:50:06 +08:00
849be61d5a feat: GuildChannelDestroyedEvent 2021-11-14 04:46:57 +08:00
c149f28fb8 feat: GuildChannelCreatedEvent 2021-11-14 04:33:02 +08:00
d3a21e577b fix: guild image type error 2021-11-13 02:30:36 +08:00
7b125259d6 style: sort imports 2021-11-12 23:43:22 +08:00
96f4e74496 style: rename packets2 to packets 2021-11-12 16:30:20 +08:00
2a92b2755f perf: speed up tea encrypt & decrypt
name         old time/op    new time/op    delta
TEAen/16-8      260ns ± 0%     245ns ± 0%   -5.83%  (p=0.008 n=5+5)
TEAen/256-8    1.86µs ± 0%    1.73µs ± 0%   -6.86%  (p=0.008 n=5+5)
TEAen/4K-8     27.8µs ± 0%    24.9µs ± 0%  -10.19%  (p=0.008 n=5+5)
TEAde/16-8      223ns ± 1%     215ns ± 1%   -3.24%  (p=0.008 n=5+5)
TEAde/256-8    1.88µs ± 0%    1.70µs ± 0%   -9.49%  (p=0.000 n=4+5)
TEAde/4K-8     28.1µs ± 1%    24.9µs ± 1%  -11.43%  (p=0.008 n=5+5)

name         old speed      new speed      delta
TEAen/16-8   61.6MB/s ± 0%  65.4MB/s ± 0%   +6.19%  (p=0.008 n=5+5)
TEAen/256-8   138MB/s ± 0%   148MB/s ± 0%   +7.35%  (p=0.008 n=5+5)
TEAen/4K-8    147MB/s ± 0%   164MB/s ± 0%  +11.34%  (p=0.008 n=5+5)
TEAde/16-8    144MB/s ± 1%   149MB/s ± 1%   +3.34%  (p=0.008 n=5+5)
TEAde/256-8   145MB/s ± 0%   160MB/s ± 0%  +10.47%  (p=0.016 n=4+5)
TEAde/4K-8    146MB/s ± 1%   165MB/s ± 1%  +12.90%  (p=0.008 n=5+5)
2021-11-12 11:40:40 +08:00
b85fc25cd5 ci(chore): Fix stylings 2021-11-11 18:17:49 +00:00
9350021852 feat: SendGuildChannelMessage return *message.GuildChannelMessage 2021-11-12 02:17:01 +08:00
19b2c4c5c8 ci(chore): Fix stylings 2021-11-11 17:51:34 +00:00
b644f5c158 feat: UploadGuildImage 2021-11-12 01:50:30 +08:00
e3411adb3d feat: GuildChannelUpdatedEvent 2021-11-11 19:25:59 +08:00
282 changed files with 18187 additions and 83558 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

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.idea .idea
vendor/ vendor/
.DS_Store

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

@ -1,15 +1,15 @@
package jce package jce
import ( import (
"bytes"
goBinary "encoding/binary" goBinary "encoding/binary"
"io"
"math" "math"
"reflect"
"github.com/Mrs4s/MiraiGo/utils"
) )
type JceReader struct { type JceReader struct {
buf *bytes.Reader buf []byte
off int
} }
type HeadData struct { type HeadData struct {
@ -18,30 +18,38 @@ type HeadData struct {
} }
func NewJceReader(data []byte) *JceReader { func NewJceReader(data []byte) *JceReader {
buf := bytes.NewReader(data) return &JceReader{buf: data}
return &JceReader{buf: buf}
} }
func (r *JceReader) readHead() (hd HeadData, l int32) { func (r *JceReader) readHead() HeadData {
b, _ := r.buf.ReadByte() hd, l := r.peakHead()
r.off += l
return hd
}
func (r *JceReader) peakHead() (hd HeadData, l int) {
b := r.buf[r.off]
hd.Type = b & 0xF hd.Type = b & 0xF
hd.Tag = (int(b) & 0xF0) >> 4 hd.Tag = int(uint(b) >> 4)
if hd.Tag == 15 { l = 1
b, _ = r.buf.ReadByte() if hd.Tag == 0xF {
hd.Tag = int(b) & 0xFF b = r.buf[r.off+1]
return hd, 2 hd.Tag = int(uint(b))
l = 2
} }
return hd, 1
}
func (r *JceReader) peakHead() (h HeadData, l int32) {
h, l = r.readHead()
_, _ = r.buf.Seek(int64(-l), io.SeekCurrent)
return return
} }
func (r *JceReader) skipHead() {
l := 1
if int(uint(r.buf[r.off])>>4) == 0xF {
l = 2
}
r.off += l
}
func (r *JceReader) skip(l int) { func (r *JceReader) skip(l int) {
r.readBytes(l) r.off += l
} }
func (r *JceReader) skipField(t byte) { func (r *JceReader) skipField(t byte) {
@ -55,10 +63,9 @@ func (r *JceReader) skipField(t byte) {
case 3, 5: case 3, 5:
r.skip(8) r.skip(8)
case 6: case 6:
b, _ := r.buf.ReadByte() r.skip(int(r.readByte()))
r.skip(int(b))
case 7: case 7:
r.skip(int(r.readInt32())) r.skip(int(r.readUInt32()))
case 8: case 8:
s := r.ReadInt32(0) s := r.ReadInt32(0)
for i := 0; i < int(s)*2; i++ { for i := 0; i < int(s)*2; i++ {
@ -70,7 +77,7 @@ func (r *JceReader) skipField(t byte) {
r.skipNextField() r.skipNextField()
} }
case 13: case 13:
r.readHead() r.skipHead()
s := r.ReadInt32(0) s := r.ReadInt32(0)
r.skip(int(s)) r.skip(int(s))
case 10: case 10:
@ -79,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)
} }
@ -89,73 +96,65 @@ func (r *JceReader) SkipField(c int) {
} }
} }
func (r *JceReader) readBytes(len int) []byte { func (r *JceReader) readBytes(n int) []byte {
if len == 0 { if r.off+n > len(r.buf) {
return nil panic("readBytes: EOF")
}
b := make([]byte, len)
_, err := r.buf.Read(b)
if err != nil {
panic(err)
} }
b := make([]byte, n)
r.off += copy(b, r.buf[r.off:])
return b return b
} }
func (r *JceReader) readByte() byte { func (r *JceReader) readByte() byte {
b, err := r.buf.ReadByte() if r.off >= len(r.buf) {
if err != nil { panic("readByte: EOF")
panic(err)
} }
b := r.buf[r.off]
r.off++
return b return b
} }
func (r *JceReader) readUInt16() uint16 { func (r *JceReader) readUInt16() uint16 {
f, _ := r.buf.ReadByte() b := make([]byte, 2)
s, err := r.buf.ReadByte() r.off += copy(b, r.buf[r.off:])
if err != nil { return goBinary.BigEndian.Uint16(b)
panic(err)
}
return uint16((int32(f) << 8) + int32(s))
} }
func (r *JceReader) readInt32() int32 { func (r *JceReader) readUInt32() uint32 {
b := r.readBytes(4) b := make([]byte, 4)
return int32(goBinary.BigEndian.Uint32(b)) r.off += copy(b, r.buf[r.off:])
return goBinary.BigEndian.Uint32(b)
} }
func (r *JceReader) readInt64() int64 { func (r *JceReader) readUInt64() uint64 {
b := r.readBytes(8) b := make([]byte, 8)
return int64(goBinary.BigEndian.Uint64(b)) r.off += copy(b, r.buf[r.off:])
return goBinary.BigEndian.Uint64(b)
} }
func (r *JceReader) readFloat32() float32 { func (r *JceReader) readFloat32() float32 {
b := r.readInt32() return math.Float32frombits(r.readUInt32())
return math.Float32frombits(uint32(b))
} }
func (r *JceReader) readFloat64() float64 { func (r *JceReader) readFloat64() float64 {
b := r.readInt64() return math.Float64frombits(r.readUInt64())
return math.Float64frombits(uint64(b))
} }
func (r *JceReader) skipToTag(tag int) bool { func (r *JceReader) skipToTag(tag int) bool {
for { hd, l := r.peakHead()
hd, l := r.peakHead() for tag > hd.Tag && hd.Type != 11 {
if tag <= hd.Tag || hd.Type == 11 { r.skip(l)
return tag == hd.Tag
}
r.skip(int(l))
r.skipField(hd.Type) r.skipField(hd.Type)
hd, l = r.peakHead()
} }
return tag == hd.Tag
} }
func (r *JceReader) skipToStructEnd() { func (r *JceReader) skipToStructEnd() {
for { hd := r.readHead()
hd, _ := r.readHead() for hd.Type != 11 {
r.skipField(hd.Type) r.skipField(hd.Type)
if hd.Type == 11 { hd = r.readHead()
return
}
} }
} }
@ -163,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
@ -182,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
@ -199,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
@ -208,7 +207,7 @@ func (r *JceReader) ReadInt32(tag int) int32 {
case 1: case 1:
return int32(r.readUInt16()) return int32(r.readUInt16())
case 2: case 2:
return r.readInt32() return int32(r.readUInt32())
default: default:
return 0 return 0
} }
@ -218,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
@ -227,9 +226,9 @@ func (r *JceReader) ReadInt64(tag int) int64 {
case 1: case 1:
return int64(int16(r.readUInt16())) return int64(int16(r.readUInt16()))
case 2: case 2:
return int64(r.readInt32()) return int64(r.readUInt32())
case 3: case 3:
return r.readInt64() return int64(r.readUInt64())
default: default:
return 0 return 0
} }
@ -239,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
@ -254,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
@ -271,12 +270,12 @@ 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 string(r.readBytes(int(r.readByte()))) return utils.B2S(r.readBytes(int(r.readByte())))
case 7: case 7:
return string(r.readBytes(int(r.readInt32()))) return utils.B2S(r.readBytes(int(r.readUInt32())))
default: default:
return "" return ""
} }
@ -286,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)
@ -296,13 +295,32 @@ func (r *JceReader) ReadBytes(tag int) []byte {
} }
return b return b
case 13: case 13:
r.readHead() r.skipHead()
return r.readBytes(int(r.ReadInt32(0))) return r.readBytes(int(r.ReadInt32(0)))
default: default:
return nil return nil
} }
} }
func (r *JceReader) ReadByteArrArr(tag int) (baa [][]byte) {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
baa = make([][]byte, s)
for i := 0; i < int(s); i++ {
baa[i] = r.ReadBytes(0)
}
return baa
default:
return nil
}
}
/*
// ReadAny Read any type via tag, unsupported JceStruct // ReadAny Read any type via tag, unsupported JceStruct
func (r *JceReader) ReadAny(tag int) interface{} { func (r *JceReader) ReadAny(tag int) interface{} {
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
@ -315,17 +333,17 @@ func (r *JceReader) ReadAny(tag int) interface{} {
case 1: case 1:
return r.readUInt16() return r.readUInt16()
case 2: case 2:
return r.readInt32() return r.readUInt32()
case 3: case 3:
return r.readInt64() return r.readUInt64()
case 4: case 4:
return r.readFloat32() return r.readFloat32()
case 5: case 5:
return r.readFloat64() return r.readFloat64()
case 6: case 6:
return string(r.readBytes(int(r.readByte()))) return utils.B2S(r.readBytes(int(r.readByte())))
case 7: case 7:
return string(r.readBytes(int(r.readInt32()))) return utils.B2S(r.readBytes(int(r.readUInt32())))
case 8: case 8:
s := r.ReadInt32(0) s := r.ReadInt32(0)
m := make(map[interface{}]interface{}) m := make(map[interface{}]interface{})
@ -343,18 +361,19 @@ func (r *JceReader) ReadAny(tag int) interface{} {
case 12: case 12:
return 0 return 0
case 13: case 13:
r.readHead() r.skipHead()
return r.readBytes(int(r.ReadInt32(0))) return r.readBytes(int(r.ReadInt32(0)))
default: default:
return nil return nil
} }
} }
*/
func (r *JceReader) ReadJceStruct(obj IJceStruct, tag int) { 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
} }
@ -362,41 +381,365 @@ func (r *JceReader) ReadJceStruct(obj IJceStruct, tag int) {
r.skipToStructEnd() r.skipToStructEnd()
} }
func (r *JceReader) ReadMap(i interface{}, tag int) { func (r *JceReader) ReadMapStrStr(tag int) map[string]string {
v := reflect.ValueOf(i)
if v.Kind() != reflect.Map {
return
}
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 8:
s := r.ReadInt32(0)
m := make(map[string]string, s)
for i := 0; i < int(s); i++ {
m[r.ReadString(0)] = r.ReadString(1)
}
return m
default:
return nil
}
}
func (r *JceReader) ReadMapStrByte(tag int) map[string][]byte {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 8:
s := r.ReadInt32(0)
m := make(map[string][]byte, s)
for i := 0; i < int(s); i++ {
m[r.ReadString(0)] = r.ReadBytes(1)
}
return m
default:
return nil
}
}
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 {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 8:
s := r.ReadInt32(0)
m := make(map[string]map[string][]byte, s)
for i := 0; i < int(s); i++ {
m[r.ReadString(0)] = r.ReadMapStrByte(1)
}
return m
default:
return nil
}
}
/*
func (r *JceReader) ReadMap(i interface{}, tag int) {
r.readMap(reflect.ValueOf(i), tag)
}
func (r *JceReader) readMap(v reflect.Value, tag int) {
if v.Kind() != reflect.Map || !r.skipToTag(tag) {
return return
} }
t := v.Type() t := v.Type()
kt := t.Key() kt := t.Key()
kv := reflect.New(kt)
vt := t.Elem() vt := t.Elem()
vv := reflect.New(vt) r.skipHead()
r.readHead()
s := r.ReadInt32(0) s := r.ReadInt32(0)
// map with string key or string value is very common.
// specialize for string
if kt.Kind() == reflect.String && vt.Kind() == reflect.String {
for i := 0; i < int(s); i++ {
kv := reflect.ValueOf(r.ReadString(0))
vv := reflect.ValueOf(r.ReadString(1))
v.SetMapIndex(kv, vv)
}
return
}
if kt.Kind() == reflect.String {
vv := reflect.New(vt)
for i := 0; i < int(s); i++ {
kv := reflect.ValueOf(r.ReadString(0))
r.readObject(vv, 1)
v.SetMapIndex(kv, vv.Elem())
}
return
}
kv := reflect.New(kt)
vv := reflect.New(vt)
for i := 0; i < int(s); i++ { for i := 0; i < int(s); i++ {
r.ReadObject(kv.Interface(), 0) r.readObject(kv, 0)
r.ReadObject(vv.Interface(), 1) r.readObject(vv, 1)
v.SetMapIndex(kv.Elem(), vv.Elem()) v.SetMapIndex(kv.Elem(), vv.Elem())
} }
} }
*/
func (r *JceReader) ReadSlice(i interface{}, tag int) { func (r *JceReader) ReadFileStorageServerInfos(tag int) []FileStorageServerInfo {
t := reflect.TypeOf(i)
v := reflect.ValueOf(i).Elem()
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice {
return
}
t = t.Elem()
if !r.skipToTag(tag) { if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]FileStorageServerInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadBigDataIPLists(tag int) []BigDataIPList {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]BigDataIPList, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadBigDataIPInfos(tag int) []BigDataIPInfo {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]BigDataIPInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadOnlineInfos(tag int) []OnlineInfo {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]OnlineInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadInstanceInfos(tag int) []InstanceInfo {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]InstanceInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadSsoServerInfos(tag int) []SsoServerInfo {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]SsoServerInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadFriendInfos(tag int) []FriendInfo {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]FriendInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadTroopNumbers(tag int) []TroopNumber {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]TroopNumber, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadTroopMemberInfos(tag int) []TroopMemberInfo {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]TroopMemberInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadPushMessageInfos(tag int) []PushMessageInfo {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]PushMessageInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
func (r *JceReader) ReadSvcDevLoginInfos(tag int) []SvcDevLoginInfo {
if !r.skipToTag(tag) {
return nil
}
hd := r.readHead()
switch hd.Type {
case 9:
s := r.ReadInt32(0)
sl := make([]SvcDevLoginInfo, s)
for i := 0; i < int(s); i++ {
r.skipHead()
sl[i].ReadFrom(r)
r.skipToStructEnd()
}
return sl
default:
return nil
}
}
/*
func (r *JceReader) ReadSlice(i interface{}, tag int) {
r.readSlice(reflect.ValueOf(i), tag)
}
func (r *JceReader) readSlice(v reflect.Value, tag int) {
t := v.Type()
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice || !r.skipToTag(tag) {
return return
} }
v = v.Elem()
t = t.Elem()
hd, _ := r.readHead() hd, _ := r.readHead()
if hd.Type == 9 { if hd.Type == 9 {
s := r.ReadInt32(0) s := r.ReadInt32(0)
@ -404,56 +747,65 @@ func (r *JceReader) ReadSlice(i interface{}, tag int) {
t = t.Elem() t = t.Elem()
val := reflect.New(t) val := reflect.New(t)
for i := 0; i < int(s); i++ { for i := 0; i < int(s); i++ {
r.ReadObject(val.Interface(), 0) r.readObject(val, 0)
sv.Index(i).Set(val.Elem()) sv.Index(i).Set(val.Elem())
} }
v.Set(sv) v.Set(sv)
} }
if hd.Type == 13 && t.Elem().Kind() == reflect.Uint8 { if hd.Type == 13 && t.Elem().Kind() == reflect.Uint8 {
r.readHead() r.skipHead()
arr := r.readBytes(int(r.ReadInt32(0))) arr := r.readBytes(int(r.ReadInt32(0)))
v.SetBytes(arr) v.SetBytes(arr)
} }
} }
func (r *JceReader) ReadObject(i interface{}, tag int) { func (r *JceReader) ReadObject(i interface{}, tag int) {
va := reflect.ValueOf(i) r.readObject(reflect.ValueOf(i), tag)
if va.Kind() != reflect.Ptr || va.IsNil() {
return
}
if ve := va.Elem(); ve.Kind() == reflect.Map {
ve.Set(reflect.MakeMap(ve.Type()))
r.ReadMap(ve.Interface(), tag)
return
}
switch o := i.(type) {
case *byte:
*o = r.ReadByte(tag)
case *bool:
*o = r.ReadBool(tag)
case *int16:
*o = r.ReadInt16(tag)
case *int:
*o = int(r.ReadInt32(tag))
case *int32:
*o = r.ReadInt32(tag)
case *int64:
*o = r.ReadInt64(tag)
case *float32:
*o = r.ReadFloat32(tag)
case *float64:
*o = r.ReadFloat64(tag)
case *string:
*o = r.ReadString(tag)
case *[]byte:
r.ReadSlice(o, tag)
case IJceStruct:
r.readHead()
o.ReadFrom(r)
r.skipToStructEnd()
}
} }
func (r *JceReader) ReadAvailable() []byte { func (r *JceReader) readObject(v reflect.Value, tag int) {
return r.readBytes(r.buf.Len()) if v.Kind() != reflect.Ptr || v.IsNil() {
return
}
elemType := v.Type().Elem()
if elemType.Kind() == reflect.Map {
elem := v.Elem()
elem.Set(reflect.MakeMap(elem.Type()))
r.readMap(elem, tag)
return
} else if elemType.Kind() == reflect.Slice && // *[]byte
elemType.Elem().Kind() == reflect.Uint8 {
elem := v.Elem()
elem.SetBytes(r.ReadBytes(tag))
return
}
switch elemType.Kind() {
case reflect.Uint8, reflect.Int8:
*(*uint8)(pointerOf(v)) = r.ReadByte(tag)
case reflect.Bool:
*(*bool)(pointerOf(v)) = r.ReadBool(tag)
case reflect.Uint16, reflect.Int16:
*(*int16)(pointerOf(v)) = r.ReadInt16(tag)
case reflect.Uint32, reflect.Int32:
*(*int32)(pointerOf(v)) = r.ReadInt32(tag)
case reflect.Uint64, reflect.Int64:
*(*int64)(pointerOf(v)) = r.ReadInt64(tag)
case reflect.String:
*(*string)(pointerOf(v)) = r.ReadString(tag)
default:
// other cases
switch o := v.Interface().(type) {
case IJceStruct:
r.skipHead()
o.ReadFrom(r)
r.skipToStructEnd()
case *float32:
*o = r.ReadFloat32(tag)
case *float64:
*o = r.ReadFloat64(tag)
}
}
} }
*/

View File

@ -1,22 +1,27 @@
package jce package jce
import ( import (
"math/rand" "crypto/rand"
"reflect"
"strconv"
"sync"
"testing" "testing"
"unsafe"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestJceReader_ReadSlice(t *testing.T) { func TestJceReader_ReadSlice(t *testing.T) {
s := make([]int64, 50) s := make([][]byte, 50)
for i := range s { for i := range s {
s[i] = rand.Int63() b := make([]byte, 64)
_, _ = rand.Read(b)
s[i] = b
} }
w := NewJceWriter() w := NewJceWriter()
w.WriteObject(s, 1) w.WriteBytesSlice(s, 1)
r := NewJceReader(w.Bytes()) r := NewJceReader(w.Bytes())
var result []int64 result := r.ReadByteArrArr(1)
r.ReadSlice(&result, 1)
assert.Equal(t, s, result) assert.Equal(t, s, result)
} }
@ -35,10 +40,9 @@ func BenchmarkJceReader_ReadSlice(b *testing.B) {
src := w.Bytes() src := w.Bytes()
b.SetBytes(int64(len(src))) b.SetBytes(int64(len(src)))
b.StartTimer() b.StartTimer()
result := make([]BigDataIPInfo, 0)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
r := NewJceReader(src) r := NewJceReader(src)
r.ReadSlice(&result, 1) _ = r.ReadBigDataIPInfos(1)
} }
} }
@ -59,12 +63,13 @@ var req = RequestDataVersion2{
"5": { "5": {
"123": []byte(`123`), "123": []byte(`123`),
}, },
}} },
}
func TestRequestDataVersion2_ReadFrom(t *testing.T) { func TestRequestDataVersion2_ReadFrom(t *testing.T) {
// todo(wdv): fuzz test // todo(wdv): fuzz test
w := NewJceWriter() w := NewJceWriter()
w.WriteObject(req.Map, 0) w.writeMapStrMapStrBytes(req.Map, 0)
src := w.Bytes() src := w.Bytes()
result := RequestDataVersion2{} result := RequestDataVersion2{}
result.ReadFrom(NewJceReader(src)) result.ReadFrom(NewJceReader(src))
@ -73,7 +78,7 @@ func TestRequestDataVersion2_ReadFrom(t *testing.T) {
func BenchmarkRequestDataVersion2_ReadFrom(b *testing.B) { func BenchmarkRequestDataVersion2_ReadFrom(b *testing.B) {
w := NewJceWriter() w := NewJceWriter()
w.WriteObject(req.Map, 0) w.writeMapStrMapStrBytes(req.Map, 0)
src := w.Bytes() src := w.Bytes()
b.SetBytes(int64(len(src))) b.SetBytes(int64(len(src)))
result := &RequestDataVersion2{} result := &RequestDataVersion2{}
@ -93,3 +98,210 @@ func TestJceReader_ReadBytes(t *testing.T) {
assert.Equal(t, b, rb) assert.Equal(t, b, rb)
} }
func (w *JceWriter) WriteObject(i any, tag byte) {
t := reflect.TypeOf(i)
if t.Kind() == reflect.Map {
w.WriteMap(i, tag)
return
}
if t.Kind() == reflect.Slice {
if b, ok := i.([]byte); ok {
w.WriteBytes(b, tag)
return
}
w.WriteSlice(i, tag)
return
}
switch o := i.(type) {
case byte:
w.WriteByte(o, tag)
case bool:
w.WriteBool(o, tag)
case int16:
w.WriteInt16(o, tag)
case int32:
w.WriteInt32(o, tag)
case int64:
w.WriteInt64(o, tag)
case float32:
w.WriteFloat32(o, tag)
case float64:
w.WriteFloat64(o, tag)
case string:
w.WriteString(o, tag)
case IJceStruct:
w.WriteJceStruct(o, tag)
}
}
func (w *JceWriter) writeObject(v reflect.Value, tag byte) {
k := v.Kind()
if k == reflect.Map {
switch o := v.Interface().(type) {
case map[string]string:
w.writeMapStrStr(o, tag)
case map[string][]byte:
w.writeMapStrBytes(o, tag)
case map[string]map[string][]byte:
w.writeMapStrMapStrBytes(o, tag)
default:
w.writeMap(v, tag)
}
return
}
if k == reflect.Slice {
switch o := v.Interface().(type) {
case []byte:
w.WriteBytes(o, tag)
case []IJceStruct:
w.WriteJceStructSlice(o, tag)
default:
w.writeSlice(v, tag)
}
return
}
switch k {
case reflect.Uint8, reflect.Int8:
w.WriteByte(*(*byte)(pointerOf(v)), tag)
case reflect.Uint16, reflect.Int16:
w.WriteInt16(*(*int16)(pointerOf(v)), tag)
case reflect.Uint32, reflect.Int32:
w.WriteInt32(*(*int32)(pointerOf(v)), tag)
case reflect.Uint64, reflect.Int64:
w.WriteInt64(*(*int64)(pointerOf(v)), tag)
case reflect.String:
w.WriteString(v.String(), tag)
default:
switch o := v.Interface().(type) {
case IJceStruct:
w.WriteJceStruct(o, tag)
case float32:
w.WriteFloat32(o, tag)
case float64:
w.WriteFloat64(o, tag)
}
}
}
type decoder struct {
index int
id int
}
var decoderCache = sync.Map{}
// WriteJceStructRaw 写入 Jce 结构体
func (w *JceWriter) WriteJceStructRaw(s any) {
t := reflect.TypeOf(s)
if t.Kind() != reflect.Ptr {
return
}
t = t.Elem()
v := reflect.ValueOf(s).Elem()
var jceDec []decoder
dec, ok := decoderCache.Load(t)
if ok { // 从缓存中加载
jceDec = dec.([]decoder)
} else { // 初次反射
jceDec = make([]decoder, 0, t.NumField())
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
strId := field.Tag.Get("jceId")
if strId == "" {
continue
}
id, err := strconv.Atoi(strId)
if err != nil {
continue
}
jceDec = append(jceDec, decoder{
index: i,
id: id,
})
}
decoderCache.Store(t, jceDec) // 存入缓存
}
for _, dec := range jceDec {
obj := v.Field(dec.index)
w.writeObject(obj, byte(dec.id))
}
}
func (w *JceWriter) WriteJceStruct(s IJceStruct, tag byte) {
w.writeHead(10, tag)
w.WriteJceStructRaw(s)
w.writeHead(11, 0)
}
func (w *JceWriter) WriteSlice(i any, tag byte) {
va := reflect.ValueOf(i)
if va.Kind() != reflect.Slice {
panic("JceWriter.WriteSlice: not a slice")
}
w.writeSlice(va, tag)
}
func (w *JceWriter) writeSlice(slice reflect.Value, tag byte) {
if slice.Kind() != reflect.Slice {
return
}
w.writeHead(9, tag)
if slice.Len() == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
return
}
w.WriteInt32(int32(slice.Len()), 0)
for i := 0; i < slice.Len(); i++ {
v := slice.Index(i)
w.writeObject(v, 0)
}
}
func (w *JceWriter) WriteJceStructSlice(l []IJceStruct, tag byte) {
w.writeHead(9, tag)
if len(l) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
return
}
w.WriteInt32(int32(len(l)), 0)
for _, v := range l {
w.WriteJceStruct(v, 0)
}
}
func (w *JceWriter) WriteMap(m any, tag byte) {
va := reflect.ValueOf(m)
if va.Kind() != reflect.Map {
panic("JceWriter.WriteMap: not a map")
}
w.writeMap(va, tag)
}
func (w *JceWriter) writeMap(m reflect.Value, tag byte) {
if m.IsNil() {
w.writeHead(8, tag)
w.writeHead(12, 0) // w.WriteInt32(0, 0)
return
}
if m.Kind() != reflect.Map {
return
}
w.writeHead(8, tag)
w.WriteInt32(int32(m.Len()), 0)
iter := m.MapRange()
for iter.Next() {
w.writeObject(iter.Key(), 0)
w.writeObject(iter.Value(), 1)
}
}
type value struct {
typ unsafe.Pointer
data unsafe.Pointer
flag uintptr
}
func pointerOf(v reflect.Value) unsafe.Pointer {
return (*value)(unsafe.Pointer(&v)).data
}

View File

@ -1,10 +1,11 @@
package jce package jce
type IJceStruct interface { type IJceStruct interface {
// ToBytes() []byte ToBytes() []byte
ReadFrom(*JceReader) ReadFrom(*JceReader)
} }
//go:generate go run github.com/Mrs4s/MiraiGo/internal/generator/jce_gen -file=structs.go -o structs_tobytes.go
type ( type (
RequestPacket struct { RequestPacket struct {
IVersion int16 `jceId:"1"` IVersion int16 `jceId:"1"`
@ -74,7 +75,6 @@ type (
} }
SvcReqRegister struct { SvcReqRegister struct {
IJceStruct
Uin int64 `jceId:"0"` Uin int64 `jceId:"0"`
Bid int64 `jceId:"1"` Bid int64 `jceId:"1"`
ConnType byte `jceId:"2"` ConnType byte `jceId:"2"`
@ -139,7 +139,6 @@ type (
} }
SvcReqRegisterNew struct { SvcReqRegisterNew struct {
IJceStruct
RequestOptional int64 `jceId:"0"` RequestOptional int64 `jceId:"0"`
C2CMsg IJceStruct `jceId:"1"` // SvcReqGetMsgV2 C2CMsg IJceStruct `jceId:"1"` // SvcReqGetMsgV2
GroupMsg IJceStruct `jceId:"2"` // SvcReqPullGroupMsgSeq GroupMsg IJceStruct `jceId:"2"` // SvcReqPullGroupMsgSeq
@ -173,7 +172,6 @@ type (
} }
PullGroupSeqParam struct { PullGroupSeqParam struct {
IJceStruct
GroupCode int64 `jceId:"0"` GroupCode int64 `jceId:"0"`
LastSeqId int64 `jceId:"1"` LastSeqId int64 `jceId:"1"`
} }
@ -251,7 +249,6 @@ type (
} }
SvcRespPushMsg struct { SvcRespPushMsg struct {
IJceStruct
Uin int64 `jceId:"0"` Uin int64 `jceId:"0"`
DelInfos []IJceStruct `jceId:"1"` DelInfos []IJceStruct `jceId:"1"`
Svrip int32 `jceId:"2"` Svrip int32 `jceId:"2"`
@ -299,7 +296,6 @@ type (
} }
FriendListRequest struct { FriendListRequest struct {
IJceStruct
Reqtype int32 `jceId:"0"` Reqtype int32 `jceId:"0"`
IfReflush byte `jceId:"1"` IfReflush byte `jceId:"1"`
Uin int64 `jceId:"2"` Uin int64 `jceId:"2"`
@ -381,7 +377,6 @@ type (
} }
TroopListRequest struct { TroopListRequest struct {
IJceStruct
Uin int64 `jceId:"0"` Uin int64 `jceId:"0"`
GetMSFMsgFlag byte `jceId:"1"` GetMSFMsgFlag byte `jceId:"1"`
Cookies []byte `jceId:"2"` Cookies []byte `jceId:"2"`
@ -432,7 +427,6 @@ type (
} }
TroopMemberListRequest struct { TroopMemberListRequest struct {
IJceStruct
Uin int64 `jceId:"0"` Uin int64 `jceId:"0"`
GroupCode int64 `jceId:"1"` GroupCode int64 `jceId:"1"`
NextUin int64 `jceId:"2"` NextUin int64 `jceId:"2"`
@ -482,7 +476,6 @@ type (
} }
ModifyGroupCardRequest struct { ModifyGroupCardRequest struct {
IJceStruct
Zero int64 `jceId:"0"` Zero int64 `jceId:"0"`
GroupCode int64 `jceId:"1"` GroupCode int64 `jceId:"1"`
NewSeq int64 `jceId:"2"` NewSeq int64 `jceId:"2"`
@ -501,7 +494,6 @@ type (
} }
SummaryCardReq struct { SummaryCardReq struct {
IJceStruct
Uin int64 `jceId:"0"` Uin int64 `jceId:"0"`
ComeFrom int32 `jceId:"1"` ComeFrom int32 `jceId:"1"`
QzoneFeedTimestamp int64 `jceId:"2"` QzoneFeedTimestamp int64 `jceId:"2"`
@ -523,7 +515,6 @@ type (
} }
SummaryCardReqSearch struct { SummaryCardReqSearch struct {
IJceStruct
Keyword string `jceId:"0"` Keyword string `jceId:"0"`
CountryCode string `jceId:"1"` CountryCode string `jceId:"1"`
Version int32 `jceId:"2"` Version int32 `jceId:"2"`
@ -531,56 +522,40 @@ type (
} }
DelFriendReq struct { DelFriendReq struct {
IJceStruct
Uin int64 `jceId:"0"` Uin int64 `jceId:"0"`
DelUin int64 `jceId:"1"` DelUin int64 `jceId:"1"`
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) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *RequestPacket) ReadFrom(r *JceReader) { func (pkt *RequestPacket) ReadFrom(r *JceReader) {
pkt.SBuffer = []byte{}
pkt.Context = make(map[string]string)
pkt.Status = make(map[string]string)
pkt.IVersion = r.ReadInt16(1) pkt.IVersion = r.ReadInt16(1)
pkt.CPacketType = r.ReadByte(2) pkt.CPacketType = r.ReadByte(2)
pkt.IMessageType = r.ReadInt32(3) pkt.IMessageType = r.ReadInt32(3)
pkt.IRequestId = r.ReadInt32(4) pkt.IRequestId = r.ReadInt32(4)
pkt.SServantName = r.ReadString(5) pkt.SServantName = r.ReadString(5)
pkt.SFuncName = r.ReadString(6) pkt.SFuncName = r.ReadString(6)
r.ReadSlice(&pkt.SBuffer, 7) pkt.SBuffer = r.ReadBytes(7)
pkt.ITimeout = r.ReadInt32(8) pkt.ITimeout = r.ReadInt32(8)
r.ReadMap(pkt.Context, 9) // r.ReadMap(pkt.Context, 9)
r.ReadMap(pkt.Status, 10) pkt.Context = r.ReadMapStrStr(9)
} // r.ReadMap(pkt.Status, 10)
pkt.Status = r.ReadMapStrStr(10)
func (pkt *RequestDataVersion3) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
} }
func (pkt *RequestDataVersion3) ReadFrom(r *JceReader) { func (pkt *RequestDataVersion3) ReadFrom(r *JceReader) {
pkt.Map = make(map[string][]byte) pkt.Map = r.ReadMapStrByte(0)
r.ReadMap(pkt.Map, 0)
}
func (pkt *RequestDataVersion2) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
} }
func (pkt *RequestDataVersion2) ReadFrom(r *JceReader) { func (pkt *RequestDataVersion2) ReadFrom(r *JceReader) {
pkt.Map = make(map[string]map[string][]byte) pkt.Map = r.ReadMapStrMapStrByte(0)
r.ReadMap(pkt.Map, 0)
} }
func (pkt *SsoServerInfo) ReadFrom(r *JceReader) { func (pkt *SsoServerInfo) ReadFrom(r *JceReader) {
@ -590,22 +565,15 @@ func (pkt *SsoServerInfo) ReadFrom(r *JceReader) {
} }
func (pkt *FileStoragePushFSSvcList) ReadFrom(r *JceReader) { func (pkt *FileStoragePushFSSvcList) ReadFrom(r *JceReader) {
pkt.UploadList = []FileStorageServerInfo{} pkt.UploadList = r.ReadFileStorageServerInfos(0)
pkt.PicDownloadList = []FileStorageServerInfo{} pkt.PicDownloadList = r.ReadFileStorageServerInfos(1)
pkt.GPicDownloadList = []FileStorageServerInfo{} pkt.GPicDownloadList = r.ReadFileStorageServerInfos(2)
pkt.QZoneProxyServiceList = []FileStorageServerInfo{} pkt.QZoneProxyServiceList = r.ReadFileStorageServerInfos(3)
pkt.UrlEncodeServiceList = []FileStorageServerInfo{} pkt.UrlEncodeServiceList = r.ReadFileStorageServerInfos(4)
pkt.BigDataChannel = &BigDataChannel{} pkt.BigDataChannel = &BigDataChannel{}
pkt.VipEmotionList = []FileStorageServerInfo{}
pkt.C2CPicDownList = []FileStorageServerInfo{}
r.ReadSlice(&pkt.UploadList, 0)
r.ReadSlice(&pkt.PicDownloadList, 1)
r.ReadSlice(&pkt.GPicDownloadList, 2)
r.ReadSlice(&pkt.QZoneProxyServiceList, 3)
r.ReadSlice(&pkt.UrlEncodeServiceList, 4)
r.ReadJceStruct(pkt.BigDataChannel, 5) r.ReadJceStruct(pkt.BigDataChannel, 5)
r.ReadSlice(&pkt.VipEmotionList, 6) pkt.VipEmotionList = r.ReadFileStorageServerInfos(6)
r.ReadSlice(&pkt.C2CPicDownList, 7) pkt.C2CPicDownList = r.ReadFileStorageServerInfos(7)
pkt.PttList = r.ReadBytes(10) pkt.PttList = r.ReadBytes(10)
} }
@ -615,8 +583,7 @@ func (pkt *FileStorageServerInfo) ReadFrom(r *JceReader) {
} }
func (pkt *BigDataChannel) ReadFrom(r *JceReader) { func (pkt *BigDataChannel) ReadFrom(r *JceReader) {
pkt.IPLists = []BigDataIPList{} pkt.IPLists = r.ReadBigDataIPLists(0)
r.ReadSlice(&pkt.IPLists, 0)
pkt.SigSession = r.ReadBytes(1) pkt.SigSession = r.ReadBytes(1)
pkt.KeySession = r.ReadBytes(2) pkt.KeySession = r.ReadBytes(2)
pkt.SigUin = r.ReadInt64(3) pkt.SigUin = r.ReadInt64(3)
@ -625,9 +592,8 @@ func (pkt *BigDataChannel) ReadFrom(r *JceReader) {
} }
func (pkt *BigDataIPList) ReadFrom(r *JceReader) { func (pkt *BigDataIPList) ReadFrom(r *JceReader) {
pkt.IPList = []BigDataIPInfo{}
pkt.ServiceType = r.ReadInt64(0) pkt.ServiceType = r.ReadInt64(0)
r.ReadSlice(&pkt.IPList, 1) pkt.IPList = r.ReadBigDataIPInfos(1)
pkt.FragmentSize = r.ReadInt64(3) pkt.FragmentSize = r.ReadInt64(3)
} }
@ -637,12 +603,6 @@ func (pkt *BigDataIPInfo) ReadFrom(r *JceReader) {
pkt.Port = r.ReadInt64(2) pkt.Port = r.ReadInt64(2)
} }
func (pkt *SvcReqRegister) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *SvcRespRegister) ReadFrom(r *JceReader) { func (pkt *SvcRespRegister) ReadFrom(r *JceReader) {
pkt.Uin = r.ReadInt64(0) pkt.Uin = r.ReadInt64(0)
pkt.Bid = r.ReadInt64(1) pkt.Bid = r.ReadInt64(1)
@ -664,24 +624,6 @@ func (pkt *SvcRespRegister) ReadFrom(r *JceReader) {
pkt.ExtOnlineStatus = r.ReadInt64(17) pkt.ExtOnlineStatus = r.ReadInt64(17)
} }
func (pkt *FriendListRequest) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *SummaryCardReq) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *SummaryCardReqSearch) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *FriendInfo) ReadFrom(r *JceReader) { func (pkt *FriendInfo) ReadFrom(r *JceReader) {
pkt.FriendUin = r.ReadInt64(0) pkt.FriendUin = r.ReadInt64(0)
pkt.GroupId = r.ReadByte(1) pkt.GroupId = r.ReadByte(1)
@ -692,14 +634,7 @@ func (pkt *FriendInfo) ReadFrom(r *JceReader) {
pkt.Nick = r.ReadString(14) pkt.Nick = r.ReadString(14)
pkt.Network = r.ReadByte(20) pkt.Network = r.ReadByte(20)
pkt.NetworkType = r.ReadInt32(24) pkt.NetworkType = r.ReadInt32(24)
pkt.CardID = []byte{} pkt.CardID = r.ReadBytes(41)
r.ReadObject(&pkt.CardID, 41)
}
func (pkt *TroopListRequest) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
} }
func (pkt *TroopNumber) ReadFrom(r *JceReader) { func (pkt *TroopNumber) ReadFrom(r *JceReader) {
@ -712,12 +647,6 @@ func (pkt *TroopNumber) ReadFrom(r *JceReader) {
pkt.MaxGroupMemberNum = r.ReadInt64(29) pkt.MaxGroupMemberNum = r.ReadInt64(29)
} }
func (pkt *TroopMemberListRequest) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *TroopMemberInfo) ReadFrom(r *JceReader) { func (pkt *TroopMemberInfo) ReadFrom(r *JceReader) {
pkt.MemberUin = r.ReadInt64(0) pkt.MemberUin = r.ReadInt64(0)
pkt.FaceId = r.ReadInt16(1) pkt.FaceId = r.ReadInt16(1)
@ -750,8 +679,7 @@ func (pkt *PushMessageInfo) ReadFrom(r *JceReader) {
func (pkt *SvcDevLoginInfo) ReadFrom(r *JceReader) { func (pkt *SvcDevLoginInfo) ReadFrom(r *JceReader) {
pkt.AppId = r.ReadInt64(0) pkt.AppId = r.ReadInt64(0)
pkt.Guid = []byte{} pkt.Guid = r.ReadBytes(1)
r.ReadSlice(&pkt.Guid, 1)
pkt.LoginTime = r.ReadInt64(2) pkt.LoginTime = r.ReadInt64(2)
pkt.LoginPlatform = r.ReadInt64(3) pkt.LoginPlatform = r.ReadInt64(3)
pkt.LoginLocation = r.ReadString(4) pkt.LoginLocation = r.ReadString(4)
@ -763,7 +691,6 @@ func (pkt *SvcDevLoginInfo) ReadFrom(r *JceReader) {
} }
func (pkt *SvcRespParam) ReadFrom(r *JceReader) { func (pkt *SvcRespParam) ReadFrom(r *JceReader) {
pkt.OnlineInfos = []OnlineInfo{}
pkt.PCStat = r.ReadInt32(0) pkt.PCStat = r.ReadInt32(0)
pkt.IsSupportC2CRoamMsg = r.ReadInt32(1) pkt.IsSupportC2CRoamMsg = r.ReadInt32(1)
pkt.IsSupportDataLine = r.ReadInt32(2) pkt.IsSupportDataLine = r.ReadInt32(2)
@ -771,7 +698,7 @@ func (pkt *SvcRespParam) ReadFrom(r *JceReader) {
pkt.IsSupportViewPCFile = r.ReadInt32(4) pkt.IsSupportViewPCFile = r.ReadInt32(4)
pkt.PcVersion = r.ReadInt32(5) pkt.PcVersion = r.ReadInt32(5)
pkt.RoamFlag = r.ReadInt64(6) pkt.RoamFlag = r.ReadInt64(6)
r.ReadSlice(&pkt.OnlineInfos, 7) pkt.OnlineInfos = r.ReadOnlineInfos(7)
pkt.PCClientType = r.ReadInt32(8) pkt.PCClientType = r.ReadInt32(8)
} }
@ -797,7 +724,6 @@ func (pkt *OnlineInfo) ReadFrom(r *JceReader) {
} }
func (pkt *SvcReqMSFLoginNotify) ReadFrom(r *JceReader) { func (pkt *SvcReqMSFLoginNotify) ReadFrom(r *JceReader) {
pkt.InstanceList = []InstanceInfo{}
pkt.AppId = r.ReadInt64(0) pkt.AppId = r.ReadInt64(0)
pkt.Status = r.ReadByte(1) pkt.Status = r.ReadByte(1)
pkt.Tablet = r.ReadByte(2) pkt.Tablet = r.ReadByte(2)
@ -806,7 +732,7 @@ func (pkt *SvcReqMSFLoginNotify) ReadFrom(r *JceReader) {
pkt.Info = r.ReadString(5) pkt.Info = r.ReadString(5)
pkt.ProductType = r.ReadInt64(6) pkt.ProductType = r.ReadInt64(6)
pkt.ClientType = r.ReadInt64(7) pkt.ClientType = r.ReadInt64(7)
r.ReadSlice(&pkt.InstanceList, 8) pkt.InstanceList = r.ReadInstanceInfos(8)
} }
func (pkt *InstanceInfo) ReadFrom(r *JceReader) { func (pkt *InstanceInfo) ReadFrom(r *JceReader) {
@ -817,32 +743,8 @@ func (pkt *InstanceInfo) ReadFrom(r *JceReader) {
pkt.ClientType = r.ReadInt64(4) pkt.ClientType = r.ReadInt64(4)
} }
func (pkt *SvcRespPushMsg) ToBytes() []byte { func (pkt *VipInfo) ReadFrom(r *JceReader) {
w := NewJceWriter() pkt.Open = r.ReadByte(0)
w.WriteJceStructRaw(pkt) pkt.Type = r.ReadInt32(1)
return w.Bytes() pkt.Level = r.ReadInt32(2)
}
func (pkt *ModifyGroupCardRequest) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *SvcReqGetDevLoginInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *SvcReqRegisterNew) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
}
func (pkt *DelFriendReq) ToBytes() []byte {
w := NewJceWriter()
w.WriteJceStructRaw(pkt)
return w.Bytes()
} }

View File

@ -0,0 +1,760 @@
// Code generated by internal/generator/jce_gen; DO NOT EDIT.
package jce
func (pkt *RequestPacket) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt16(pkt.IVersion, 1)
w.WriteByte(pkt.CPacketType, 2)
w.WriteInt32(pkt.IMessageType, 3)
w.WriteInt32(pkt.IRequestId, 4)
w.WriteString(pkt.SServantName, 5)
w.WriteString(pkt.SFuncName, 6)
w.WriteBytes(pkt.SBuffer, 7)
w.WriteInt32(pkt.ITimeout, 8)
w.writeMapStrStr(pkt.Context, 9)
w.writeMapStrStr(pkt.Status, 10)
return w.Bytes()
}
func (pkt *RequestDataVersion3) ToBytes() []byte {
w := NewJceWriter()
w.writeMapStrBytes(pkt.Map, 0)
return w.Bytes()
}
func (pkt *RequestDataVersion2) ToBytes() []byte {
w := NewJceWriter()
w.writeMapStrMapStrBytes(pkt.Map, 0)
return w.Bytes()
}
func (pkt *SsoServerInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteString(pkt.Server, 1)
w.WriteInt32(pkt.Port, 2)
w.WriteString(pkt.Location, 8)
return w.Bytes()
}
func (pkt *FileStoragePushFSSvcList) ToBytes() []byte {
w := NewJceWriter()
{ // write pkt.UploadList tag=0
w.writeHead(9, 0)
if len(pkt.UploadList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.UploadList)), 0)
for _, i := range pkt.UploadList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
{ // write pkt.PicDownloadList tag=1
w.writeHead(9, 1)
if len(pkt.PicDownloadList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.PicDownloadList)), 0)
for _, i := range pkt.PicDownloadList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
{ // write pkt.GPicDownloadList tag=2
w.writeHead(9, 2)
if len(pkt.GPicDownloadList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.GPicDownloadList)), 0)
for _, i := range pkt.GPicDownloadList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
{ // write pkt.QZoneProxyServiceList tag=3
w.writeHead(9, 3)
if len(pkt.QZoneProxyServiceList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.QZoneProxyServiceList)), 0)
for _, i := range pkt.QZoneProxyServiceList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
{ // write pkt.UrlEncodeServiceList tag=4
w.writeHead(9, 4)
if len(pkt.UrlEncodeServiceList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.UrlEncodeServiceList)), 0)
for _, i := range pkt.UrlEncodeServiceList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
{ // write BigDataChannel tag=5
w.writeHead(10, 5)
w.buf.Write(pkt.BigDataChannel.ToBytes())
w.writeHead(11, 0)
}
{ // write pkt.VipEmotionList tag=6
w.writeHead(9, 6)
if len(pkt.VipEmotionList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.VipEmotionList)), 0)
for _, i := range pkt.VipEmotionList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
{ // write pkt.C2CPicDownList tag=7
w.writeHead(9, 7)
if len(pkt.C2CPicDownList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.C2CPicDownList)), 0)
for _, i := range pkt.C2CPicDownList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
w.WriteBytes(pkt.PttList, 10)
return w.Bytes()
}
func (pkt *FileStorageServerInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteString(pkt.Server, 1)
w.WriteInt32(pkt.Port, 2)
return w.Bytes()
}
func (pkt *BigDataChannel) ToBytes() []byte {
w := NewJceWriter()
{ // write pkt.IPLists tag=0
w.writeHead(9, 0)
if len(pkt.IPLists) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.IPLists)), 0)
for _, i := range pkt.IPLists {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
w.WriteBytes(pkt.SigSession, 1)
w.WriteBytes(pkt.KeySession, 2)
w.WriteInt64(pkt.SigUin, 3)
w.WriteInt32(pkt.ConnectFlag, 4)
w.WriteBytes(pkt.PbBuf, 5)
return w.Bytes()
}
func (pkt *BigDataIPList) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.ServiceType, 0)
{ // write pkt.IPList tag=1
w.writeHead(9, 1)
if len(pkt.IPList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.IPList)), 0)
for _, i := range pkt.IPList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
w.WriteInt64(pkt.FragmentSize, 3)
return w.Bytes()
}
func (pkt *BigDataIPInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Type, 0)
w.WriteString(pkt.Server, 1)
w.WriteInt64(pkt.Port, 2)
return w.Bytes()
}
func (pkt *SvcReqRegister) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteInt64(pkt.Bid, 1)
w.WriteByte(pkt.ConnType, 2)
w.WriteString(pkt.Other, 3)
w.WriteInt32(pkt.Status, 4)
w.WriteByte(pkt.OnlinePush, 5)
w.WriteByte(pkt.IsOnline, 6)
w.WriteByte(pkt.IsShowOnline, 7)
w.WriteByte(pkt.KickPC, 8)
w.WriteByte(pkt.KickWeak, 9)
w.WriteInt64(pkt.Timestamp, 10)
w.WriteInt64(pkt.IOSVersion, 11)
w.WriteByte(pkt.NetType, 12)
w.WriteString(pkt.BuildVer, 13)
w.WriteByte(pkt.RegType, 14)
w.WriteBytes(pkt.DevParam, 15)
w.WriteBytes(pkt.Guid, 16)
w.WriteInt32(pkt.LocaleId, 17)
w.WriteByte(pkt.SilentPush, 18)
w.WriteString(pkt.DevName, 19)
w.WriteString(pkt.DevType, 20)
w.WriteString(pkt.OSVer, 21)
w.WriteByte(pkt.OpenPush, 22)
w.WriteInt64(pkt.LargeSeq, 23)
w.WriteInt64(pkt.LastWatchStartTime, 24)
w.WriteInt64(pkt.OldSSOIp, 26)
w.WriteInt64(pkt.NewSSOIp, 27)
w.WriteString(pkt.ChannelNo, 28)
w.WriteInt64(pkt.CPID, 29)
w.WriteString(pkt.VendorName, 30)
w.WriteString(pkt.VendorOSName, 31)
w.WriteString(pkt.IOSIdfa, 32)
w.WriteBytes(pkt.B769, 33)
w.WriteByte(pkt.IsSetStatus, 34)
w.WriteBytes(pkt.ServerBuf, 35)
w.WriteByte(pkt.SetMute, 36)
w.WriteInt64(pkt.ExtOnlineStatus, 38)
w.WriteInt32(pkt.BatteryStatus, 39)
return w.Bytes()
}
func (pkt *SvcRespRegister) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteInt64(pkt.Bid, 1)
w.WriteByte(pkt.ReplyCode, 2)
w.WriteString(pkt.Result, 3)
w.WriteInt64(pkt.ServerTime, 4)
w.WriteByte(pkt.LogQQ, 5)
w.WriteByte(pkt.NeedKik, 6)
w.WriteByte(pkt.UpdateFlag, 7)
w.WriteInt64(pkt.Timestamp, 8)
w.WriteByte(pkt.CrashFlag, 9)
w.WriteString(pkt.ClientIp, 10)
w.WriteInt32(pkt.ClientPort, 11)
w.WriteInt32(pkt.HelloInterval, 12)
w.WriteInt32(pkt.LargeSeq, 13)
w.WriteByte(pkt.LargeSeqUpdate, 14)
w.WriteBytes(pkt.D769RspBody, 15)
w.WriteInt32(pkt.Status, 16)
w.WriteInt64(pkt.ExtOnlineStatus, 17)
w.WriteInt64(pkt.ClientBatteryGetInterval, 18)
w.WriteInt64(pkt.ClientAutoStatusInterval, 19)
return w.Bytes()
}
func (pkt *SvcReqRegisterNew) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.RequestOptional, 0)
{ // write C2CMsg tag=1
w.writeHead(10, 1)
w.buf.Write(pkt.C2CMsg.ToBytes())
w.writeHead(11, 0)
}
{ // write GroupMsg tag=2
w.writeHead(10, 2)
w.buf.Write(pkt.GroupMsg.ToBytes())
w.writeHead(11, 0)
}
w.WriteByte(pkt.DisGroupMsgFilter, 14)
w.WriteByte(pkt.GroupMask, 15)
w.WriteInt64(pkt.EndSeq, 16)
w.WriteBytes(pkt.O769Body, 20)
return w.Bytes()
}
func (pkt *SvcReqGetMsgV2) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteInt32(pkt.DateTime, 1)
w.WriteByte(pkt.RecivePic, 4)
w.WriteInt16(pkt.Ability, 6)
w.WriteByte(pkt.Channel, 9)
w.WriteByte(pkt.Inst, 16)
w.WriteByte(pkt.ChannelEx, 17)
w.WriteBytes(pkt.SyncCookie, 18)
w.WriteInt64(int64(pkt.SyncFlag), 19)
w.WriteByte(pkt.RambleFlag, 20)
w.WriteInt64(pkt.GeneralAbi, 26)
w.WriteBytes(pkt.PubAccountCookie, 27)
return w.Bytes()
}
func (pkt *SvcReqPullGroupMsgSeq) ToBytes() []byte {
w := NewJceWriter()
{ // write pkt.GroupInfo tag=0
w.writeHead(9, 0)
if len(pkt.GroupInfo) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.GroupInfo)), 0)
for _, i := range pkt.GroupInfo {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
w.WriteByte(pkt.VerifyType, 1)
w.WriteInt32(pkt.Filter, 2)
return w.Bytes()
}
func (pkt *PullGroupSeqParam) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.GroupCode, 0)
w.WriteInt64(pkt.LastSeqId, 1)
return w.Bytes()
}
func (pkt *SvcRespParam) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt32(pkt.PCStat, 0)
w.WriteInt32(pkt.IsSupportC2CRoamMsg, 1)
w.WriteInt32(pkt.IsSupportDataLine, 2)
w.WriteInt32(pkt.IsSupportPrintable, 3)
w.WriteInt32(pkt.IsSupportViewPCFile, 4)
w.WriteInt32(pkt.PcVersion, 5)
w.WriteInt64(pkt.RoamFlag, 6)
{ // write pkt.OnlineInfos tag=7
w.writeHead(9, 7)
if len(pkt.OnlineInfos) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.OnlineInfos)), 0)
for _, i := range pkt.OnlineInfos {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
w.WriteInt32(pkt.PCClientType, 8)
return w.Bytes()
}
func (pkt *RequestPushNotify) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteByte(pkt.Type, 1)
w.WriteString(pkt.Service, 2)
w.WriteString(pkt.Cmd, 3)
w.WriteBytes(pkt.NotifyCookie, 4)
w.WriteInt32(pkt.MsgType, 5)
w.WriteInt32(pkt.UserActive, 6)
w.WriteInt32(pkt.GeneralFlag, 7)
w.WriteInt64(pkt.BindedUin, 8)
return w.Bytes()
}
func (pkt *OnlineInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt32(pkt.InstanceId, 0)
w.WriteInt32(pkt.ClientType, 1)
w.WriteInt32(pkt.OnlineStatus, 2)
w.WriteInt32(pkt.PlatformId, 3)
w.WriteString(pkt.SubPlatform, 4)
w.WriteInt64(pkt.UClientType, 5)
return w.Bytes()
}
func (pkt *SvcReqMSFLoginNotify) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.AppId, 0)
w.WriteByte(pkt.Status, 1)
w.WriteByte(pkt.Tablet, 2)
w.WriteInt64(pkt.Platform, 3)
w.WriteString(pkt.Title, 4)
w.WriteString(pkt.Info, 5)
w.WriteInt64(pkt.ProductType, 6)
w.WriteInt64(pkt.ClientType, 7)
{ // write pkt.InstanceList tag=8
w.writeHead(9, 8)
if len(pkt.InstanceList) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.InstanceList)), 0)
for _, i := range pkt.InstanceList {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
return w.Bytes()
}
func (pkt *InstanceInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt32(pkt.AppId, 0)
w.WriteByte(pkt.Tablet, 1)
w.WriteInt64(pkt.Platform, 2)
w.WriteInt64(pkt.ProductType, 3)
w.WriteInt64(pkt.ClientType, 4)
return w.Bytes()
}
func (pkt *PushMessageInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.FromUin, 0)
w.WriteInt64(pkt.MsgTime, 1)
w.WriteInt16(pkt.MsgType, 2)
w.WriteInt16(pkt.MsgSeq, 3)
w.WriteString(pkt.Msg, 4)
w.WriteInt32(pkt.RealMsgTime, 5)
w.WriteBytes(pkt.VMsg, 6)
w.WriteInt64(pkt.AppShareID, 7)
w.WriteBytes(pkt.MsgCookies, 8)
w.WriteBytes(pkt.AppShareCookie, 9)
w.WriteInt64(pkt.MsgUid, 10)
w.WriteInt64(pkt.LastChangeTime, 11)
w.WriteInt64(pkt.FromInstId, 14)
w.WriteBytes(pkt.RemarkOfSender, 15)
w.WriteString(pkt.FromMobile, 16)
w.WriteString(pkt.FromName, 17)
return w.Bytes()
}
func (pkt *SvcRespPushMsg) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
{ // write pkt.DelInfos tag=1
w.writeHead(9, 1)
if len(pkt.DelInfos) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.DelInfos)), 0)
for _, i := range pkt.DelInfos {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
w.WriteInt32(pkt.Svrip, 2)
w.WriteBytes(pkt.PushToken, 3)
w.WriteInt32(pkt.ServiceType, 4)
return w.Bytes()
}
func (pkt *SvcReqGetDevLoginInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteBytes(pkt.Guid, 0)
w.WriteString(pkt.AppName, 1)
w.WriteInt64(pkt.LoginType, 2)
w.WriteInt64(pkt.Timestamp, 3)
w.WriteInt64(pkt.NextItemIndex, 4)
w.WriteInt64(pkt.RequireMax, 5)
w.WriteInt64(pkt.GetDevListType, 6)
return w.Bytes()
}
func (pkt *DelMsgInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.FromUin, 0)
w.WriteInt64(pkt.MsgTime, 1)
w.WriteInt16(pkt.MsgSeq, 2)
w.WriteBytes(pkt.MsgCookies, 3)
w.WriteInt16(pkt.Cmd, 4)
w.WriteInt64(pkt.MsgType, 5)
w.WriteInt64(pkt.AppId, 6)
w.WriteInt64(pkt.SendTime, 7)
w.WriteInt32(pkt.SsoSeq, 8)
w.WriteInt32(pkt.SsoIp, 9)
w.WriteInt32(pkt.ClientIp, 10)
return w.Bytes()
}
func (pkt *FriendListRequest) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt32(pkt.Reqtype, 0)
w.WriteByte(pkt.IfReflush, 1)
w.WriteInt64(pkt.Uin, 2)
w.WriteInt16(pkt.StartIndex, 3)
w.WriteInt16(pkt.FriendCount, 4)
w.WriteByte(pkt.GroupId, 5)
w.WriteByte(pkt.IfGetGroupInfo, 6)
w.WriteByte(pkt.GroupStartIndex, 7)
w.WriteByte(pkt.GroupCount, 8)
w.WriteByte(pkt.IfGetMSFGroup, 9)
w.WriteByte(pkt.IfShowTermType, 10)
w.WriteInt64(pkt.Version, 11)
w.WriteInt64Slice(pkt.UinList, 12)
w.WriteInt32(pkt.AppType, 13)
w.WriteByte(pkt.IfGetDOVId, 14)
w.WriteByte(pkt.IfGetBothFlag, 15)
w.WriteBytes(pkt.D50, 16)
w.WriteBytes(pkt.D6B, 17)
w.WriteInt64Slice(pkt.SnsTypeList, 18)
return w.Bytes()
}
func (pkt *FriendInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.FriendUin, 0)
w.WriteByte(pkt.GroupId, 1)
w.WriteInt16(pkt.FaceId, 2)
w.WriteString(pkt.Remark, 3)
w.WriteByte(pkt.QQType, 4)
w.WriteByte(pkt.Status, 5)
w.WriteByte(pkt.MemberLevel, 6)
w.WriteByte(pkt.IsMqqOnLine, 7)
w.WriteByte(pkt.QQOnlineState, 8)
w.WriteByte(pkt.IsIphoneOnline, 9)
w.WriteByte(pkt.DetailStatusFlag, 10)
w.WriteByte(pkt.QQOnlineStateV2, 11)
w.WriteString(pkt.ShowName, 12)
w.WriteByte(pkt.IsRemark, 13)
w.WriteString(pkt.Nick, 14)
w.WriteByte(pkt.SpecialFlag, 15)
w.WriteBytes(pkt.IMGroupID, 16)
w.WriteBytes(pkt.MSFGroupID, 17)
w.WriteInt32(pkt.TermType, 18)
w.WriteByte(pkt.Network, 20)
w.WriteBytes(pkt.Ring, 21)
w.WriteInt64(pkt.AbiFlag, 22)
w.WriteInt64(pkt.FaceAddonId, 23)
w.WriteInt32(pkt.NetworkType, 24)
w.WriteInt64(pkt.VipFont, 25)
w.WriteInt32(pkt.IconType, 26)
w.WriteString(pkt.TermDesc, 27)
w.WriteInt64(pkt.ColorRing, 28)
w.WriteByte(pkt.ApolloFlag, 29)
w.WriteInt64(pkt.ApolloTimestamp, 30)
w.WriteByte(pkt.Sex, 31)
w.WriteInt64(pkt.FounderFont, 32)
w.WriteString(pkt.EimId, 33)
w.WriteString(pkt.EimMobile, 34)
w.WriteByte(pkt.OlympicTorch, 35)
w.WriteInt64(pkt.ApolloSignTime, 36)
w.WriteInt64(pkt.LaviUin, 37)
w.WriteInt64(pkt.TagUpdateTime, 38)
w.WriteInt64(pkt.GameLastLoginTime, 39)
w.WriteInt64(pkt.GameAppId, 40)
w.WriteBytes(pkt.CardID, 41)
w.WriteInt64(pkt.BitSet, 42)
w.WriteByte(pkt.KingOfGloryFlag, 43)
w.WriteInt64(pkt.KingOfGloryRank, 44)
w.WriteString(pkt.MasterUin, 45)
w.WriteInt64(pkt.LastMedalUpdateTime, 46)
w.WriteInt64(pkt.FaceStoreId, 47)
w.WriteInt64(pkt.FontEffect, 48)
w.WriteString(pkt.DOVId, 49)
w.WriteInt64(pkt.BothFlag, 50)
w.WriteByte(pkt.CentiShow3DFlag, 51)
w.WriteBytes(pkt.IntimateInfo, 52)
w.WriteByte(pkt.ShowNameplate, 53)
w.WriteByte(pkt.NewLoverDiamondFlag, 54)
w.WriteBytes(pkt.ExtSnsFrdData, 55)
w.WriteBytes(pkt.MutualMarkData, 56)
return w.Bytes()
}
func (pkt *TroopListRequest) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteByte(pkt.GetMSFMsgFlag, 1)
w.WriteBytes(pkt.Cookies, 2)
w.WriteInt64Slice(pkt.GroupInfo, 3)
w.WriteByte(pkt.GroupFlagExt, 4)
w.WriteInt32(pkt.Version, 5)
w.WriteInt64(pkt.CompanyId, 6)
w.WriteInt64(pkt.VersionNum, 7)
w.WriteByte(pkt.GetLongGroupName, 8)
return w.Bytes()
}
func (pkt *TroopNumber) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.GroupUin, 0)
w.WriteInt64(pkt.GroupCode, 1)
w.WriteByte(pkt.Flag, 2)
w.WriteInt64(pkt.GroupInfoSeq, 3)
w.WriteString(pkt.GroupName, 4)
w.WriteString(pkt.GroupMemo, 5)
w.WriteInt64(pkt.GroupFlagExt, 6)
w.WriteInt64(pkt.GroupRankSeq, 7)
w.WriteInt64(pkt.CertificationType, 8)
w.WriteInt64(pkt.ShutUpTimestamp, 9)
w.WriteInt64(pkt.MyShutUpTimestamp, 10)
w.WriteInt64(pkt.CmdUinUinFlag, 11)
w.WriteInt64(pkt.AdditionalFlag, 12)
w.WriteInt64(pkt.GroupTypeFlag, 13)
w.WriteInt64(pkt.GroupSecType, 14)
w.WriteInt64(pkt.GroupSecTypeInfo, 15)
w.WriteInt64(pkt.GroupClassExt, 16)
w.WriteInt64(pkt.AppPrivilegeFlag, 17)
w.WriteInt64(pkt.SubscriptionUin, 18)
w.WriteInt64(pkt.MemberNum, 19)
w.WriteInt64(pkt.MemberNumSeq, 20)
w.WriteInt64(pkt.MemberCardSeq, 21)
w.WriteInt64(pkt.GroupFlagExt3, 22)
w.WriteInt64(pkt.GroupOwnerUin, 23)
w.WriteByte(pkt.IsConfGroup, 24)
w.WriteByte(pkt.IsModifyConfGroupFace, 25)
w.WriteByte(pkt.IsModifyConfGroupName, 26)
w.WriteInt64(pkt.CmdUinJoinTime, 27)
w.WriteInt64(pkt.CompanyId, 28)
w.WriteInt64(pkt.MaxGroupMemberNum, 29)
w.WriteInt64(pkt.CmdUinGroupMask, 30)
w.WriteInt64(pkt.GuildAppId, 31)
w.WriteInt64(pkt.GuildSubType, 32)
w.WriteInt64(pkt.CmdUinRingtoneID, 33)
w.WriteInt64(pkt.CmdUinFlagEx2, 34)
return w.Bytes()
}
func (pkt *TroopMemberListRequest) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteInt64(pkt.GroupCode, 1)
w.WriteInt64(pkt.NextUin, 2)
w.WriteInt64(pkt.GroupUin, 3)
w.WriteInt64(pkt.Version, 4)
w.WriteInt64(pkt.ReqType, 5)
w.WriteInt64(pkt.GetListAppointTime, 6)
w.WriteByte(pkt.RichCardNameVer, 7)
return w.Bytes()
}
func (pkt *TroopMemberInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.MemberUin, 0)
w.WriteInt16(pkt.FaceId, 1)
w.WriteByte(pkt.Age, 2)
w.WriteByte(pkt.Gender, 3)
w.WriteString(pkt.Nick, 4)
w.WriteByte(pkt.Status, 5)
w.WriteString(pkt.ShowName, 6)
w.WriteString(pkt.Name, 8)
w.WriteString(pkt.Memo, 12)
w.WriteString(pkt.AutoRemark, 13)
w.WriteInt64(pkt.MemberLevel, 14)
w.WriteInt64(pkt.JoinTime, 15)
w.WriteInt64(pkt.LastSpeakTime, 16)
w.WriteInt64(pkt.CreditLevel, 17)
w.WriteInt64(pkt.Flag, 18)
w.WriteInt64(pkt.FlagExt, 19)
w.WriteInt64(pkt.Point, 20)
w.WriteByte(pkt.Concerned, 21)
w.WriteByte(pkt.Shielded, 22)
w.WriteString(pkt.SpecialTitle, 23)
w.WriteInt64(pkt.SpecialTitleExpireTime, 24)
w.WriteString(pkt.Job, 25)
w.WriteByte(pkt.ApolloFlag, 26)
w.WriteInt64(pkt.ApolloTimestamp, 27)
w.WriteInt64(pkt.GlobalGroupLevel, 28)
w.WriteInt64(pkt.TitleId, 29)
w.WriteInt64(pkt.ShutUpTimestap, 30)
w.WriteInt64(pkt.GlobalGroupPoint, 31)
w.WriteByte(pkt.RichCardNameVer, 33)
w.WriteInt64(pkt.VipType, 34)
w.WriteInt64(pkt.VipLevel, 35)
w.WriteInt64(pkt.BigClubLevel, 36)
w.WriteInt64(pkt.BigClubFlag, 37)
w.WriteInt64(pkt.Nameplate, 38)
w.WriteBytes(pkt.GroupHonor, 39)
return w.Bytes()
}
func (pkt *ModifyGroupCardRequest) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Zero, 0)
w.WriteInt64(pkt.GroupCode, 1)
w.WriteInt64(pkt.NewSeq, 2)
{ // write pkt.UinInfo tag=3
w.writeHead(9, 3)
if len(pkt.UinInfo) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.UinInfo)), 0)
for _, i := range pkt.UinInfo {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}
}
return w.Bytes()
}
func (pkt *UinInfo) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteInt64(pkt.Flag, 1)
w.WriteString(pkt.Name, 2)
w.WriteByte(pkt.Gender, 3)
w.WriteString(pkt.Phone, 4)
w.WriteString(pkt.Email, 5)
w.WriteString(pkt.Remark, 6)
return w.Bytes()
}
func (pkt *SummaryCardReq) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteInt32(pkt.ComeFrom, 1)
w.WriteInt64(pkt.QzoneFeedTimestamp, 2)
w.WriteByte(pkt.IsFriend, 3)
w.WriteInt64(pkt.GroupCode, 4)
w.WriteInt64(pkt.GroupUin, 5)
w.WriteInt64(pkt.GetControl, 8)
w.WriteInt32(pkt.AddFriendSource, 9)
w.WriteBytes(pkt.SecureSig, 10)
w.WriteBytesSlice(pkt.ReqServices, 14)
w.WriteInt64(pkt.TinyId, 15)
w.WriteInt64(pkt.LikeSource, 16)
w.WriteByte(pkt.ReqMedalWallInfo, 18)
w.WriteInt64Slice(pkt.Req0x5ebFieldId, 19)
w.WriteByte(pkt.ReqNearbyGodInfo, 20)
w.WriteByte(pkt.ReqExtendCard, 22)
return w.Bytes()
}
func (pkt *SummaryCardReqSearch) ToBytes() []byte {
w := NewJceWriter()
w.WriteString(pkt.Keyword, 0)
w.WriteString(pkt.CountryCode, 1)
w.WriteInt32(pkt.Version, 2)
w.WriteBytesSlice(pkt.ReqServices, 3)
return w.Bytes()
}
func (pkt *DelFriendReq) ToBytes() []byte {
w := NewJceWriter()
w.WriteInt64(pkt.Uin, 0)
w.WriteInt64(pkt.DelUin, 1)
w.WriteByte(pkt.DelType, 2)
w.WriteInt32(pkt.Version, 3)
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

@ -3,9 +3,7 @@ package jce
import ( import (
"bytes" "bytes"
goBinary "encoding/binary" goBinary "encoding/binary"
"reflect" "math"
"strconv"
"sync"
) )
type JceWriter struct { type JceWriter struct {
@ -16,18 +14,16 @@ func NewJceWriter() *JceWriter {
return &JceWriter{buf: new(bytes.Buffer)} return &JceWriter{buf: new(bytes.Buffer)}
} }
func (w *JceWriter) writeHead(t byte, tag int) { func (w *JceWriter) writeHead(t, tag byte) {
if tag < 15 { if tag < 0xF {
b := byte(tag<<4) | t w.buf.WriteByte(tag<<4 | t)
w.buf.WriteByte(b) } else {
} else if tag < 256 { w.buf.WriteByte(0xF0 | t)
b := 0xF0 | t w.buf.WriteByte(tag)
w.buf.WriteByte(b)
w.buf.WriteByte(byte(tag))
} }
} }
func (w *JceWriter) WriteByte(b byte, tag int) *JceWriter { func (w *JceWriter) WriteByte(b, tag byte) *JceWriter {
if b == 0 { if b == 0 {
w.writeHead(12, tag) w.writeHead(12, tag)
} else { } else {
@ -37,78 +33,116 @@ func (w *JceWriter) WriteByte(b byte, tag int) *JceWriter {
return w return w
} }
func (w *JceWriter) WriteBool(b bool, tag int) { func (w *JceWriter) WriteBool(b bool, tag byte) {
var by byte = 0 var by byte
if b { if b {
by = 1 by = 1
} }
w.WriteByte(by, tag) w.WriteByte(by, tag)
} }
func (w *JceWriter) WriteInt16(n int16, tag int) { func (w *JceWriter) WriteInt16(n int16, tag byte) {
if n >= -128 && n <= 127 { switch {
case n >= -128 && n <= 127:
w.WriteByte(byte(n), tag) w.WriteByte(byte(n), tag)
return default:
w.putInt16(n, tag)
} }
}
//go:nosplit
func (w *JceWriter) putInt16(n int16, tag byte) {
w.writeHead(1, tag) w.writeHead(1, tag)
_ = goBinary.Write(w.buf, goBinary.BigEndian, n) var buf [2]byte
goBinary.BigEndian.PutUint16(buf[:], uint16(n))
w.buf.Write(buf[:])
} }
func (w *JceWriter) WriteInt32(n int32, tag int) *JceWriter { func (w *JceWriter) WriteInt32(n int32, tag byte) *JceWriter {
if n >= -32768 && n <= 32767 { // ? if ((n >= 32768) && (n <= 32767)) switch {
w.WriteInt16(int16(n), tag) case n >= -128 && n <= 127:
return w w.WriteByte(byte(n), tag)
case n >= -32768 && n <= 32767:
w.putInt16(int16(n), tag)
default:
w.putInt32(n, tag)
} }
return w
}
//go:nosplit
func (w *JceWriter) putInt32(n int32, tag byte) {
w.writeHead(2, tag) w.writeHead(2, tag)
_ = goBinary.Write(w.buf, goBinary.BigEndian, n) var buf [4]byte
return w goBinary.BigEndian.PutUint32(buf[:], uint32(n))
w.buf.Write(buf[:])
} }
func (w *JceWriter) WriteInt64(n int64, tag int) *JceWriter { func (w *JceWriter) WriteInt64(n int64, tag byte) *JceWriter {
if n >= -2147483648 && n <= 2147483647 { switch {
return w.WriteInt32(int32(n), tag) case n >= -128 && n <= 127:
w.WriteByte(byte(n), tag)
case n >= -32768 && n <= 32767:
w.putInt16(int16(n), tag)
case n >= -2147483648 && n <= 2147483647:
w.putInt32(int32(n), tag)
default:
w.putInt64(n, tag)
} }
w.writeHead(3, tag)
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
return w return w
} }
func (w *JceWriter) WriteFloat32(n float32, tag int) { //go:nosplit
func (w *JceWriter) putInt64(n int64, tag byte) {
w.writeHead(3, tag)
var buf [8]byte
goBinary.BigEndian.PutUint64(buf[:], uint64(n))
w.buf.Write(buf[:])
}
//go:nosplit
func (w *JceWriter) WriteFloat32(n float32, tag byte) {
w.writeHead(4, tag) w.writeHead(4, tag)
_ = goBinary.Write(w.buf, goBinary.BigEndian, n) var buf [4]byte
goBinary.BigEndian.PutUint32(buf[:], math.Float32bits(n))
w.buf.Write(buf[:])
} }
func (w *JceWriter) WriteFloat64(n float64, tag int) { //go:nosplit
func (w *JceWriter) WriteFloat64(n float64, tag byte) {
w.writeHead(5, tag) w.writeHead(5, tag)
_ = goBinary.Write(w.buf, goBinary.BigEndian, n) var buf [8]byte
goBinary.BigEndian.PutUint64(buf[:], math.Float64bits(n))
w.buf.Write(buf[:])
} }
func (w *JceWriter) WriteString(s string, tag int) *JceWriter { func (w *JceWriter) WriteString(s string, tag byte) *JceWriter {
by := []byte(s) if len(s) > 255 {
if len(by) > 255 {
w.writeHead(7, tag) w.writeHead(7, tag)
_ = goBinary.Write(w.buf, goBinary.BigEndian, int32(len(by))) var buf [4]byte
w.buf.Write(by) goBinary.BigEndian.PutUint32(buf[:], uint32(len(s)))
w.buf.Write(buf[:])
w.buf.WriteString(s)
return w return w
} }
w.writeHead(6, tag) w.writeHead(6, tag)
w.buf.WriteByte(byte(len(by))) w.buf.WriteByte(byte(len(s)))
w.buf.Write(by) w.buf.WriteString(s)
return w return w
} }
func (w *JceWriter) WriteBytes(l []byte, tag int) *JceWriter { func (w *JceWriter) WriteBytes(l []byte, tag byte) *JceWriter {
w.writeHead(13, tag) w.writeHead(13, tag)
w.writeHead(0, 0) w.buf.WriteByte(0) // w.writeHead(0, 0)
w.WriteInt32(int32(len(l)), 0) w.WriteInt32(int32(len(l)), 0)
w.buf.Write(l) w.buf.Write(l)
return w return w
} }
func (w *JceWriter) WriteInt64Slice(l []int64, tag int) { func (w *JceWriter) WriteInt64Slice(l []int64, tag byte) {
w.writeHead(9, tag) w.writeHead(9, tag)
if len(l) == 0 { if len(l) == 0 {
w.WriteInt32(0, 0) w.writeHead(12, 0) // w.WriteInt32(0, 0)
return return
} }
w.WriteInt32(int32(len(l)), 0) w.WriteInt32(int32(len(l)), 0)
@ -117,143 +151,60 @@ func (w *JceWriter) WriteInt64Slice(l []int64, tag int) {
} }
} }
func (w *JceWriter) WriteSlice(i interface{}, tag int) { func (w *JceWriter) WriteBytesSlice(l [][]byte, tag byte) {
va := reflect.ValueOf(i)
if va.Kind() != reflect.Slice {
return
}
w.writeHead(9, tag)
if va.Len() == 0 {
w.WriteInt32(0, 0)
return
}
w.WriteInt32(int32(va.Len()), 0)
for i := 0; i < va.Len(); i++ {
v := va.Index(i)
w.WriteObject(v.Interface(), 0)
}
}
func (w *JceWriter) WriteJceStructSlice(l []IJceStruct, tag int) {
w.writeHead(9, tag) w.writeHead(9, tag)
if len(l) == 0 { if len(l) == 0 {
w.WriteInt32(0, 0) w.writeHead(12, 0) // w.WriteInt32(0, 0)
return return
} }
w.WriteInt32(int32(len(l)), 0) w.WriteInt32(int32(len(l)), 0)
for _, v := range l { for _, v := range l {
w.WriteJceStruct(v, 0) w.WriteBytes(v, 0)
} }
} }
func (w *JceWriter) WriteMap(m interface{}, tag int) { func (w *JceWriter) writeMapStrStr(m map[string]string, tag byte) {
if m == nil { if m == nil {
w.writeHead(8, tag) w.writeHead(8, tag)
w.WriteInt32(0, 0) w.writeHead(12, 0) // w.WriteInt32(0, 0)
return
}
va := reflect.ValueOf(m)
if va.Kind() != reflect.Map {
return return
} }
w.writeHead(8, tag) w.writeHead(8, tag)
w.WriteInt32(int32(va.Len()), 0) w.WriteInt32(int32(len(m)), 0)
iter := va.MapRange() for k, v := range m {
for iter.Next() { w.WriteString(k, 0)
w.WriteObject(iter.Key().Interface(), 0) w.WriteString(v, 1)
w.WriteObject(iter.Value().Interface(), 1)
} }
} }
func (w *JceWriter) WriteObject(i interface{}, tag int) { func (w *JceWriter) writeMapStrBytes(m map[string][]byte, tag byte) {
t := reflect.TypeOf(i) if m == nil {
if t.Kind() == reflect.Map { w.writeHead(8, tag)
w.WriteMap(i, tag) w.writeHead(12, 0) // w.WriteInt32(0, 0)
return return
} }
if t.Kind() == reflect.Slice { w.writeHead(8, tag)
if b, ok := i.([]byte); ok { w.WriteInt32(int32(len(m)), 0)
w.WriteBytes(b, tag) for k, v := range m {
return w.WriteString(k, 0)
} w.WriteBytes(v, 1)
w.WriteSlice(i, tag) }
}
func (w *JceWriter) writeMapStrMapStrBytes(m map[string]map[string][]byte, tag byte) {
if m == nil {
w.writeHead(8, tag)
w.writeHead(12, 0) // w.WriteInt32(0, 0)
return return
} }
switch o := i.(type) { w.writeHead(8, tag)
case byte: w.WriteInt32(int32(len(m)), 0)
w.WriteByte(o, tag) for k, v := range m {
case bool: w.WriteString(k, 0)
w.WriteBool(o, tag) w.writeMapStrBytes(v, 1)
case int16:
w.WriteInt16(o, tag)
case int32:
w.WriteInt32(o, tag)
case int64:
w.WriteInt64(o, tag)
case float32:
w.WriteFloat32(o, tag)
case float64:
w.WriteFloat64(o, tag)
case string:
w.WriteString(o, tag)
case IJceStruct:
w.WriteJceStruct(o, tag)
} }
} }
type decoder struct {
index int
id int
}
var decoderCache = sync.Map{}
// WriteJceStructRaw 写入 Jce 结构体
func (w *JceWriter) WriteJceStructRaw(s interface{}) {
t := reflect.TypeOf(s)
reflect.ValueOf(s).Interface()
if t.Kind() != reflect.Ptr {
return
}
t = t.Elem()
v := reflect.ValueOf(s).Elem()
var jceDec []decoder
dec, ok := decoderCache.Load(t)
if ok { // 从缓存中加载
jceDec = dec.([]decoder)
} else { // 初次反射
jceDec = make([]decoder, 0, t.NumField())
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
strId := field.Tag.Get("jceId")
if strId == "" {
continue
}
id, err := strconv.Atoi(strId)
if err != nil {
continue
}
jceDec = append(jceDec, decoder{
index: i,
id: id,
})
}
decoderCache.Store(t, jceDec) // 存入缓存
}
for _, dec := range jceDec {
obj := v.Field(dec.index).Interface()
if obj != nil {
w.WriteObject(obj, dec.id)
}
}
}
func (w *JceWriter) WriteJceStruct(s IJceStruct, tag int) {
w.writeHead(10, tag)
w.WriteJceStructRaw(s)
w.writeHead(11, 0)
}
func (w *JceWriter) Bytes() []byte { func (w *JceWriter) Bytes() []byte {
return w.buf.Bytes() return w.buf.Bytes()
} }

View File

@ -9,10 +9,10 @@ import (
var globalBytes []byte var globalBytes []byte
func BenchmarkJceWriter_WriteMap(b *testing.B) { func BenchmarkJceWriter_WriteMap(b *testing.B) {
var x = globalBytes x := globalBytes
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
w := NewJceWriter() w := NewJceWriter()
w.WriteMap(req.Map, 0) w.writeMapStrMapStrBytes(req.Map, 0)
x = w.Bytes() x = w.Bytes()
} }
globalBytes = x globalBytes = x
@ -39,20 +39,16 @@ var reqPacket1 = &RequestPacket{
} }
func BenchmarkJceWriter_WriteJceStructRaw(b *testing.B) { func BenchmarkJceWriter_WriteJceStructRaw(b *testing.B) {
var x = globalBytes x := globalBytes
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
w := NewJceWriter() _ = reqPacket1.ToBytes()
w.WriteJceStructRaw(reqPacket1)
x = w.Bytes()
} }
globalBytes = x globalBytes = x
b.SetBytes(int64(len(globalBytes))) b.SetBytes(int64(len(globalBytes)))
} }
func TestJceWriter_WriteJceStructRaw(t *testing.T) { func TestJceWriter_WriteJceStructRaw(t *testing.T) {
w := NewJceWriter() r := NewJceReader(reqPacket1.ToBytes())
w.WriteJceStructRaw(reqPacket1)
r := NewJceReader(w.Bytes())
var reqPacket2 RequestPacket var reqPacket2 RequestPacket
reqPacket2.ReadFrom(r) reqPacket2.ReadFrom(r)
assert.Equal(t, reqPacket1, &reqPacket2) assert.Equal(t, reqPacket1, &reqPacket2)

View File

@ -2,39 +2,37 @@ 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)
}, },
} }
// NewWriter 从池中取出一个 Writer // SelectWriter 从池中取出一个 Writer
func NewWriter() *Writer { func SelectWriter() *Writer {
w := bufferPool.Get().(*Writer) // 因为 bufferPool 定义有 New 函数
if w == nil { // 所以 bufferPool.Get() 永不为 nil
return new(Writer) // 不用判空
} return bufferPool.Get().(*Writer)
return w
} }
// 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 w.Cap() < maxSize { // 对于大Buffer直接丢弃 if (*bytes.Buffer)(w).Cap() < maxSize { // 对于大Buffer直接丢弃
w.Reset() w.Reset()
bufferPool.Put(w) bufferPool.Put(w)
} }
} }
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{
@ -66,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{
@ -91,38 +89,3 @@ func releaseZlibWriter(w *zlibWriter) {
zlibPool.Put(w) zlibPool.Put(w)
} }
} }
const size256k = 256 * 1024
var b256kPool = sync.Pool{
New: func() interface{} {
return make128kSlicePointer()
},
}
// Get256KBytes 获取一个128k大小 []byte
func Get256KBytes() *[]byte {
buf := b256kPool.Get().(*[]byte)
if buf == nil {
return make128kSlicePointer()
}
if cap(*buf) < size256k {
return make128kSlicePointer()
}
*buf = (*buf)[:size256k]
return buf
}
// Put256KBytes 放回一个128k大小 []byte
func Put256KBytes(b *[]byte) {
if cap(*b) < size256k || cap(*b) > 2*size256k { // 太大或太小的 []byte 不要放入
return
}
*b = (*b)[:cap(*b)]
b256kPool.Put(b)
}
func make128kSlicePointer() *[]byte {
data := make([]byte, size256k)
return &data
}

View File

@ -1,92 +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{}
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))
} }
@ -71,6 +72,10 @@ func (r *Reader) ReadString() string {
return utils.B2S(data) return utils.B2S(data)
} }
func (r *Reader) ReadInt32Bytes() []byte {
return r.ReadBytes(int(r.ReadInt32() - 4))
}
func (r *Reader) ReadStringShort() string { func (r *Reader) ReadStringShort() string {
data := r.ReadBytes(int(r.ReadUInt16())) data := r.ReadBytes(int(r.ReadUInt16()))
return utils.B2S(data) return utils.B2S(data)
@ -85,39 +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
if tagSize == 1 {
k = uint16(r.ReadByte())
} else if tagSize == 2 {
k = r.ReadUInt16()
} else if tagSize == 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 ---
@ -145,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

@ -2,158 +2,142 @@ package binary
import ( import (
"encoding/binary" "encoding/binary"
"math/rand" _ "unsafe" // required by go:linkname
"unsafe"
) )
func xorQ(a, b []byte, c []byte) { // MAGIC
*(*uint64)(unsafe.Pointer(&c[0])) =
*(*uint64)(unsafe.Pointer(&a[0])) ^ *(*uint64)(unsafe.Pointer(&b[0]))
}
type TEA [4]uint32 type TEA [4]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的解释
func (t *TEA) Encrypt(src []byte) (dst []byte) { func (t TEA) Encrypt(src []byte) (dst []byte) {
lens := len(src) lens := len(src)
fill := 10 - (lens+1)%8 fill := 10 - (lens+1)%8
tmp1 := make([]byte, 8) // 非纯src的数据
tmp2 := make([]byte, 8)
dst = make([]byte, fill+lens+7) dst = make([]byte, fill+lens+7)
// for i := 0; i < fill; i++ {
// dst[i] = ' '
// } // For test purpose
_, _ = rand.Read(dst[0:fill])
dst[0] = byte(fill-3) | 0xF8 // 存储pad长度 dst[0] = byte(fill-3) | 0xF8 // 存储pad长度
in := 0 // 位置 copy(dst[fill:], src)
// #1
if fill < 8 { var iv1, iv2, holder uint64
in = 8 - fill for i := 0; i < len(dst); i += 8 {
copy(dst[fill:8], src[:in]) block := binary.BigEndian.Uint64(dst[i:])
holder = block ^ iv1
iv1 = t.encode(holder)
iv1 ^= iv2
iv2 = holder
binary.BigEndian.PutUint64(dst[i:], iv1)
} }
copy(tmp2, dst[0:8])
t.encode(dst[0:8], dst[0:8])
out := 8 // 位置
// #2
if fill > 8 {
copy(dst[fill:out+8], src[:16-fill])
xorQ(dst[8:16], dst[0:8], dst[8:16]) // 与前一次结果xor
copy(tmp1, dst[8:16])
t.encode(dst[8:16], dst[8:16])
xorQ(dst[8:16], tmp2, dst[8:16]) // 与前一次数据xor
copy(tmp2, tmp1)
in = 16 - fill
out = 16
}
// #3+或#4+
lens -= 8
for in < lens {
xorQ(src[in:in+8], dst[out-8:out], dst[out:out+8]) // 与前一次结果xor
copy(tmp1, dst[out:out+8])
t.encode(dst[out:out+8], dst[out:out+8])
xorQ(dst[out:out+8], tmp2, dst[out:out+8]) // 与前一次数据xor
copy(tmp2, tmp1)
in += 8
out += 8
}
tmp3 := make([]byte, 8)
copy(tmp3, src[in:])
xorQ(tmp3, dst[out-8:out], dst[out:out+8]) // 与前一次结果xor
t.encode(dst[out:out+8], dst[out:out+8])
xorQ(dst[out:out+8], tmp2, dst[out:out+8]) // 与前一次数据xor
return dst return dst
} }
func (t *TEA) Decrypt(data []byte) []byte { func (t TEA) Decrypt(data []byte) []byte {
if len(data) < 16 || len(data)%8 != 0 { if len(data) < 16 || len(data)%8 != 0 {
return nil return nil
} }
dst := make([]byte, len(data)) dst := make([]byte, len(data))
copy(dst, data) var iv1, iv2, holder uint64
t.decode(dst[0:8], dst[0:8]) for i := 0; i < len(dst); i += 8 {
tmp := make([]byte, 8) iv1 = binary.BigEndian.Uint64(data[i:])
copy(tmp, dst[0:8]) iv2 ^= iv1
for in := 8; in < len(data); in += 8 { iv2 = t.decode(iv2)
xorQ(dst[in:in+8], tmp, dst[in:in+8]) binary.BigEndian.PutUint64(dst[i:], iv2^holder)
t.decode(dst[in:in+8], dst[in:in+8]) holder = iv1
xorQ(dst[in:in+8], data[in-8:in], dst[in:in+8])
xorQ(dst[in:in+8], data[in-8:in], tmp)
} }
return dst[dst[0]&7+3 : len(data)-7] return dst[dst[0]&7+3 : len(data)-7]
} }
//go:nosplit //go:nosplit
func unpack(data []byte) (v0, v1 uint32) { func (t *TEA) encode(n uint64) uint64 {
v1 = uint32(data[7]) | uint32(data[6])<<8 | uint32(data[5])<<16 | uint32(data[4])<<24 v0, v1 := uint32(n>>32), uint32(n)
v0 = uint32(data[3]) | uint32(data[2])<<8 | uint32(data[1])<<16 | uint32(data[0])<<24 t0, t1, t2, t3 := t[0], t[1], t[2], t[3]
return v0, v1
}
//go:nosplit v0 += (v1 + 0x9e3779b9) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
func repack(data []byte, v0, v1 uint32) { v1 += (v0 + 0x9e3779b9) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
_ = data[7] // early bounds check to guarantee safety of writes below v0 += (v1 + 0x3c6ef372) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
data[0] = byte(v0 >> 24) v1 += (v0 + 0x3c6ef372) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
data[1] = byte(v0 >> 16) v0 += (v1 + 0xdaa66d2b) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
data[2] = byte(v0 >> 8) v1 += (v0 + 0xdaa66d2b) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
data[3] = byte(v0) v0 += (v1 + 0x78dde6e4) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x78dde6e4) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x1715609d) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x1715609d) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xb54cda56) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xb54cda56) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x5384540f) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x5384540f) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xf1bbcdc8) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xf1bbcdc8) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x8ff34781) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x8ff34781) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x2e2ac13a) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x2e2ac13a) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xcc623af3) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xcc623af3) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x6a99b4ac) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x6a99b4ac) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x08d12e65) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x08d12e65) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xa708a81e) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xa708a81e) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0x454021d7) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0x454021d7) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 += (v1 + 0xe3779b90) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 += (v0 + 0xe3779b90) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
data[4] = byte(v1 >> 24) return uint64(v0)<<32 | uint64(v1)
data[5] = byte(v1 >> 16)
data[6] = byte(v1 >> 8)
data[7] = byte(v1)
}
var sumTable = [0x10]uint32{
0x9e3779b9,
0x3c6ef372,
0xdaa66d2b,
0x78dde6e4,
0x1715609d,
0xb54cda56,
0x5384540f,
0xf1bbcdc8,
0x8ff34781,
0x2e2ac13a,
0xcc623af3,
0x6a99b4ac,
0x08d12e65,
0xa708a81e,
0x454021d7,
0xe3779b90,
}
//go:nosplit
func (t *TEA) encode(src, dst []byte) {
v0, v1 := unpack(src)
for i := 0; i < 0x10; i++ {
v0 += ((v1 << 4) + t[0]) ^ (v1 + sumTable[i]) ^ ((v1 >> 5) + t[1])
v1 += ((v0 << 4) + t[2]) ^ (v0 + sumTable[i]) ^ ((v0 >> 5) + t[3])
}
repack(dst, v0, v1)
} }
// 每次8字节 // 每次8字节
//
//go:nosplit //go:nosplit
func (t *TEA) decode(src, dst []byte) { func (t *TEA) decode(n uint64) uint64 {
v0, v1 := unpack(src) v0, v1 := uint32(n>>32), uint32(n)
for i := 0xf; i >= 0; i-- { t0, t1, t2, t3 := t[0], t[1], t[2], t[3]
v1 -= ((v0 << 4) + t[2]) ^ (v0 + sumTable[i]) ^ ((v0 >> 5) + t[3])
v0 -= ((v1 << 4) + t[0]) ^ (v1 + sumTable[i]) ^ ((v1 >> 5) + t[1]) v1 -= (v0 + 0xe3779b90) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
} v0 -= (v1 + 0xe3779b90) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
repack(dst, v0, v1) v1 -= (v0 + 0x454021d7) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x454021d7) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xa708a81e) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xa708a81e) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x08d12e65) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x08d12e65) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x6a99b4ac) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x6a99b4ac) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xcc623af3) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xcc623af3) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x2e2ac13a) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x2e2ac13a) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x8ff34781) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x8ff34781) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xf1bbcdc8) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xf1bbcdc8) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x5384540f) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x5384540f) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xb54cda56) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xb54cda56) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x1715609d) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x1715609d) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x78dde6e4) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x78dde6e4) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0xdaa66d2b) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0xdaa66d2b) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x3c6ef372) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x3c6ef372) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
v1 -= (v0 + 0x9e3779b9) ^ (v0<<4 + t2) ^ (v0>>5 + t3)
v0 -= (v1 + 0x9e3779b9) ^ (v1<<4 + t0) ^ (v1>>5 + t1)
return uint64(v0)<<32 | uint64(v1)
} }
//go:nosplit //go:nosplit
func NewTeaCipher(key []byte) *TEA { func NewTeaCipher(key []byte) (t TEA) {
if len(key) != 16 { if len(key) != 16 {
return nil return TEA{}
} }
t := new(TEA) t[3] = binary.BigEndian.Uint32(key[12:16])
t[3] = binary.BigEndian.Uint32(key[12:]) t[2] = binary.BigEndian.Uint32(key[8:12])
t[2] = binary.BigEndian.Uint32(key[8:]) t[1] = binary.BigEndian.Uint32(key[4:8])
t[1] = binary.BigEndian.Uint32(key[4:]) t[0] = binary.BigEndian.Uint32(key[:4])
t[0] = binary.BigEndian.Uint32(key[0:])
return t return t
} }

View File

@ -110,7 +110,11 @@ func BenchmarkTEAen(b *testing.B) {
benchEncrypt(b, data) benchEncrypt(b, data)
}) })
b.Run("4K", func(b *testing.B) { b.Run("4K", func(b *testing.B) {
data := make([]byte, 4096) data := make([]byte, 1024*4)
benchEncrypt(b, data)
})
b.Run("32K", func(b *testing.B) {
data := make([]byte, 1024*32)
benchEncrypt(b, data) benchEncrypt(b, data)
}) })
} }
@ -128,4 +132,20 @@ func BenchmarkTEAde(b *testing.B) {
data := make([]byte, 4096) data := make([]byte, 4096)
benchDecrypt(b, data) benchDecrypt(b, data)
}) })
b.Run("32K", func(b *testing.B) {
data := make([]byte, 1024*32)
benchDecrypt(b, data)
})
}
func BenchmarkTEA_encode(b *testing.B) {
for i := 0; i < b.N; i++ {
testTEA.encode(114514)
}
}
func BenchmarkTEA_decode(b *testing.B) {
for i := 0; i < b.N; i++ {
testTEA.decode(114514)
}
} }

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

@ -4,21 +4,49 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"github.com/Mrs4s/MiraiGo/utils"
) )
// Writer 写入 // Writer 写入
type Writer bytes.Buffer type Writer bytes.Buffer
func NewWriterF(f func(writer *Writer)) []byte { func NewWriterF(f func(writer *Writer)) []byte {
w := NewWriter() w := SelectWriter()
f(w) f(w)
b := append([]byte(nil), w.Bytes()...) b := make([]byte, len(w.Bytes()))
PutWriter(w) copy(b, w.Bytes())
w.put()
return b return b
} }
// OpenWriterF must call func cl to close
func OpenWriterF(f func(*Writer)) (b []byte, cl func()) {
w := SelectWriter()
f(w)
return w.Bytes(), w.put
}
func (w *Writer) FillUInt16() (pos int) {
pos = w.Len()
(*bytes.Buffer)(w).Write([]byte{0, 0})
return
}
func (w *Writer) WriteUInt16At(pos int, v uint16) {
newdata := (*bytes.Buffer)(w).Bytes()[pos:]
binary.BigEndian.PutUint16(newdata, v)
}
func (w *Writer) FillUInt32() (pos int) {
pos = w.Len()
(*bytes.Buffer)(w).Write([]byte{0, 0, 0, 0})
return
}
func (w *Writer) WriteUInt32At(pos int, v uint32) {
newdata := (*bytes.Buffer)(w).Bytes()[pos:]
binary.BigEndian.PutUint32(newdata, v)
}
func (w *Writer) Write(b []byte) { func (w *Writer) Write(b []byte) {
(*bytes.Buffer)(w).Write(b) (*bytes.Buffer)(w).Write(b)
} }
@ -51,13 +79,13 @@ func (w *Writer) WriteUInt64(v uint64) {
} }
func (w *Writer) WriteString(v string) { func (w *Writer) WriteString(v string) {
payload := utils.S2B(v) w.WriteUInt32(uint32(len(v) + 4))
w.WriteUInt32(uint32(len(payload) + 4)) (*bytes.Buffer)(w).WriteString(v)
w.Write(payload)
} }
func (w *Writer) WriteStringShort(v string) { func (w *Writer) WriteStringShort(v string) {
w.WriteBytesShort(utils.S2B(v)) w.WriteUInt16(uint16(len(v)))
(*bytes.Buffer)(w).WriteString(v)
} }
func (w *Writer) WriteBool(b bool) { func (w *Writer) WriteBool(b bool) {
@ -69,36 +97,13 @@ func (w *Writer) WriteBool(b bool) {
} }
func (w *Writer) EncryptAndWrite(key []byte, data []byte) { func (w *Writer) EncryptAndWrite(key []byte, data []byte) {
tea := NewTeaCipher(key) w.Write(NewTeaCipher(key).Encrypt(data))
ed := tea.Encrypt(data)
w.Write(ed)
} }
func (w *Writer) WriteIntLvPacket(offset int, f func(writer *Writer)) { func (w *Writer) WriteIntLvPacket(offset int, f func(*Writer)) {
data := NewWriterF(f) pos := w.FillUInt32()
w.WriteUInt32(uint32(len(data) + offset)) f(w)
w.Write(data) w.WriteUInt32At(pos, uint32(w.Len()+offset-pos-4))
}
func (w *Writer) WriteUniPacket(commandName string, sessionId, extraData, body []byte) {
w1 := NewWriter()
{ // WriteIntLvPacket
w1.WriteString(commandName)
w1.WriteUInt32(8)
w1.Write(sessionId)
if len(extraData) == 0 {
w1.WriteUInt32(0x04)
} else {
w1.WriteUInt32(uint32(len(extraData) + 4))
w1.Write(extraData)
}
}
data := w1.Bytes()
w.WriteUInt32(uint32(len(data) + 4))
w.Write(data)
PutWriter(w1)
w.WriteUInt32(uint32(len(body) + 4)) // WriteIntLvPacket
w.Write(body)
} }
func (w *Writer) WriteBytesShort(data []byte) { func (w *Writer) WriteBytesShort(data []byte) {
@ -114,12 +119,12 @@ func (w *Writer) WriteTlvLimitedSize(data []byte, limit int) {
w.WriteBytesShort(data[:limit]) w.WriteBytesShort(data[:limit])
} }
func (w *Writer) Bytes() []byte { func (w *Writer) Len() int {
return (*bytes.Buffer)(w).Bytes() return (*bytes.Buffer)(w).Len()
} }
func (w *Writer) Cap() int { func (w *Writer) Bytes() []byte {
return (*bytes.Buffer)(w).Cap() return (*bytes.Buffer)(w).Bytes()
} }
func (w *Writer) Reset() { func (w *Writer) Reset() {
@ -129,3 +134,7 @@ func (w *Writer) Reset() {
func (w *Writer) Grow(n int) { func (w *Writer) Grow(n int) {
(*bytes.Buffer)(w).Grow(n) (*bytes.Buffer)(w).Grow(n)
} }
func (w *Writer) put() {
PutWriter(w)
}

View File

@ -1,7 +1,7 @@
package binary package binary
import ( import (
"math/rand" "crypto/rand"
"testing" "testing"
) )
@ -49,3 +49,51 @@ func BenchmarkNewWriterF128_5(b *testing.B) {
} }
}) })
} }
func BenchmarkOpenWriterF128(b *testing.B) {
test := make([]byte, 128)
rand.Read(test)
b.StartTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, close := OpenWriterF(func(w *Writer) {
w.Write(test)
})
close()
}
})
}
func BenchmarkOpenWriterF128_3(b *testing.B) {
test := make([]byte, 128)
rand.Read(test)
b.StartTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, close := OpenWriterF(func(w *Writer) {
w.Write(test)
w.Write(test)
w.Write(test)
})
close()
}
})
}
func BenchmarkOpenWriterF128_5(b *testing.B) {
test := make([]byte, 128)
rand.Read(test)
b.StartTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, close := OpenWriterF(func(w *Writer) {
w.Write(test)
w.Write(test)
w.Write(test)
w.Write(test)
w.Write(test)
})
close()
}
})
}

View File

@ -1,8 +1,11 @@
package client package client
import "github.com/Mrs4s/MiraiGo/client/pb/msg" import (
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/msg"
)
var privateMsgDecoders = map[int32]func(*QQClient, *msg.Message, *incomingPacketInfo){ 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,
@ -10,21 +13,21 @@ var privateMsgDecoders = map[int32]func(*QQClient, *msg.Message, *incomingPacket
208: privatePttDecoder, 208: privatePttDecoder,
} }
var nonSvcNotifyTroopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *incomingPacketInfo){ 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, *incomingPacketInfo){ 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, *incomingPacketInfo){ 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, *incomingPacketInfo){ 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

@ -1,18 +1,18 @@
package client package client
//go:generate go run cmd/c2c_switcher.go //go:generate go run github.com/Mrs4s/MiraiGo/internal/generator/c2c_switcher
import ( import (
"fmt" "fmt"
"sync/atomic"
"time" "time"
"github.com/Mrs4s/MiraiGo/binary" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/pkg/errors" "github.com/Mrs4s/MiraiGo/internal/proto"
"google.golang.org/protobuf/proto"
) )
type ( type (
@ -28,21 +28,21 @@ type (
) )
const ( const (
GroupSource TempSessionSource = 0 // 来自群聊 GroupSource TempSessionSource = iota // 来自群聊
ConsultingSource TempSessionSource = 1 // 来自QQ咨询 ConsultingSource // 来自QQ咨询
SearchSource TempSessionSource = 2 // 来自查找 SearchSource // 来自查找
MovieSource TempSessionSource = 3 // 来自QQ电影 MovieSource // 来自QQ电影
HotChatSource TempSessionSource = 4 // 来自热聊 HotChatSource // 来自热聊
SystemMessageSource TempSessionSource = 6 // 来自验证消息 SystemMessageSource // 来自验证消息
MultiChatSource TempSessionSource = 7 // 来自多人聊天 MultiChatSource // 来自多人聊天
DateSource TempSessionSource = 8 // 来自约会 DateSource // 来自约会
AddressBookSource TempSessionSource = 9 // 来自通讯录 AddressBookSource // 来自通讯录
) )
func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info *incomingPacketInfo) { func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info network.RequestParams) {
c.syncCookie = rsp.SyncCookie c.sig.SyncCookie = rsp.SyncCookie
c.pubAccountCookie = rsp.PubAccountCookie c.sig.PubAccountCookie = rsp.PubAccountCookie
c.msgCtrlBuf = rsp.MsgCtrlBuf // c.msgCtrlBuf = rsp.MsgCtrlBuf
if rsp.UinPairMsgs == nil { if rsp.UinPairMsgs == nil {
return return
} }
@ -51,17 +51,17 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info *in
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, info) c.commMsgProcessor(pMsg, info)
@ -70,43 +70,43 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info *in
if delItems != nil { if delItems != nil {
_, _ = c.sendAndWait(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.String()) c.debug("continue sync with flag: %v", rsp.SyncFlag)
seq, pkt := c.buildGetMessageRequestPacket(rsp.GetSyncFlag(), time.Now().Unix()) seq, pkt := c.buildGetMessageRequestPacket(rsp.SyncFlag.Unwrap(), time.Now().Unix())
_, _ = c.sendAndWait(seq, pkt, info.Params) _, _ = c.sendAndWait(seq, pkt, info)
} }
} }
func (c *QQClient) commMsgProcessor(pMsg *msg.Message, info *incomingPacketInfo) { 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 info.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, info) 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, _ *incomingPacketInfo) { 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 := atomic.LoadInt32(&c.friendSeq) frdSeq := c.friendSeq.Load()
if frdSeq < pMsg.Head.GetMsgSeq() { if frdSeq < pMsg.Head.MsgSeq.Unwrap() {
if atomic.CompareAndSwapInt32(&c.friendSeq, frdSeq, pMsg.Head.GetMsgSeq()) { if c.friendSeq.CompareAndSwap(frdSeq, pMsg.Head.MsgSeq.Unwrap()) {
break break
} }
} else { } else {
@ -117,53 +117,67 @@ func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo
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, _ *incomingPacketInfo) { 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, _ *incomingPacketInfo) { 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:
@ -184,41 +198,41 @@ func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ *incomingPacketInfo) {
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, _ *incomingPacketInfo) { 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,
}) })
@ -226,45 +240,45 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ *incomingP
} }
} }
func systemMessageDecoder(c *QQClient, _ *msg.Message, _ *incomingPacketInfo) { func systemMessageDecoder(c *QQClient, _ *msg.Message, _ network.RequestParams) {
_, pkt := c.buildSystemMsgNewFriendPacket() _, pkt := c.buildSystemMsgNewFriendPacket()
_ = c.sendPacket(pkt) _ = c.sendPacket(pkt)
} }
func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message, info *incomingPacketInfo) { 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 *incomingPacketInfo) { 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.sendAndWait(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

@ -1,21 +1,22 @@
// Code generated by client/c2c_switcher.go DO NOT EDIT. // Code generated by internal/generator/c2c_switcher DO NOT EDIT.
package client package client
import ( import (
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
) )
const ( const (
UnknownDecoder = iota UnknownDecoder = iota
privateMsgDecoders
nonSvcNotifyTroopSystemMsgDecoders nonSvcNotifyTroopSystemMsgDecoders
troopSystemMsgDecoders
sysMsgDecoders
otherDecoders otherDecoders
privateMsgDecoders
sysMsgDecoders
troopSystemMsgDecoders
) )
func peekC2CDecoder(msgType int32) (decoder func(*QQClient, *msg.Message, *incomingPacketInfo), 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

@ -3,28 +3,29 @@ package client
import ( import (
"crypto/md5" "crypto/md5"
"fmt" "fmt"
"math"
"math/rand" "math/rand"
"net" "net/netip"
"sort" "sort"
"strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/Mrs4s/MiraiGo/internal/crypto"
packets2 "github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/pkg/errors" "github.com/pkg/errors"
"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/highway"
"github.com/Mrs4s/MiraiGo/client/internal/intern"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"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/message"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
//go:generate go run github.com/a8m/syncmap -o "handler_map_gen.go" -pkg client -name HandlerMap "map[uint16]*handlerInfo"
type QQClient struct { type QQClient struct {
Uin int64 Uin int64
PasswordMd5 [16]byte PasswordMd5 [16]byte
@ -33,108 +34,97 @@ type QQClient struct {
once sync.Once once sync.Once
// option // option
AllowSlider bool AllowSlider bool
UseFragmentMessage bool
// account info // account info
Online atomic.Bool
Nickname string Nickname string
Age uint16 Age uint16
Gender uint16 Gender uint16
FriendList []*FriendInfo FriendList []*FriendInfo
GroupList []*GroupInfo GroupList []*GroupInfo
OnlineClients []*OtherClientInfo OnlineClients []*OtherClientInfo
Online bool
QiDian *QiDianAccountInfo QiDian *QiDianAccountInfo
GuildService *GuildService GuildService *GuildService
// protocol public field // protocol public field
SequenceId int32 SequenceId atomic.Int32
OutGoingPacketSessionId []byte SessionId []byte
RandomKey []byte TCP *network.TCPClient // todo: combine other protocol state into one struct
TCP *utils.TCPListener ConnectTime time.Time
ConnectTime time.Time
transport *network.Transport
oicq *oicq.Codec
logger Logger
// internal state // internal state
handlers HandlerMap handlers syncx.Map[uint16, *handlerInfo]
waiters sync.Map waiters syncx.Map[string, func(any, error)]
servers []*net.TCPAddr initServerOnce sync.Once
servers []netip.AddrPort
currServerIndex int currServerIndex int
retryTimes int retryTimes int
version *versionInfo
deviceInfo *DeviceInfo
alive bool alive bool
ecdh *crypto.EncryptECDH
// tlv cache
t104 []byte
t174 []byte
g []byte
t402 []byte
t150 []byte
t149 []byte
t528 []byte
t530 []byte
randSeed []byte // t403
rollbackSig []byte
// sync info
syncCookie []byte
pubAccountCookie []byte
msgCtrlBuf []byte
ksid []byte
// session info // session info
sigInfo *loginSigInfo qwebSeq atomic.Int64
bigDataSession *bigDataSessionInfo sig *auth.SigInfo
dpwd []byte highwaySession *highway.Session
timeDiff int64 // pwdFlag bool
pwdFlag bool // timeDiff int64
// address // address
srvSsoAddrs []string // 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
lastMessageSeq int32 msgSvcCache *utils.Cache[unit]
msgSvcCache *utils.Cache
lastC2CMsgTime int64 lastC2CMsgTime int64
transCache *utils.Cache transCache *utils.Cache[unit]
lastLostMsg string
groupSysMsgCache *GroupSystemMessages groupSysMsgCache *GroupSystemMessages
groupMsgBuilders sync.Map msgBuilders syncx.Map[int32, *messageBuilder]
onlinePushCache *utils.Cache onlinePushCache *utils.Cache[unit]
requestPacketRequestID int32
groupSeq int32
friendSeq int32
heartbeatEnabled bool heartbeatEnabled bool
groupDataTransSeq int32 requestPacketRequestID atomic.Int32
highwayApplyUpSeq int32 groupSeq atomic.Int32
eventHandlers *eventHandlers friendSeq atomic.Int32
highwayApplyUpSeq atomic.Int32
groupListLock sync.Mutex groupListLock sync.Mutex
} }
type loginSigInfo struct {
loginBitmap uint64
tgt []byte
tgtKey []byte
srmToken []byte // study room manager | 0x16a
t133 []byte
encryptedA1 []byte
userStKey []byte
userStWebSig []byte
sKey []byte
sKeyExpiredTime int64
d2 []byte
d2Key []byte
wtSessionTicketKey []byte
deviceToken []byte
psKeyMap map[string][]byte
pt4TokenMap map[string][]byte
}
type QiDianAccountInfo struct { type QiDianAccountInfo struct {
MasterUin int64 MasterUin int64
ExtName string ExtName string
@ -145,12 +135,19 @@ type QiDianAccountInfo struct {
} }
type handlerInfo struct { type handlerInfo struct {
fun func(i interface{}, err error) fun func(i any, err error)
dynamic bool dynamic bool
params requestParams params network.RequestParams
} }
var decoders = map[string]func(*QQClient, *incomingPacketInfo, []byte) (interface{}, error){ func (h *handlerInfo) getParams() network.RequestParams {
if h == nil {
return nil
}
return h.params
}
var decoders = map[string]func(*QQClient, *network.Packet) (any, error){
"wtlogin.login": decodeLoginResponse, "wtlogin.login": decodeLoginResponse,
"wtlogin.exchange_emp": decodeExchangeEmpResponse, "wtlogin.exchange_emp": decodeExchangeEmpResponse,
"wtlogin.trans_emp": decodeTransEmpResponse, "wtlogin.trans_emp": decodeTransEmpResponse,
@ -159,6 +156,7 @@ var decoders = map[string]func(*QQClient, *incomingPacketInfo, []byte) (interfac
"MessageSvc.PushNotify": decodeSvcNotify, "MessageSvc.PushNotify": decodeSvcNotify,
"OnlinePush.ReqPush": decodeOnlinePushReqPacket, "OnlinePush.ReqPush": decodeOnlinePushReqPacket,
"OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket, "OnlinePush.PbPushTransMsg": decodeOnlinePushTransPacket,
"OnlinePush.SidTicketExpired": decodeSidExpiredPacket,
"ConfigPushSvc.PushReq": decodePushReqPacket, "ConfigPushSvc.PushReq": decodePushReqPacket,
"MessageSvc.PbGetMsg": decodeMessageSvcPacket, "MessageSvc.PbGetMsg": decodeMessageSvcPacket,
"MessageSvc.PushForceOffline": decodeForceOfflinePacket, "MessageSvc.PushForceOffline": decodeForceOfflinePacket,
@ -168,18 +166,12 @@ var decoders = map[string]func(*QQClient, *incomingPacketInfo, []byte) (interfac
"friendlist.GetTroopListReqV2": decodeGroupListResponse, "friendlist.GetTroopListReqV2": decodeGroupListResponse,
"friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse, "friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse,
"group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse, "group_member_card.get_group_member_card_info": decodeGroupMemberInfoResponse,
"PttStore.GroupPttUp": decodeGroupPttStoreResponse,
"LongConn.OffPicUp": decodeOffPicUpResponse, "LongConn.OffPicUp": decodeOffPicUpResponse,
"ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket, "ProfileService.Pb.ReqSystemMsgNew.Group": decodeSystemMsgGroupPacket,
"ProfileService.Pb.ReqSystemMsgNew.Friend": decodeSystemMsgFriendPacket, "ProfileService.Pb.ReqSystemMsgNew.Friend": decodeSystemMsgFriendPacket,
"OidbSvc.0xd79": decodeWordSegmentation, "OidbSvc.0xd79": decodeWordSegmentation,
"OidbSvc.0x990": decodeTranslateResponse, "OidbSvc.0x990": decodeTranslateResponse,
"SummaryCard.ReqSummaryCard": decodeSummaryCardResponse, "SummaryCard.ReqSummaryCard": decodeSummaryCardResponse,
"LightAppSvc.mini_app_info.GetAppInfoById": decodeAppInfoResponse,
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
} }
// NewClient create new qq client // NewClient create new qq client
@ -188,94 +180,58 @@ func NewClient(uin int64, password string) *QQClient {
} }
func NewClientEmpty() *QQClient { func NewClientEmpty() *QQClient {
return NewClient(0, "") return NewClientMd5(0, [16]byte{})
} }
func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
cli := &QQClient{ cli := &QQClient{
Uin: uin, Uin: uin,
PasswordMd5: passwordMd5, PasswordMd5: passwordMd5,
SequenceId: 0x3635, AllowSlider: true,
AllowSlider: true, TCP: &network.TCPClient{},
RandomKey: make([]byte, 16), sig: &auth.SigInfo{
OutGoingPacketSessionId: []byte{0x02, 0xB0, 0x5B, 0x8B}, OutPacketSessionID: []byte{0x02, 0xB0, 0x5B, 0x8B},
TCP: &utils.TCPListener{}, },
sigInfo: &loginSigInfo{}, msgSvcCache: utils.NewCache[unit](time.Second * 15),
requestPacketRequestID: 1921334513, transCache: utils.NewCache[unit](time.Second * 15),
groupSeq: int32(rand.Intn(20000)), onlinePushCache: utils.NewCache[unit](time.Second * 15),
friendSeq: 22911, alive: true,
highwayApplyUpSeq: 77918, highwaySession: new(highway.Session),
eventHandlers: &eventHandlers{},
msgSvcCache: utils.NewCache(time.Second * 15),
transCache: utils.NewCache(time.Second * 15),
onlinePushCache: utils.NewCache(time.Second * 15),
servers: []*net.TCPAddr{},
alive: true,
ecdh: crypto.NewEcdh(),
} }
cli.transport = &network.Transport{Sig: cli.sig}
cli.oicq = oicq.NewCodec(cli.Uin)
{ // init atomic values
cli.SequenceId.Store(int32(rand.Intn(100000)))
cli.requestPacketRequestID.Store(1921334513)
cli.groupSeq.Store(int32(rand.Intn(20000)))
cli.friendSeq.Store(22911)
cli.highwayApplyUpSeq.Store(77918)
}
cli.highwaySession.Uin = strconv.FormatInt(cli.Uin, 10)
cli.GuildService = &GuildService{c: cli} cli.GuildService = &GuildService{c: cli}
cli.ecdh.FetchPubKey(uin)
cli.UseDevice(SystemDeviceInfo)
sso, err := getSSOAddress()
if err == nil && len(sso) > 0 {
cli.servers = append(sso, cli.servers...)
}
adds, err := net.LookupIP("msfwifi.3g.qq.com") // host servers
if err == nil && len(adds) > 0 {
var hostAddrs []*net.TCPAddr
for _, addr := range adds {
hostAddrs = append(hostAddrs, &net.TCPAddr{
IP: addr,
Port: 8080,
})
}
cli.servers = append(hostAddrs, cli.servers...)
}
if len(cli.servers) == 0 {
cli.servers = []*net.TCPAddr{ // default servers
{IP: net.IP{42, 81, 172, 81}, Port: 80},
{IP: net.IP{114, 221, 148, 59}, Port: 14000},
{IP: net.IP{42, 81, 172, 147}, Port: 443},
{IP: net.IP{125, 94, 60, 146}, Port: 80},
{IP: net.IP{114, 221, 144, 215}, Port: 80},
{IP: net.IP{42, 81, 172, 22}, Port: 80},
}
}
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.TCP.PlannedDisconnect(cli.plannedDisconnect) cli.TCP.PlannedDisconnect(cli.plannedDisconnect)
cli.TCP.UnexpectedDisconnect(cli.unexpectedDisconnect) cli.TCP.UnexpectedDisconnect(cli.unexpectedDisconnect)
rand.Read(cli.RandomKey)
return cli return cli
} }
func (c *QQClient) UseDevice(info *DeviceInfo) { func (c *QQClient) version() *auth.AppVersion {
c.version = genVersionInfo(info.Protocol) return c.transport.Version
c.ksid = []byte(fmt.Sprintf("|%s|A8.2.7.27f6ea96", info.IMEI)) }
c.deviceInfo = info
func (c *QQClient) Device() *DeviceInfo {
return c.transport.Device
}
func (c *QQClient) UseDevice(info *auth.Device) {
c.transport.Version = info.Protocol.Version()
c.transport.Device = info
c.highwaySession.AppID = int32(c.version().AppId)
c.sig.Ksid = []byte(fmt.Sprintf("|%s|A8.2.7.27f6ea96", info.IMEI))
} }
func (c *QQClient) Release() { func (c *QQClient) Release() {
if c.Online { if c.Online.Load() {
c.Disconnect() c.Disconnect()
} }
c.alive = false c.alive = false
@ -283,7 +239,7 @@ func (c *QQClient) Release() {
// Login send login request // Login send login request
func (c *QQClient) Login() (*LoginResponse, error) { func (c *QQClient) Login() (*LoginResponse, error) {
if c.Online { if c.Online.Load() {
return nil, ErrAlreadyOnline return nil, ErrAlreadyOnline
} }
err := c.connect() err := c.connect()
@ -297,13 +253,13 @@ func (c *QQClient) Login() (*LoginResponse, error) {
} }
l := rsp.(LoginResponse) l := rsp.(LoginResponse)
if l.Success { if l.Success {
_ = c.init(false) err = c.init(false)
} }
return &l, nil return &l, err
} }
func (c *QQClient) TokenLogin(token []byte) error { func (c *QQClient) TokenLogin(token []byte) error {
if c.Online { if c.Online.Load() {
return ErrAlreadyOnline return ErrAlreadyOnline
} }
err := c.connect() err := c.connect()
@ -313,18 +269,18 @@ func (c *QQClient) TokenLogin(token []byte) error {
{ {
r := binary.NewReader(token) r := binary.NewReader(token)
c.Uin = r.ReadInt64() c.Uin = r.ReadInt64()
c.sigInfo.d2 = r.ReadBytesShort() c.sig.D2 = r.ReadBytesShort()
c.sigInfo.d2Key = r.ReadBytesShort() c.sig.D2Key = r.ReadBytesShort()
c.sigInfo.tgt = r.ReadBytesShort() c.sig.TGT = r.ReadBytesShort()
c.sigInfo.srmToken = r.ReadBytesShort() c.sig.SrmToken = r.ReadBytesShort()
c.sigInfo.t133 = r.ReadBytesShort() c.sig.T133 = r.ReadBytesShort()
c.sigInfo.encryptedA1 = r.ReadBytesShort() c.sig.EncryptedA1 = r.ReadBytesShort()
c.sigInfo.wtSessionTicketKey = r.ReadBytesShort() c.oicq.WtSessionTicketKey = r.ReadBytesShort()
c.OutGoingPacketSessionId = 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()
} }
_, err = c.sendAndWait(c.buildRequestChangeSigPacket()) _, err = c.sendAndWait(c.buildRequestChangeSigPacket(true))
if err != nil { if err != nil {
return err return err
} }
@ -332,14 +288,18 @@ func (c *QQClient) TokenLogin(token []byte) error {
} }
func (c *QQClient) FetchQRCode() (*QRCodeLoginResponse, error) { func (c *QQClient) FetchQRCode() (*QRCodeLoginResponse, error) {
if c.Online { return c.FetchQRCodeCustomSize(3, 4, 2)
}
func (c *QQClient) FetchQRCodeCustomSize(size, margin, ecLevel uint32) (*QRCodeLoginResponse, error) {
if c.Online.Load() {
return nil, ErrAlreadyOnline return nil, ErrAlreadyOnline
} }
err := c.connect() err := c.connect()
if err != nil { if err != nil {
return nil, err return nil, err
} }
i, err := c.sendAndWait(c.buildQRCodeFetchRequestPacket()) i, err := c.sendAndWait(c.buildQRCodeFetchRequestPacket(size, margin, ecLevel))
if err != nil { if err != nil {
return nil, errors.Wrap(err, "fetch qrcode error") return nil, errors.Wrap(err, "fetch qrcode error")
} }
@ -361,9 +321,9 @@ func (c *QQClient) QRCodeLogin(info *QRCodeLoginInfo) (*LoginResponse, error) {
} }
rsp := i.(LoginResponse) rsp := i.(LoginResponse)
if rsp.Success { if rsp.Success {
_ = c.init(false) err = c.init(false)
} }
return &rsp, nil return &rsp, err
} }
// SubmitCaptcha send captcha to server // SubmitCaptcha send captcha to server
@ -376,9 +336,9 @@ func (c *QQClient) SubmitCaptcha(result string, sign []byte) (*LoginResponse, er
} }
l := rsp.(LoginResponse) l := rsp.(LoginResponse)
if l.Success { if l.Success {
_ = c.init(false) err = c.init(false)
} }
return &l, nil return &l, err
} }
func (c *QQClient) SubmitTicket(ticket string) (*LoginResponse, error) { func (c *QQClient) SubmitTicket(ticket string) (*LoginResponse, error) {
@ -390,9 +350,9 @@ func (c *QQClient) SubmitTicket(ticket string) (*LoginResponse, error) {
} }
l := rsp.(LoginResponse) l := rsp.(LoginResponse)
if l.Success { if l.Success {
_ = c.init(false) err = c.init(false)
} }
return &l, nil return &l, err
} }
func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) { func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) {
@ -403,33 +363,34 @@ func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) {
} }
l := rsp.(LoginResponse) l := rsp.(LoginResponse)
if l.Success { if l.Success {
_ = c.init(false) err = c.init(false)
} }
return &l, nil return &l, err
} }
func (c *QQClient) RequestSMS() bool { func (c *QQClient) RequestSMS() bool {
rsp, err := c.sendAndWait(c.buildSMSRequestPacket()) 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
} }
func (c *QQClient) init(tokenLogin bool) error { func (c *QQClient) init(tokenLogin bool) error {
if len(c.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)
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 {
@ -447,12 +408,12 @@ func (c *QQClient) init(tokenLogin bool) error {
go c.doHeartbeat() go c.doHeartbeat()
} }
_ = c.RefreshStatus() _ = c.RefreshStatus()
if c.version.Protocol == QiDian { if c.version().Protocol == auth.QiDian {
_, _ = c.sendAndWait(c.buildLoginExtraPacket()) // 小登录 _, _ = c.sendAndWait(c.buildLoginExtraPacket()) // 小登录
_, _ = c.sendAndWait(c.buildConnKeyRequestPacket()) // big data key 如果等待 config push 的话时间来不及 _, _ = c.sendAndWait(c.buildConnKeyRequestPacket()) // big data key 如果等待 config push 的话时间来不及
} }
seq, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix()) seq, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix())
_, _ = c.sendAndWait(seq, pkt, requestParams{"used_reg_proxy": true, "init": true}) _, _ = c.sendAndWait(seq, pkt, network.RequestParams{"used_reg_proxy": true, "init": true})
c.syncChannelFirstView() c.syncChannelFirstView()
return nil return nil
} }
@ -460,15 +421,15 @@ func (c *QQClient) init(tokenLogin bool) error {
func (c *QQClient) GenToken() []byte { func (c *QQClient) GenToken() []byte {
return binary.NewWriterF(func(w *binary.Writer) { return binary.NewWriterF(func(w *binary.Writer) {
w.WriteUInt64(uint64(c.Uin)) w.WriteUInt64(uint64(c.Uin))
w.WriteBytesShort(c.sigInfo.d2) w.WriteBytesShort(c.sig.D2)
w.WriteBytesShort(c.sigInfo.d2Key) w.WriteBytesShort(c.sig.D2Key)
w.WriteBytesShort(c.sigInfo.tgt) w.WriteBytesShort(c.sig.TGT)
w.WriteBytesShort(c.sigInfo.srmToken) w.WriteBytesShort(c.sig.SrmToken)
w.WriteBytesShort(c.sigInfo.t133) w.WriteBytesShort(c.sig.T133)
w.WriteBytesShort(c.sigInfo.encryptedA1) w.WriteBytesShort(c.sig.EncryptedA1)
w.WriteBytesShort(c.sigInfo.wtSessionTicketKey) w.WriteBytesShort(c.oicq.WtSessionTicketKey)
w.WriteBytesShort(c.OutGoingPacketSessionId) w.WriteBytesShort(c.sig.OutPacketSessionID)
w.WriteBytesShort(c.deviceInfo.TgtgtKey) w.WriteBytesShort(c.Device().TgtgtKey)
}) })
} }
@ -517,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 == 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
@ -542,71 +503,6 @@ func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
return r, nil return r, nil
} }
func (c *QQClient) GetForwardMessage(resID string) *message.ForwardMessage {
m := c.DownloadForwardMessage(resID)
if m == nil {
return nil
}
var (
item *msg.PbMultiMsgItem
ret = &message.ForwardMessage{Nodes: []*message.ForwardNode{}}
)
for _, iter := range m.Items {
if iter.GetFileName() == m.FileName {
item = iter
}
}
if item == nil {
return nil
}
for _, m := range item.GetBuffer().GetMsg() {
ret.Nodes = append(ret.Nodes, &message.ForwardNode{
SenderId: m.Head.GetFromUin(),
SenderName: func() string {
if m.Head.GetMsgType() == 82 && m.Head.GroupInfo != nil {
return m.Head.GroupInfo.GetGroupCard()
}
return m.Head.GetFromNick()
}(),
Time: m.Head.GetMsgTime(),
Message: message.ParseMessageElems(m.Body.RichText.Elems),
})
}
return ret
}
func (c *QQClient) DownloadForwardMessage(resId string) *message.ForwardElement {
i, err := c.sendAndWait(c.buildMultiApplyDownPacket(resId))
if err != nil {
return nil
}
multiMsg := i.(*msg.PbMultiMsgTransmit)
if multiMsg.GetPbItemList() == nil {
return nil
}
var pv string
for i := 0; i < int(math.Min(4, float64(len(multiMsg.GetMsg())))); i++ {
m := multiMsg.Msg[i]
pv += fmt.Sprintf(`<title size="26" color="#777777">%s: %s</title>`,
func() string {
if m.Head.GetMsgType() == 82 && m.Head.GroupInfo != nil {
return m.Head.GroupInfo.GetGroupCard()
}
return m.Head.GetFromNick()
}(),
message.ToReadableString(
message.ParseMessageElems(multiMsg.Msg[i].GetBody().GetRichText().Elems),
),
)
}
return genForwardTemplate(
resId, pv, "群聊的聊天记录", "[聊天记录]", "聊天记录",
fmt.Sprintf("查看 %d 条转发消息", len(multiMsg.GetMsg())),
time.Now().UnixNano(),
multiMsg.GetPbItemList(),
)
}
func (c *QQClient) SendGroupPoke(groupCode, target int64) { func (c *QQClient) SendGroupPoke(groupCode, target int64) {
_, _ = c.sendAndWait(c.buildGroupPokePacket(groupCode, target)) _, _ = c.sendAndWait(c.buildGroupPokePacket(groupCode, target))
} }
@ -631,6 +527,7 @@ func (c *QQClient) GetGroupList() ([]*GroupInfo, error) {
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
@ -643,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()
@ -656,6 +554,11 @@ 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 {
@ -664,7 +567,7 @@ func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error)
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
@ -673,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 {
@ -693,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 {
@ -729,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 = ""
@ -757,11 +669,11 @@ func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) {
} }
func (c *QQClient) getSKey() string { func (c *QQClient) getSKey() string {
if c.sigInfo.sKeyExpiredTime < time.Now().Unix() && len(c.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.sendAndWait(c.buildRequestTgtgtNopicsigPacket()) _, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket())
} }
return string(c.sigInfo.sKey) return string(c.sig.SKey)
} }
func (c *QQClient) getCookies() string { func (c *QQClient) getCookies() string {
@ -771,7 +683,7 @@ func (c *QQClient) getCookies() string {
func (c *QQClient) getCookiesWithDomain(domain string) string { func (c *QQClient) getCookiesWithDomain(domain string) string {
cookie := c.getCookies() cookie := c.getCookies()
if psKey, ok := c.sigInfo.psKeyMap[domain]; ok { if psKey, ok := c.sig.PsKeyMap[domain]; ok {
return fmt.Sprintf("%s p_uin=o%d; p_skey=%s;", cookie, c.Uin, psKey) return fmt.Sprintf("%s p_uin=o%d; p_skey=%s;", cookie, c.Uin, psKey)
} else { } else {
return cookie return cookie
@ -802,10 +714,6 @@ func (c *QQClient) updateGroupName(groupCode int64, newName string) {
_, _ = c.sendAndWait(c.buildGroupNameUpdatePacket(groupCode, newName)) _, _ = c.sendAndWait(c.buildGroupNameUpdatePacket(groupCode, newName))
} }
func (c *QQClient) updateGroupMemo(groupCode int64, newMemo string) {
_, _ = c.sendAndWait(c.buildGroupMemoUpdatePacket(groupCode, newMemo))
}
func (c *QQClient) groupMuteAll(groupCode int64, mute bool) { func (c *QQClient) groupMuteAll(groupCode int64, mute bool) {
_, _ = c.sendAndWait(c.buildGroupMuteAllPacket(groupCode, mute)) _, _ = c.sendAndWait(c.buildGroupMuteAllPacket(groupCode, mute))
} }
@ -818,8 +726,8 @@ func (c *QQClient) quitGroup(groupCode int64) {
_, _ = c.sendAndWait(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.sendAndWait(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) {
@ -834,57 +742,73 @@ func (g *GroupInfo) removeMember(uin int64) {
}) })
} }
func (c *QQClient) SetCustomServer(servers []*net.TCPAddr) { func (c *QQClient) setGroupAnonymous(groupCode int64, enable bool) {
c.servers = append(servers, c.servers...) _, _ = c.sendAndWait(c.buildSetGroupAnonymous(groupCode, enable))
} }
func (c *QQClient) SendGroupGift(groupCode, uin uint64, gift message.GroupGift) { // UpdateProfile 修改个人资料
_, packet := c.sendGroupGiftPacket(groupCode, uin, gift) func (c *QQClient) UpdateProfile(profile ProfileDetailUpdate) {
_ = c.sendPacket(packet) _, _ = 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.sendAndWait(c.buildClientRegisterPacket()) _, err := c.sendAndWait(c.buildClientRegisterPacket())
if err == nil { if err == nil {
c.Online = true c.Online.Store(true)
} }
return err return err
} }
func (c *QQClient) nextSeq() uint16 { func (c *QQClient) nextSeq() uint16 {
return uint16(atomic.AddInt32(&c.SequenceId, 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 {
return atomic.AddInt32(&c.requestPacketRequestID, 2) return c.requestPacketRequestID.Add(2)
} }
func (c *QQClient) nextGroupSeq() int32 { func (c *QQClient) nextGroupSeq() int32 {
return atomic.AddInt32(&c.groupSeq, 2) return c.groupSeq.Add(2)
} }
func (c *QQClient) nextFriendSeq() int32 { func (c *QQClient) nextFriendSeq() int32 {
return atomic.AddInt32(&c.friendSeq, 1) return c.friendSeq.Add(1)
} }
func (c *QQClient) nextGroupDataTransSeq() int32 { func (c *QQClient) nextQWebSeq() int64 {
return atomic.AddInt32(&c.groupDataTransSeq, 2) return c.qwebSeq.Add(1)
} }
func (c *QQClient) nextHighwayApplySeq() int32 { func (c *QQClient) nextHighwayApplySeq() int32 {
return atomic.AddInt32(&c.highwayApplyUpSeq, 2) return c.highwayApplyUpSeq.Add(2)
} }
func (c *QQClient) doHeartbeat() { func (c *QQClient) doHeartbeat() {
c.heartbeatEnabled = true c.heartbeatEnabled = true
times := 0 times := 0
for c.Online { for c.Online.Load() {
time.Sleep(time.Second * 30) time.Sleep(time.Second * 30)
seq := c.nextSeq() seq := c.nextSeq()
sso := packets2.BuildSsoPacket(seq, c.version.AppId, c.version.SubAppId, "Heartbeat.Alive", c.deviceInfo.IMEI, []byte{}, c.OutGoingPacketSessionId, []byte{}, c.ksid) req := network.Request{
packet := packets2.BuildLoginPacket(c.Uin, 0, []byte{}, sso, []byte{}) Type: network.RequestTypeLogin,
EncryptType: network.EncryptTypeNoEncrypt,
SequenceID: int32(seq),
Uin: c.Uin,
CommandName: "Heartbeat.Alive",
Body: EmptyBytes,
}
packet := c.transport.PackPacket(&req)
_, err := c.sendAndWait(seq, packet) _, err := c.sendAndWait(seq, packet)
if errors.Is(err, utils.ErrConnectionClosed) { if errors.Is(err, network.ErrConnectionClosed) {
continue continue
} }
times++ times++

View File

@ -2,27 +2,28 @@ package client
import ( import (
"crypto/md5" "crypto/md5"
"encoding/hex"
"fmt" "fmt"
"net" "net/netip"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"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/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"
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/client/pb/profilecard" "github.com/Mrs4s/MiraiGo/client/pb/profilecard"
"github.com/Mrs4s/MiraiGo/client/pb/qweb"
"github.com/Mrs4s/MiraiGo/client/pb/structmsg" "github.com/Mrs4s/MiraiGo/client/pb/structmsg"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
@ -32,38 +33,46 @@ var (
) )
// wtlogin.login // wtlogin.login
func decodeLoginResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
reader := binary.NewReader(payload) 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 m.Exists(0x402) { if err != nil {
c.dpwd = []byte(utils.RandomString(16)) return nil, err
c.t402 = m[0x402]
h := md5.Sum(append(append(c.deviceInfo.Guid, c.dpwd...), c.t402...))
c.g = h[:]
} }
if m.Exists(0x402) {
c.sig.Dpwd = []byte(utils.RandomString(16))
c.sig.T402 = m[0x402]
h := md5.Sum(append(append(c.Device().Guid, c.sig.Dpwd...), c.sig.T402...))
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 { // if t161, ok := m[0x161]; ok {
c.decodeT161(t161) // c.decodeT161(t161)
} // }
if m.Exists(0x403) { if m.Exists(0x403) {
c.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
} }
if t == 2 { if t == 2 {
c.t104 = m[0x104] c.sig.T104 = m[0x104]
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, _ *incomingPacketInfo, payload []byte) (in
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, _ *incomingPacketInfo, payload []byte) (in
} 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, _ *incomingPacketInfo, payload []byte) (in
if t == 40 { if t == 40 {
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
ErrorMessage: "账号被冻结", ErrorMessage: "账号被冻结",
Error: UnknownLoginError, Error: UnknownLoginError,
}, nil }, nil
@ -97,16 +109,18 @@ func decodeLoginResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (in
if t == 160 || t == 239 { if t == 160 || t == 239 {
if t174, ok := m[0x174]; ok { // 短信验证 if t174, ok := m[0x174]; ok { // 短信验证
c.t104 = m[0x104] c.sig.T104 = m[0x104]
c.t174 = t174 c.sig.T174 = t174
c.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, _ *incomingPacketInfo, payload []byte) (in
} }
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]),
@ -122,9 +137,10 @@ func decodeLoginResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (in
} }
if _, ok := m[0x17b]; ok { // 二次验证 if _, ok := m[0x17b]; ok { // 二次验证
c.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, _ *incomingPacketInfo, payload []byte) (in
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,13 +158,14 @@ func decodeLoginResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (in
if t == 162 { if t == 162 {
return LoginResponse{ return LoginResponse{
Code: t,
Error: TooManySMSRequestError, Error: TooManySMSRequestError,
}, nil }, nil
} }
if t == 204 { if t == 204 {
c.t104 = m[0x104] c.sig.T104 = m[0x104]
c.randSeed = m[0x403] c.sig.RandSeed = m[0x403]
return c.sendAndWait(c.buildDeviceLockLoginPacket()) return c.sendAndWait(c.buildDeviceLockLoginPacket())
} // drive lock } // drive lock
@ -157,6 +175,7 @@ func decodeLoginResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (in
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, _ *incomingPacketInfo, payload []byte) (in
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, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeClientRegisterResponse(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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,32 +217,34 @@ func decodeClientRegisterResponse(c *QQClient, _ *incomingPacketInfo, payload []
} }
// wtlogin.exchange_emp // wtlogin.exchange_emp
func decodeExchangeEmpResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeExchangeEmpResponse(c *QQClient, pkt *network.Packet) (any, error) {
reader := binary.NewReader(payload) 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 {
c.Error("exchange_emp error: %v", t) return nil, errors.Errorf("exchange_emp failed: %v", t)
return nil, errors.New("exchange_emp failed")
} }
if cmd == 15 { if cmd == 15 {
c.decodeT119R(m[0x119]) c.decodeT119R(m[0x119])
} }
if cmd == 11 { if cmd == 11 {
h := md5.Sum(c.sigInfo.d2Key) h := md5.Sum(c.sig.D2Key)
c.decodeT119(m[0x119], h[:]) c.decodeT119(m[0x119], h[:])
} }
return nil, nil return nil, nil
} }
// wtlogin.trans_emp // wtlogin.trans_emp
func decodeTransEmpResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeTransEmpResponse(c *QQClient, pkt *network.Packet) (any, error) {
if len(payload) < 48 { if len(pkt.Payload) < 48 {
return nil, errors.New("missing payload length") return nil, errors.New("missing payload length")
} }
reader := binary.NewReader(payload) 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()
@ -243,7 +265,10 @@ func decodeTransEmpResponse(c *QQClient, _ *incomingPacketInfo, payload []byte)
} }
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,
@ -283,13 +308,17 @@ func decodeTransEmpResponse(c *QQClient, _ *incomingPacketInfo, payload []byte)
return nil, errors.Errorf("wtlogin.trans_emp sub cmd 0x12 error: %v", code) 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)
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],
@ -300,9 +329,9 @@ func decodeTransEmpResponse(c *QQClient, _ *incomingPacketInfo, payload []byte)
} }
// ConfigPushSvc.PushReq // ConfigPushSvc.PushReq
func decodePushReqPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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:])
@ -312,19 +341,18 @@ func decodePushReqPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (in
switch t { switch t {
case 1: case 1:
ssoPkt := jce.NewJceReader(jceBuf) ssoPkt := jce.NewJceReader(jceBuf)
servers := []jce.SsoServerInfo{} servers := ssoPkt.ReadSsoServerInfos(1)
ssoPkt.ReadSlice(&servers, 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,50 +371,51 @@ func decodePushReqPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (in
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.bigDataSession = &bigDataSessionInfo{ c.highwaySession.SigSession = rsp.RspBody.SigSession
SigSession: rsp.RspBody.SigSession, c.highwaySession.SessionKey = rsp.RspBody.SessionKey
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.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(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)
_, pkt := c.buildConfPushRespPacket(t, seq, jceBuf) _, resp := c.buildConfPushRespPacket(t, seq, jceBuf)
return nil, c.sendPacket(pkt) return nil, c.sendPacket(resp)
} }
// MessageSvc.PbGetMsg // MessageSvc.PbGetMsg
func decodeMessageSvcPacket(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeMessageSvcPacket(c *QQClient, pkt *network.Packet) (any, error) {
rsp := msg.GetMessageResponse{} rsp := msg.GetMessageResponse{}
err := proto.Unmarshal(payload, &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, info) c.c2cMessageSyncProcessor(&rsp, pkt.Params)
return nil, nil return nil, nil
} }
// MessageSvc.PushNotify // MessageSvc.PushNotify
func decodeSvcNotify(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeSvcNotify(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload[15:])) 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 {
@ -411,9 +440,9 @@ func decodeSvcNotify(c *QQClient, _ *incomingPacketInfo, payload []byte) (interf
} }
// SummaryCard.ReqSummaryCard // SummaryCard.ReqSummaryCard
func decodeSummaryCardResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeSummaryCardResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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 {
@ -423,18 +452,35 @@ func decodeSummaryCardResponse(_ *QQClient, _ *incomingPacketInfo, payload []byt
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),
} }
services := [][]byte{} vipInfo := rsp.ReadMapIntVipInfo(29) // 1 -> vip, 3 -> svip
rsp.ReadSlice(&services, 46) 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)
readService := func(buf []byte) (*profilecard.BusiComm, []byte) { readService := func(buf []byte) (*profilecard.BusiComm, []byte) {
r := binary.NewReader(buf) r := binary.NewReader(buf)
r.ReadByte() r.ReadByte()
@ -448,11 +494,11 @@ func decodeSummaryCardResponse(_ *QQClient, _ *incomingPacketInfo, payload []byt
} }
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()
} }
} }
} }
@ -460,15 +506,14 @@ func decodeSummaryCardResponse(_ *QQClient, _ *incomingPacketInfo, payload []byt
} }
// friendlist.getFriendGroupList // friendlist.getFriendGroupList
func decodeFriendGroupListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeFriendGroupListResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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:])
totalFriendCount := r.ReadInt16(5) totalFriendCount := r.ReadInt16(5)
friends := make([]jce.FriendInfo, 0) friends := r.ReadFriendInfos(7)
r.ReadSlice(&friends, 7)
l := make([]*FriendInfo, 0, len(friends)) l := make([]*FriendInfo, 0, len(friends))
for _, f := range friends { for _, f := range friends {
l = append(l, &FriendInfo{ l = append(l, &FriendInfo{
@ -486,9 +531,9 @@ func decodeFriendGroupListResponse(_ *QQClient, _ *incomingPacketInfo, payload [
} }
// friendlist.delFriend // friendlist.delFriend
func decodeFriendDeleteResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeFriendDeleteResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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:])
@ -499,23 +544,20 @@ func decodeFriendDeleteResponse(_ *QQClient, _ *incomingPacketInfo, payload []by
} }
// friendlist.GetTroopListReqV2 // friendlist.GetTroopListReqV2
func decodeGroupListResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGroupListResponse(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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:])
vecCookie := []byte{} vecCookie := r.ReadBytes(4)
groups := []jce.TroopNumber{} groups := r.ReadTroopNumbers(5)
r.ReadSlice(&vecCookie, 4)
r.ReadSlice(&groups, 5)
l := make([]*GroupInfo, 0, len(groups)) l := make([]*GroupInfo, 0, len(groups))
for _, g := range groups { for _, g := range groups {
l = append(l, &GroupInfo{ l = append(l, &GroupInfo{
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),
@ -533,34 +575,31 @@ func decodeGroupListResponse(c *QQClient, _ *incomingPacketInfo, payload []byte)
} }
// friendlist.GetTroopMemberListReq // friendlist.GetTroopMemberListReq
func decodeGroupMemberListResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGroupMemberListResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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:])
members := make([]jce.TroopMemberInfo, 0) members := r.ReadTroopMemberInfos(3)
r.ReadSlice(&members, 3)
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{
@ -570,105 +609,103 @@ func decodeGroupMemberListResponse(_ *QQClient, _ *incomingPacketInfo, payload [
} }
// group_member_card.get_group_member_card_info // group_member_card.get_group_member_card_info
func decodeGroupMemberInfoResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGroupMemberInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := pb.GroupMemberRspBody{} rsp := pb.GroupMemberRspBody{}
if err := proto.Unmarshal(payload, &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, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeOffPicUpResponse(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := cmd0x352.RspBody{} rsp := cmd0x352.RspBody{}
if err := proto.Unmarshal(payload, &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, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeOnlinePushTransPacket(c *QQClient, pkt *network.Packet) (any, error) {
info := msg.TransMsgInfo{} info := msg.TransMsgInfo{}
err := proto.Unmarshal(payload, &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,
}) })
@ -678,13 +715,13 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *incomingPacketInfo, payload []b
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),
@ -693,7 +730,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *incomingPacketInfo, payload []b
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,
}) })
@ -701,16 +738,25 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *incomingPacketInfo, payload []b
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)
@ -718,19 +764,17 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *incomingPacketInfo, payload []b
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,
@ -744,9 +788,9 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *incomingPacketInfo, payload []b
} }
// ProfileService.Pb.ReqSystemMsgNew.Friend // ProfileService.Pb.ReqSystemMsgNew.Friend
func decodeSystemMsgFriendPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeSystemMsgFriendPacket(c *QQClient, pkt *network.Packet) (any, error) {
rsp := structmsg.RspSystemMsgNew{} rsp := structmsg.RspSystemMsgNew{}
if err := proto.Unmarshal(payload, &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 {
@ -754,7 +798,7 @@ func decodeSystemMsgFriendPacket(c *QQClient, _ *incomingPacketInfo, payload []b
} }
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,
@ -766,36 +810,33 @@ func decodeSystemMsgFriendPacket(c *QQClient, _ *incomingPacketInfo, payload []b
} }
// MessageSvc.PushForceOffline // MessageSvc.PushForceOffline
func decodeForceOfflinePacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeForceOfflinePacket(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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, _ *incomingPacketInfo, _ []byte) (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, _ *incomingPacketInfo, payload []byte) (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(payload, &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
@ -803,6 +844,21 @@ func decodeWordSegmentation(_ *QQClient, _ *incomingPacketInfo, payload []byte)
return nil, errors.New("no word received") return nil, errors.New("no word received")
} }
func decodeSidExpiredPacket(c *QQClient, pkt *network.Packet) (any, error) {
/*
_, err := c.sendAndWait(c.buildRequestChangeSigPacket(true))
if err != nil {
return nil, errors.Wrap(err, "resign client error")
}
if err = c.registerClient(); err != nil {
return nil, errors.Wrap(err, "register error")
}
_ = c.sendPacket(c.uniPacketWithSeq(i.SequenceId, "OnlinePush.SidTicketExpired", EmptyBytes))
*/
return nil, nil
}
/* unused
// LightAppSvc.mini_app_info.GetAppInfoById // LightAppSvc.mini_app_info.GetAppInfoById
func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) {
pkg := qweb.QWebRsp{} pkg := qweb.QWebRsp{}
@ -810,15 +866,16 @@ func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (
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.RetCode != 0 { if pkg.RetCode.Unwrap() != 0 {
return nil, errors.New(pkg.ErrMsg) 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")
} }
return rsp.AppInfo, nil return rsp.AppInfo, nil
} }
*/
func ignoreDecoder(_ *QQClient, _ *incomingPacketInfo, _ []byte) (interface{}, error) { func ignoreDecoder(_ *QQClient, _ *network.Packet) (any, error) {
return nil, nil return nil, nil
} }

View File

@ -4,6 +4,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary/jce" "github.com/Mrs4s/MiraiGo/binary/jce"
"github.com/Mrs4s/MiraiGo/client/internal/auth"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
) )
@ -22,10 +23,11 @@ type (
UserOnlineStatus int UserOnlineStatus int
ClientProtocol int ClientProtocol = auth.ProtocolType
LoginResponse struct { LoginResponse struct {
Success bool Success bool
Code byte
Error LoginError Error LoginError
// Captcha info // Captcha info
@ -76,6 +78,7 @@ type (
Mobile string Mobile string
LoginDays int64 LoginDays int64
Qid string Qid string
VipLevel string
} }
OtherClientInfo struct { OtherClientInfo struct {
@ -174,11 +177,6 @@ type (
client *QQClient client *QQClient
} }
LogEvent struct {
Type string
Message string
}
ServerUpdatedEvent struct { ServerUpdatedEvent struct {
Servers []jce.SsoServerInfo Servers []jce.SsoServerInfo
} }
@ -194,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"`
@ -221,15 +230,42 @@ type (
} }
GuildMessageReactionsUpdatedEvent struct { GuildMessageReactionsUpdatedEvent struct {
OperatorId uint64 // OperatorId 操作者TinyId, 删除贴表情的事件下不会有值 OperatorId uint64 // OperatorId 操作者TinyId, 删除贴表情的事件下不会有值
EmojiId int32 // EmojiId 被贴的表情, 只有自身消息被贴表情才会有值 EmojiId int32 // EmojiId 被贴的表情, 只有自身消息被贴表情才会有值
GuildId uint64 GuildId uint64
ChannelId uint64 ChannelId uint64
MessageId uint64 MessageId uint64
MessageSenderUin int64 // MessageSenderUin 被贴表情的消息发送者QQ号 // MessageSenderUin int64 // MessageSenderUin 被贴表情的消息发送者QQ号
CurrentReactions []*message.GuildMessageEmojiReaction CurrentReactions []*message.GuildMessageEmojiReaction
} }
GuildChannelUpdatedEvent struct {
OperatorId uint64
GuildId uint64
ChannelId uint64
OldChannelInfo *ChannelInfo
NewChannelInfo *ChannelInfo
}
GuildMessageRecalledEvent struct {
OperatorId uint64
GuildId uint64
ChannelId uint64
MessageId uint64
RecallTime int64
}
GuildChannelOperationEvent struct {
OperatorId uint64
GuildId uint64
ChannelInfo *ChannelInfo
}
MemberJoinGuildEvent struct {
Guild *GuildInfo
Member *GuildMemberInfo
}
OcrResponse struct { OcrResponse struct {
Texts []*TextDetection `json:"texts"` Texts []*TextDetection `json:"texts"`
Language string `json:"language"` Language string `json:"language"`
@ -257,33 +293,6 @@ type (
list []*GroupMemberInfo list []*GroupMemberInfo
} }
imageUploadResponse struct {
UploadKey []byte
UploadIp []uint32
UploadPort []uint32
ResourceId string
Message string
FileId int64
Width int32
Height int32
ResultCode int32
IsExists bool
}
pttUploadResponse struct {
ResultCode int32
Message string
IsExists bool
ResourceId string
UploadKey []byte
UploadIp []string
UploadPort []uint32
FileKey []byte
FileId int64
}
groupMessageReceiptEvent struct { groupMessageReceiptEvent struct {
Rand int32 Rand int32
Seq int32 Seq int32
@ -294,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
@ -342,11 +355,13 @@ const (
Administrator MemberPermission = 2 Administrator MemberPermission = 2
Member MemberPermission = 3 Member MemberPermission = 3
AndroidPhone ClientProtocol = 1 Unset = auth.Unset
IPad ClientProtocol = 2 AndroidPhone = auth.AndroidPhone
AndroidWatch ClientProtocol = 3 AndroidWatch = auth.AndroidWatch
MacOS ClientProtocol = 4 MacOS = auth.MacOS
QiDian ClientProtocol = 5 QiDian = auth.QiDian
IPad = auth.IPad
AndroidPad = auth.AndroidPad
) )
func (r *UserJoinGroupRequest) Accept() { func (r *UserJoinGroupRequest) Accept() {

View File

@ -2,72 +2,77 @@ 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)
groupMuteEventHandlers []func(*QQClient, *GroupMuteEvent) guildMessageRecalledHandlers []func(*QQClient, *GuildMessageRecalledEvent)
groupRecalledHandlers []func(*QQClient, *GroupMessageRecalledEvent) guildChannelUpdatedHandlers []func(*QQClient, *GuildChannelUpdatedEvent)
friendRecalledHandlers []func(*QQClient, *FriendMessageRecalledEvent) guildChannelCreatedHandlers []func(*QQClient, *GuildChannelOperationEvent)
joinGroupHandlers []func(*QQClient, *GroupInfo) guildChannelDestroyedHandlers []func(*QQClient, *GuildChannelOperationEvent)
leaveGroupHandlers []func(*QQClient, *GroupLeaveEvent) memberJoinedGuildHandlers []func(*QQClient, *MemberJoinGuildEvent)
memberJoinedHandlers []func(*QQClient, *MemberJoinGroupEvent)
memberLeavedHandlers []func(*QQClient, *MemberLeaveGroupEvent) serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool
memberCardUpdatedHandlers []func(*QQClient, *MemberCardUpdatedEvent) subscribedEventHandlers []any
groupNameUpdatedHandlers []func(*QQClient, *GroupNameUpdatedEvent) groupMessageReceiptHandlers sync.Map
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)) {
@ -78,99 +83,30 @@ func (s *GuildService) OnGuildMessageReactionsUpdated(f func(*QQClient, *GuildMe
s.c.eventHandlers.guildMessageReactionsUpdatedHandlers = append(s.c.eventHandlers.guildMessageReactionsUpdatedHandlers, f) s.c.eventHandlers.guildMessageReactionsUpdatedHandlers = append(s.c.eventHandlers.guildMessageReactionsUpdatedHandlers, f)
} }
func (c *QQClient) OnGroupMuted(f func(*QQClient, *GroupMuteEvent)) { func (s *GuildService) OnGuildMessageRecalled(f func(*QQClient, *GuildMessageRecalledEvent)) {
c.eventHandlers.groupMuteEventHandlers = append(c.eventHandlers.groupMuteEventHandlers, f) s.c.eventHandlers.guildMessageRecalledHandlers = append(s.c.eventHandlers.guildMessageRecalledHandlers, f)
} }
func (c *QQClient) OnJoinGroup(f func(*QQClient, *GroupInfo)) { func (s *GuildService) OnGuildChannelUpdated(f func(*QQClient, *GuildChannelUpdatedEvent)) {
c.eventHandlers.joinGroupHandlers = append(c.eventHandlers.joinGroupHandlers, f) s.c.eventHandlers.guildChannelUpdatedHandlers = append(s.c.eventHandlers.guildChannelUpdatedHandlers, f)
} }
func (c *QQClient) OnLeaveGroup(f func(*QQClient, *GroupLeaveEvent)) { func (s *GuildService) OnGuildChannelCreated(f func(*QQClient, *GuildChannelOperationEvent)) {
c.eventHandlers.leaveGroupHandlers = append(c.eventHandlers.leaveGroupHandlers, f) s.c.eventHandlers.guildChannelCreatedHandlers = append(s.c.eventHandlers.guildChannelCreatedHandlers, f)
} }
func (c *QQClient) OnGroupMemberJoined(f func(*QQClient, *MemberJoinGroupEvent)) { func (s *GuildService) OnGuildChannelDestroyed(f func(*QQClient, *GuildChannelOperationEvent)) {
c.eventHandlers.memberJoinedHandlers = append(c.eventHandlers.memberJoinedHandlers, f) s.c.eventHandlers.guildChannelDestroyedHandlers = append(s.c.eventHandlers.guildChannelDestroyedHandlers, f)
} }
func (c *QQClient) OnGroupMemberLeaved(f func(*QQClient, *MemberLeaveGroupEvent)) { func (s *GuildService) OnMemberJoinedGuild(f func(*QQClient, *MemberJoinGuildEvent)) {
c.eventHandlers.memberLeavedHandlers = append(c.eventHandlers.memberLeavedHandlers, f) s.c.eventHandlers.memberJoinedGuildHandlers = append(s.c.eventHandlers.memberJoinedGuildHandlers, 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
@ -185,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
@ -262,110 +143,55 @@ func (c *QQClient) dispatchGuildMessageReactionsUpdatedEvent(e *GuildMessageReac
} }
} }
func (c *QQClient) dispatchGroupMuteEvent(e *GroupMuteEvent) { func (c *QQClient) dispatchGuildMessageRecalledEvent(e *GuildMessageRecalledEvent) {
if e == nil { if e == nil {
return return
} }
for _, f := range c.eventHandlers.groupMuteEventHandlers { for _, f := range c.eventHandlers.guildMessageRecalledHandlers {
cover(func() { cover(func() {
f(c, e) f(c, e)
}) })
} }
} }
func (c *QQClient) dispatchGroupMessageRecalledEvent(e *GroupMessageRecalledEvent) { func (c *QQClient) dispatchGuildChannelUpdatedEvent(e *GuildChannelUpdatedEvent) {
if e == nil { if e == nil {
return return
} }
for _, f := range c.eventHandlers.groupRecalledHandlers { for _, f := range c.eventHandlers.guildChannelUpdatedHandlers {
cover(func() { cover(func() {
f(c, e) f(c, e)
}) })
} }
} }
func (c *QQClient) dispatchFriendMessageRecalledEvent(e *FriendMessageRecalledEvent) { func (c *QQClient) dispatchGuildChannelCreatedEvent(e *GuildChannelOperationEvent) {
if e == nil { if e == nil {
return return
} }
for _, f := range c.eventHandlers.friendRecalledHandlers { for _, f := range c.eventHandlers.guildChannelCreatedHandlers {
cover(func() { cover(func() {
f(c, e) f(c, e)
}) })
} }
} }
func (c *QQClient) dispatchJoinGroupEvent(group *GroupInfo) { func (c *QQClient) dispatchGuildChannelDestroyedEvent(e *GuildChannelOperationEvent) {
if group == nil {
return
}
for _, f := range c.eventHandlers.joinGroupHandlers {
cover(func() {
f(c, group)
})
}
}
func (c *QQClient) dispatchLeaveGroupEvent(e *GroupLeaveEvent) {
if e == nil { if e == nil {
return return
} }
for _, f := range c.eventHandlers.leaveGroupHandlers { for _, f := range c.eventHandlers.guildChannelDestroyedHandlers {
cover(func() { cover(func() {
f(c, e) f(c, e)
}) })
} }
} }
func (c *QQClient) dispatchNewMemberEvent(e *MemberJoinGroupEvent) { func (c *QQClient) dispatchMemberJoinedGuildEvent(e *MemberJoinGuildEvent) {
if e == nil { if e == nil {
return return
} }
for _, f := range c.eventHandlers.memberJoinedHandlers { for _, f := range c.eventHandlers.memberJoinedGuildHandlers {
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() { cover(func() {
f(c, e) f(c, e)
}) })
@ -373,144 +199,12 @@ func (c *QQClient) dispatchPermissionChanged(e *MemberPermissionChangedEvent) {
} }
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

@ -3,12 +3,11 @@ package client
import ( import (
"fmt" "fmt"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/faceroam" "github.com/Mrs4s/MiraiGo/client/pb/faceroam"
"github.com/Mrs4s/MiraiGo/internal/proto"
) )
type CustomFace struct { type CustomFace struct {
@ -29,24 +28,22 @@ func (c *QQClient) GetCustomFaces() ([]*CustomFace, error) {
} }
func (c *QQClient) buildFaceroamRequestPacket() (uint16, []byte) { func (c *QQClient) buildFaceroamRequestPacket() (uint16, []byte) {
seq := c.nextSeq()
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{},
}) })
packet := packets.BuildUniPacket(c.Uin, seq, "Faceroam.OpReq", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("Faceroam.OpReq", payload)
return seq, packet
} }
func decodeFaceroamResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeFaceroamResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := faceroam.FaceroamRspBody{} rsp := faceroam.FaceroamRspBody{}
if err := proto.Unmarshal(payload, &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 {
@ -56,7 +53,7 @@ func decodeFaceroamResponse(c *QQClient, _ *incomingPacketInfo, payload []byte)
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,454 +2,126 @@ package client
import ( import (
"crypto/md5" "crypto/md5"
crand "crypto/rand"
"encoding/hex" "encoding/hex"
"encoding/json"
"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"
"google.golang.org/protobuf/proto"
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/binary/jce" "github.com/Mrs4s/MiraiGo/binary/jce"
devinfo "github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/internal/auth"
"github.com/Mrs4s/MiraiGo/client/pb/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
type ( type (
DeviceInfo struct { DeviceInfo = auth.Device
Display []byte Version = auth.OSVersion
Product []byte
Device []byte
Board []byte
Brand []byte
Model []byte
Bootloader []byte
FingerPrint []byte
BootId []byte
ProcVersion []byte
BaseBand []byte
SimInfo []byte
OSType []byte
MacAddress []byte
IpAddress []byte
WifiBSSID []byte
WifiSSID []byte
IMSIMd5 []byte
IMEI string
AndroidId []byte
APN []byte
VendorName []byte
VendorOSName []byte
Guid []byte
TgtgtKey []byte
Protocol ClientProtocol
Version *Version
}
Version struct {
Incremental []byte
Release []byte
CodeName []byte
Sdk uint32
}
DeviceInfoFile struct {
Display string `json:"display"`
Product string `json:"product"`
Device string `json:"device"`
Board string `json:"board"`
Model string `json:"model"`
FingerPrint string `json:"finger_print"`
BootId string `json:"boot_id"`
ProcVersion string `json:"proc_version"`
Protocol int `json:"protocol"` // 0: Pad 1: Phone 2: Watch
IMEI string `json:"imei"`
Brand string `json:"brand"`
Bootloader string `json:"bootloader"`
BaseBand string `json:"base_band"`
Version *VersionFile `json:"version"`
SimInfo string `json:"sim_info"`
OsType string `json:"os_type"`
MacAddress string `json:"mac_address"`
IpAddress []int32 `json:"ip_address"`
WifiBSSID string `json:"wifi_bssid"`
WifiSSID string `json:"wifi_ssid"`
ImsiMd5 string `json:"imsi_md5"`
AndroidId string `json:"android_id"`
Apn string `json:"apn"`
VendorName string `json:"vendor_name"`
VendorOSName string `json:"vendor_os_name"`
}
VersionFile struct {
Incremental string `json:"incremental"`
Release string `json:"release"`
Codename string `json:"codename"`
Sdk uint32 `json:"sdk"`
}
groupMessageBuilder struct {
MessageSlices []*msg.Message
}
versionInfo struct {
ApkSign []byte
ApkId string
SortVersionName string
SdkVersion string
AppId uint32
SubAppId uint32
BuildTime uint32
SSOVersion uint32
MiscBitmap uint32
SubSigmap uint32
MainSigMap uint32
Protocol ClientProtocol
}
incomingPacketInfo struct {
CommandName string
SequenceId uint16
Params requestParams
}
requestParams map[string]interface{}
) )
var SystemDeviceInfo = &DeviceInfo{ var EmptyBytes = make([]byte, 0)
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: []byte{},
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 ( func GenRandomDevice() *DeviceInfo {
EmptyBytes = []byte{}
NumberRange = "0123456789"
)
func init() {
r := make([]byte, 16) r := make([]byte, 16)
rand.Read(r) crand.Read(r)
t := md5.Sum(r) const numberRange = "0123456789"
SystemDeviceInfo.IMSIMd5 = t[:]
SystemDeviceInfo.GenNewGuid()
SystemDeviceInfo.GenNewTgtgtKey()
}
func GenRandomDevice() { var device = &DeviceInfo{
r := make([]byte, 16) Product: []byte("mirai"),
rand.Read(r) Device: []byte("mirai"),
SystemDeviceInfo.Display = []byte("MIRAI." + utils.RandomStringRange(6, NumberRange) + ".001") Board: []byte("mirai"),
SystemDeviceInfo.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, NumberRange) + ":user/release-keys") Brand: []byte("mamoe"),
SystemDeviceInfo.BootId = binary.GenUUID(r) Model: []byte("mirai"),
SystemDeviceInfo.ProcVersion = []byte("Linux version 3.0.31-" + utils.RandomString(8) + " (android-build@xxx.xxx.xxx.xxx.com)") Bootloader: []byte("unknown"),
rand.Read(r) BootId: []byte("cb886ae2-00b6-4d68-a230-787f111d12c7"),
t := md5.Sum(r) ProcVersion: []byte("Linux version 3.0.31-cb886ae2 (android-build@xxx.xxx.xxx.xxx.com)"),
SystemDeviceInfo.IMSIMd5 = t[:] BaseBand: EmptyBytes,
SystemDeviceInfo.IMEI = GenIMEI() SimInfo: []byte("T-Mobile"),
r = make([]byte, 8) OSType: []byte("android"),
rand.Read(r) MacAddress: []byte("00:50:56:C0:00:08"),
hex.Encode(SystemDeviceInfo.AndroidId, r) IpAddress: []byte{10, 0, 1, 3}, // 10.0.1.3
SystemDeviceInfo.GenNewGuid() WifiBSSID: []byte("00:50:56:C0:00:08"),
SystemDeviceInfo.GenNewTgtgtKey() WifiSSID: []byte("<unknown ssid>"),
} IMEI: "468356291846738",
AndroidId: []byte("MIRAI.123456.001"),
func genVersionInfo(p ClientProtocol) *versionInfo { APN: []byte("wifi"),
switch p { VendorName: []byte("MIUI"),
case AndroidPhone: // Dumped by mirai from qq android v8.8.38 VendorOSName: []byte("mirai"),
return &versionInfo{ Protocol: AndroidPad,
ApkId: "com.tencent.mobileqq", Version: &Version{
AppId: 537100432, Incremental: []byte("5891938"),
SubAppId: 537100432, Release: []byte("10"),
SortVersionName: "8.8.38", CodeName: []byte("REL"),
BuildTime: 1634310940, SDK: 29,
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: p,
}
case AndroidWatch:
return &versionInfo{
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: p,
}
case IPad:
return &versionInfo{
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: p,
}
case MacOS:
return &versionInfo{
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: p,
}
case QiDian:
return &versionInfo{
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: p,
}
}
return nil
}
func (info *DeviceInfo) ToJson() []byte {
f := &DeviceInfoFile{
Display: string(info.Display),
Product: string(info.Product),
Device: string(info.Device),
Board: string(info.Board),
Model: string(info.Model),
FingerPrint: string(info.FingerPrint),
BootId: string(info.BootId),
ProcVersion: string(info.ProcVersion),
IMEI: info.IMEI,
Brand: string(info.Brand),
Bootloader: string(info.Bootloader),
BaseBand: string(info.BaseBand),
AndroidId: string(info.AndroidId),
Version: &VersionFile{
Incremental: string(info.Version.Incremental),
Release: string(info.Version.Release),
Codename: string(info.Version.CodeName),
Sdk: info.Version.Sdk,
}, },
SimInfo: string(info.SimInfo),
OsType: string(info.OSType),
MacAddress: string(info.MacAddress),
IpAddress: []int32{int32(info.IpAddress[0]), int32(info.IpAddress[1]), int32(info.IpAddress[2]), int32(info.IpAddress[3])},
WifiBSSID: string(info.WifiBSSID),
WifiSSID: string(info.WifiSSID),
ImsiMd5: hex.EncodeToString(info.IMSIMd5),
Apn: string(info.APN),
VendorName: string(info.VendorName),
VendorOSName: string(info.VendorOSName),
Protocol: func() int {
switch info.Protocol {
case IPad:
return 0
case AndroidPhone:
return 1
case AndroidWatch:
return 2
case MacOS:
return 3
case QiDian:
return 4
}
return 0
}(),
}
d, _ := json.Marshal(f)
return d
}
func (info *DeviceInfo) ReadJson(d []byte) error {
var f DeviceInfoFile
if err := json.Unmarshal(d, &f); err != nil {
return errors.Wrap(err, "failed to unmarshal json message")
}
SetIfNotEmpty := func(trg *[]byte, str string) {
if str != "" {
*trg = []byte(str)
}
}
SetIfNotEmpty(&info.Display, f.Display)
SetIfNotEmpty(&info.Product, f.Product)
SetIfNotEmpty(&info.Device, f.Device)
SetIfNotEmpty(&info.Board, f.Board)
SetIfNotEmpty(&info.Brand, f.Brand)
SetIfNotEmpty(&info.Model, f.Model)
SetIfNotEmpty(&info.Bootloader, f.Bootloader)
SetIfNotEmpty(&info.FingerPrint, f.FingerPrint)
SetIfNotEmpty(&info.BootId, f.BootId)
SetIfNotEmpty(&info.ProcVersion, f.ProcVersion)
SetIfNotEmpty(&info.BaseBand, f.BaseBand)
SetIfNotEmpty(&info.SimInfo, f.SimInfo)
SetIfNotEmpty(&info.OSType, f.OsType)
SetIfNotEmpty(&info.MacAddress, f.MacAddress)
if len(f.IpAddress) == 4 {
info.IpAddress = []byte{byte(f.IpAddress[0]), byte(f.IpAddress[1]), byte(f.IpAddress[2]), byte(f.IpAddress[3])}
}
SetIfNotEmpty(&info.WifiBSSID, f.WifiBSSID)
SetIfNotEmpty(&info.WifiSSID, f.WifiSSID)
if len(f.ImsiMd5) != 0 {
imsiMd5, err := hex.DecodeString(f.ImsiMd5)
if err != nil {
info.IMSIMd5 = imsiMd5
}
}
if f.IMEI != "" {
info.IMEI = f.IMEI
}
SetIfNotEmpty(&info.APN, f.Apn)
SetIfNotEmpty(&info.VendorName, f.VendorName)
SetIfNotEmpty(&info.VendorOSName, f.VendorOSName)
SetIfNotEmpty(&info.AndroidId, f.AndroidId)
if f.AndroidId == "" {
info.AndroidId = info.Display // ?
} }
switch f.Protocol { device.Display = []byte("MIRAI." + utils.RandomStringRange(6, numberRange) + ".001")
case 1: device.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, numberRange) + ":user/release-keys")
info.Protocol = AndroidPhone device.BootId = binary.GenUUID(r)
case 2: device.ProcVersion = []byte("Linux version 3.0.31-" + utils.RandomString(8) + " (android-build@xxx.xxx.xxx.xxx.com)")
info.Protocol = AndroidWatch crand.Read(r)
case 3: t := md5.Sum(r)
info.Protocol = MacOS device.IMSIMd5 = t[:]
case 4: device.IMEI = GenIMEI()
info.Protocol = QiDian r = make([]byte, 8)
default: crand.Read(r)
info.Protocol = IPad hex.Encode(device.AndroidId, r)
} device.GenNewGuid()
SystemDeviceInfo.GenNewGuid() device.GenNewTgtgtKey()
SystemDeviceInfo.GenNewTgtgtKey() device.RequestQImei()
return nil return device
}
func (info *DeviceInfo) GenNewGuid() {
t := md5.Sum(append(info.AndroidId, info.MacAddress...))
info.Guid = t[:]
}
func (info *DeviceInfo) GenNewTgtgtKey() {
r := make([]byte, 16)
rand.Read(r)
h := md5.New()
h.Write(r)
h.Write(info.Guid)
info.TgtgtKey = h.Sum(nil)
}
func (info *DeviceInfo) GenDeviceInfoData() []byte {
m := &devinfo.DeviceInfo{
Bootloader: string(info.Bootloader),
ProcVersion: string(info.ProcVersion),
Codename: string(info.Version.CodeName),
Incremental: string(info.Version.Incremental),
Fingerprint: string(info.FingerPrint),
BootId: string(info.BootId),
AndroidId: string(info.AndroidId),
BaseBand: string(info.BaseBand),
InnerVersion: string(info.Version.Incremental),
}
data, err := proto.Marshal(m)
if err != nil {
panic(errors.Wrap(err, "failed to unmarshal protobuf message"))
}
return data
} }
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 := genVersionInfo(SystemDeviceInfo.Protocol) 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)},
} }
@ -459,12 +131,15 @@ func getSSOAddress() ([]*net.TCPAddr, error) {
SFuncName: "HttpServerListReq", SFuncName: "HttpServerListReq",
SBuffer: buf.ToBytes(), SBuffer: buf.ToBytes(),
} }
tea := binary.NewTeaCipher(key) b, cl := binary.OpenWriterF(func(w *binary.Writer) {
rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp", tea.Encrypt(binary.NewWriterF(func(w *binary.Writer) {
w.WriteIntLvPacket(0, func(w *binary.Writer) { w.WriteIntLvPacket(0, func(w *binary.Writer) {
w.Write(pkt.ToBytes()) w.Write(pkt.ToBytes())
}) })
}))) })
tea := binary.NewTeaCipher(key)
encpkt := tea.Encrypt(b)
cl()
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")
} }
@ -473,17 +148,16 @@ func getSSOAddress() ([]*net.TCPAddr, error) {
rspPkt.ReadFrom(jce.NewJceReader(tea.Decrypt(rsp)[4:])) rspPkt.ReadFrom(jce.NewJceReader(tea.Decrypt(rsp)[4:]))
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 := []jce.SsoServerInfo{} servers := reader.ReadSsoServerInfos(2)
reader.ReadSlice(&servers, 2) adds := make([]netip.AddrPort, 0, len(servers))
adds := make([]*net.TCPAddr, 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
} }
@ -496,47 +170,46 @@ func qualityTest(addr string) (int64, error) {
return 0, errors.Wrap(err, "failed to connect to server during quality test") return 0, errors.Wrap(err, "failed to connect to server during quality test")
} }
_ = conn.Close() _ = conn.Close()
end := time.Now() return time.Since(start).Milliseconds(), nil
return end.Sub(start).Milliseconds(), nil
} }
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),
}, // },
} // }
} // }
return message.ParseMessageElems(msg.Body.RichText.Elems) return message.ParseMessageElems(msg.Body.RichText.Elems)
}(), }(),
} }
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
} }
@ -544,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,
@ -569,32 +242,59 @@ 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
} }
func packUniRequestData(data []byte) (r []byte) { func packUniRequestData(data []byte) []byte {
r = append([]byte{0x0A}, data...) r := make([]byte, 0, len(data)+2)
r = append(r, 0x0a)
r = append(r, data...)
r = append(r, 0x0B) r = append(r, 0x0B)
return 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,
@ -603,58 +303,40 @@ func genForwardTemplate(resID, preview, title, brief, source, summary string, ts
} }
} }
func genLongTemplate(resID, brief string, ts int64) *message.ServiceElement { func (c *QQClient) getWebDeviceInfo() (i string) {
limited := func() string { qimei := strings.ToLower(utils.RandomString(36))
if len(brief) > 30 { 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))
return brief[:30] + "…" 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.Device().Brand, 5629 /* Total Memory*/, 1725 /* CPU Frequency */, 8 /* CPU Core Count */)
return brief 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"
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>`, return
utils.XmlEscape(limited), resID, ts, utils.XmlEscape(limited),
)
return &message.ServiceElement{
Id: 35,
Content: template,
ResId: resID,
SubType: "Long",
}
} }
func (p requestParams) bool(k string) bool { var oidbSSOPool = sync.Pool{}
if p == nil {
return false
}
i, ok := p[k]
if !ok {
return false
}
return i.(bool)
}
func (p requestParams) int32(k string) int32 { func getOidbSSOPackage() *oidb.OIDBSSOPkg {
if p == nil { g := oidbSSOPool.Get()
return 0 if g == nil {
return new(oidb.OIDBSSOPkg)
} }
i, ok := p[k] return g.(*oidb.OIDBSSOPkg)
if !ok {
return 0
}
return i.(int32)
} }
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())
} }
@ -663,51 +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 (c *QQClient) 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...),
})
}

View File

@ -1,23 +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/Mrs4s/MiraiGo/internal/packets"
"google.golang.org/protobuf/proto"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/pb/exciting" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/message"
) )
type ( type (
@ -56,13 +50,13 @@ type (
} }
) )
var fsWaiter = utils.NewUploadWaiter()
func init() { func init() {
decoders["OidbSvc.0x6d8_1"] = decodeOIDB6d81Response decoders["OidbSvc.0x6d8_1"] = decodeOIDB6d81Response
decoders["OidbSvc.0x6d6_0"] = decodeOIDB6d60Response decoders["OidbSvc.0x6d6_0"] = decodeOIDB6d60Response
decoders["OidbSvc.0x6d6_2"] = decodeOIDB6d62Response decoders["OidbSvc.0x6d6_2"] = decodeOIDB6d62Response
decoders["OidbSvc.0x6d6_3"] = decodeOIDB6d63Response decoders["OidbSvc.0x6d6_3"] = decodeOIDB6d63Response
decoders["OidbSvc.0x6d6_4"] = decodeOIDB6d64Response
decoders["OidbSvc.0x6d6_5"] = decodeOIDB6d65Response
decoders["OidbSvc.0x6d7_0"] = decodeOIDB6d7Response decoders["OidbSvc.0x6d7_0"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d7_1"] = decodeOIDB6d7Response decoders["OidbSvc.0x6d7_1"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d7_2"] = decodeOIDB6d7Response decoders["OidbSvc.0x6d7_2"] = decodeOIDB6d7Response
@ -72,7 +66,7 @@ func init() {
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")
} }
}() }()
@ -85,8 +79,8 @@ func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err
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,
} }
@ -94,8 +88,8 @@ func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err
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
} }
@ -105,7 +99,7 @@ func (c *QQClient) GetGroupFileUrl(groupCode int64, fileId string, busId int32)
return "" return ""
} }
url := i.(string) url := i.(string)
url += "?fname=" + hex.EncodeToString([]byte(fileId)) url += fmt.Sprintf("?fname=%x", fileId)
return url return url
} }
@ -130,108 +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)
i, err := fs.client.sendAndWait(fs.client.buildGroupFileUploadReqPacket(folderId, name, fs.GroupCode, size, md5Hash, sha1Hash))
if err != nil {
return errors.Wrap(err, "query upload failed")
} }
rsp := i.(*oidb.UploadFileRspBody) target := message.Source{
if rsp.BoolFileExist { SourceType: message.SourceGroup,
_, pkt := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.FileId, rsp.BusId, rand.Int31()) PrimaryID: fs.GroupCode,
return fs.client.sendPacket(pkt)
} }
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.FileId),
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),
})
if _, err = fs.client.excitingUploadStream(file, 71, fs.client.bigDataSession.SigSession, ext); err != nil {
return errors.Wrap(err, "upload failed")
}
_, pkt := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.FileId, rsp.BusId, rand.Int31())
return fs.client.sendPacket(pkt)
} }
func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string { func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string {
@ -269,265 +209,274 @@ func (fs *GroupFileSystem) DeleteFile(parentFolderID, fileId string, busId int32
return i.(string) return i.(string)
} }
func (c *QQClient) buildGroupFileUploadReqPacket(parentFolderID, fileName string, groupCode, fileSize int64, md5, sha1 []byte) (uint16, []byte) { // RenameFile 重命名群文件,需要管理权限或者是自己发的文件.
seq := c.nextSeq() // 返回错误, 空为重命名成功
b, _ := proto.Marshal(&oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{ func (fs *GroupFileSystem) RenameFile(parentFolderID, fileId string, busId int32, newFileName string) string {
GroupCode: groupCode, i, err := fs.client.sendAndWait(fs.client.buildGroupFileRenameReqPacket(fs.GroupCode, parentFolderID, fileId, busId, newFileName))
AppId: 3, if err != nil {
BusId: 102, return err.Error()
Entrance: 5,
ParentFolderId: parentFolderID,
FileName: fileName,
LocalPath: "/storage/emulated/0/Pictures/files/s/" + fileName,
Int64FileSize: fileSize,
Sha: sha1,
Md5: md5,
SupportMultiUpload: true,
}})
req := &oidb.OIDBSSOPkg{
Command: 1750,
ServiceType: 0,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
} }
payload, _ := proto.Marshal(req) return i.(string)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d6_0", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) }
return seq, packet
// 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),
BusId: proto.Int32(102),
Entrance: proto.Int32(5),
ParentFolderId: proto.Some(file.RemoteFolder),
FileName: proto.Some(file.FileName),
LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + file.FileName),
Int64FileSize: proto.Some(file.size),
Sha: file.sha1,
Md5: file.md5,
SupportMultiUpload: proto.Bool(true),
}}
payload := c.packOIDBPackageProto(1750, 0, body)
return c.uniPacket("OidbSvc.0x6d6_0", payload)
} }
func (c *QQClient) buildGroupFileFeedsRequest(groupCode int64, fileID string, busId, msgRand int32) (uint16, []byte) { func (c *QQClient) buildGroupFileFeedsRequest(groupCode int64, fileID string, busId, msgRand int32) (uint16, []byte) {
seq := c.nextSeq()
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)),
}}, }},
}}) }})
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d9_4", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, req) return c.uniPacket("OidbSvc.0x6d9_4", req)
return seq, packet
} }
// OidbSvc.0x6d8_1 // OidbSvc.0x6d8_1
func (c *QQClient) buildGroupFileListRequestPacket(groupCode int64, folderID string, startIndex uint32) (uint16, []byte) { func (c *QQClient) buildGroupFileListRequestPacket(groupCode int64, folderID string, startIndex uint32) (uint16, []byte) {
seq := c.nextSeq()
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)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d8_1", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
} }
func (c *QQClient) buildGroupFileCountRequestPacket(groupCode int64) (uint16, []byte) { func (c *QQClient) buildGroupFileCountRequestPacket(groupCode int64) (uint16, []byte) {
seq := c.nextSeq() body := &oidb.D6D8ReqBody{
body := &oidb.D6D8ReqBody{GroupFileCountReq: &oidb.GetFileCountReqBody{ GroupFileCountReq: &oidb.GetFileCountReqBody{
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3), AppId: proto.Uint32(3),
BusId: proto.Uint32(0), 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)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d8_1", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("OidbSvc.0x6d8_1", payload)
return seq, packet
} }
func (c *QQClient) buildGroupFileSpaceRequestPacket(groupCode int64) (uint16, []byte) { func (c *QQClient) buildGroupFileSpaceRequestPacket(groupCode int64) (uint16, []byte) {
seq := c.nextSeq()
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)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d8_1", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
} }
func (c *QQClient) buildGroupFileCreateFolderPacket(groupCode int64, parentFolder, name string) (uint16, []byte) { func (c *QQClient) buildGroupFileCreateFolderPacket(groupCode int64, parentFolder, name string) (uint16, []byte) {
seq := c.nextSeq()
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),
}}) }})
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d7_0", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("OidbSvc.0x6d7_0", payload)
return seq, packet
} }
func (c *QQClient) buildGroupFileRenameFolderPacket(groupCode int64, folderId, newName string) (uint16, []byte) { func (c *QQClient) buildGroupFileRenameFolderPacket(groupCode int64, folderId, newName string) (uint16, []byte) {
seq := c.nextSeq()
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),
}}) }})
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d7_2", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("OidbSvc.0x6d7_2", payload)
return seq, packet
} }
func (c *QQClient) buildGroupFileDeleteFolderPacket(groupCode int64, folderId string) (uint16, []byte) { func (c *QQClient) buildGroupFileDeleteFolderPacket(groupCode int64, folderId string) (uint16, []byte) {
seq := c.nextSeq()
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),
}}) }})
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d7_1", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("OidbSvc.0x6d7_1", payload)
return seq, packet
} }
// OidbSvc.0x6d6_2 // OidbSvc.0x6d6_2
func (c *QQClient) buildGroupFileDownloadReqPacket(groupCode int64, fileId string, busId int32) (uint16, []byte) { func (c *QQClient) buildGroupFileDownloadReqPacket(groupCode int64, fileId string, busId int32) (uint16, []byte) {
seq := c.nextSeq()
body := &oidb.D6D6ReqBody{ body := &oidb.D6D6ReqBody{
DownloadFileReq: &oidb.DownloadFileReqBody{ DownloadFileReq: &oidb.DownloadFileReqBody{
GroupCode: groupCode, GroupCode: proto.Some(groupCode),
AppId: 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)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d6_2", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
} }
func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId, fileId string, busId int32) (uint16, []byte) { func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId, fileId string, busId int32) (uint16, []byte) {
seq := c.nextSeq()
body := &oidb.D6D6ReqBody{DeleteFileReq: &oidb.DeleteFileReqBody{ body := &oidb.D6D6ReqBody{DeleteFileReq: &oidb.DeleteFileReqBody{
GroupCode: groupCode, GroupCode: proto.Some(groupCode),
AppId: 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)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x6d6_3", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
} }
func decodeOIDB6d81Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (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(payload, &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, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeOIDB6d62Response(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D6RspBody{} rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &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.ClientWording) return nil, errors.New(rsp.DownloadFileRsp.ClientWording.Unwrap())
} }
ip := rsp.DownloadFileRsp.DownloadIp 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, _ *incomingPacketInfo, payload []byte) (interface{}, error) { // GroupFileDeleteRespPacket
pkg := oidb.OIDBSSOPkg{} func decodeOIDB6d63Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{} rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &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.ClientWording, nil
} }
func decodeOIDB6d60Response(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { // GroupFileUploadRespPacket
pkg := oidb.OIDBSSOPkg{} func decodeOIDB6d60Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{} rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &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, _ *incomingPacketInfo, payload []byte) (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(payload, &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

@ -1,23 +1,24 @@
package client package client
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/rand" "math/rand"
"net/http"
"net/url" "net/url"
"sort" "sort"
"strings" "strings"
"sync" "sync"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"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/network"
"github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/client/pb/profilecard" "github.com/Mrs4s/MiraiGo/client/pb/profilecard"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
@ -26,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
@ -42,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 通过搜索得到的群信息
@ -79,9 +78,8 @@ func (c *QQClient) GetGroupInfo(groupCode int64) (*GroupInfo, error) {
// OidbSvc.0x88d_0 // OidbSvc.0x88d_0
func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte) { func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte) {
seq := c.nextSeq()
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)),
@ -116,14 +114,8 @@ func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte)
}, },
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)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x88d_0", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
return seq, packet
} }
// SearchGroupByKeyword 通过关键词搜索陌生群组 // SearchGroupByKeyword 通过关键词搜索陌生群组
@ -137,7 +129,6 @@ func (c *QQClient) SearchGroupByKeyword(keyword string) ([]GroupSearchInfo, erro
// SummaryCard.ReqSearch // SummaryCard.ReqSearch
func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) { func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
seq := c.nextSeq()
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()),
@ -149,7 +140,7 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
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),
@ -186,14 +177,13 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
Context: make(map[string]string), Context: make(map[string]string),
Status: make(map[string]string), Status: make(map[string]string),
} }
packet := packets.BuildUniPacket(c.Uin, seq, "SummaryCard.ReqSearch", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes()) return c.uniPacket("SummaryCard.ReqSearch", pkt.ToBytes())
return seq, packet
} }
// SummaryCard.ReqSearch // SummaryCard.ReqSearch
func decodeGroupSearchResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGroupSearchResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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 {
@ -201,7 +191,8 @@ func decodeGroupSearchResponse(_ *QQClient, _ *incomingPacketInfo, payload []byt
} }
rsp := data.Map["RespSearch"]["SummaryCard.RespSearch"][1:] rsp := data.Map["RespSearch"]["SummaryCard.RespSearch"][1:]
r := jce.NewJceReader(rsp) r := jce.NewJceReader(rsp)
rspService := r.ReadAny(2).([]interface{})[0].([]byte) // rspService := r.ReadAny(2).([]interface{})[0].([]byte)
rspService := r.ReadByteArrArr(2)[0]
sr := binary.NewReader(rspService) sr := binary.NewReader(rspService)
sr.ReadByte() sr.ReadByte()
ld1 := sr.ReadInt32() ld1 := sr.ReadInt32()
@ -215,11 +206,11 @@ func decodeGroupSearchResponse(_ *QQClient, _ *incomingPacketInfo, payload []byt
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
@ -228,14 +219,11 @@ func decodeGroupSearchResponse(_ *QQClient, _ *incomingPacketInfo, payload []byt
} }
// OidbSvc.0x88d_0 // OidbSvc.0x88d_0
func decodeGroupInfoResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGroupInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D88DRspBody{} rsp := oidb.D88DRspBody{}
if err := proto.Unmarshal(payload, &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))
@ -245,21 +233,34 @@ func decodeGroupInfoResponse(c *QQClient, _ *incomingPacketInfo, payload []byte)
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
} }
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",
c.getSKey(), c.Uin, groupCode, len(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["Content-Type"] = []string{"multipart/form-data;boundary=****"}
rsp, err := http.DefaultClient.Do(req)
if err != nil {
return errors.Wrap(err, "failed to upload group head portrait")
}
rsp.Body.Close()
return nil
}
func (g *GroupInfo) UpdateName(newName string) { func (g *GroupInfo) UpdateName(newName string) {
if g.AdministratorOrOwner() && newName != "" && strings.Count(newName, "") <= 20 { if g.AdministratorOrOwner() && newName != "" && strings.Count(newName, "") <= 20 {
g.client.updateGroupName(g.Code, newName) g.client.updateGroupName(g.Code, newName)
@ -267,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)
@ -324,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 {
@ -356,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)
@ -395,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")
@ -414,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,24 +4,23 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"math" "math"
"math/rand" "math/rand"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/Mrs4s/MiraiGo/internal/packets" "github.com/pkg/errors"
"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"
"github.com/Mrs4s/MiraiGo/client/pb/oidb" "github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
) )
func init() { func init() {
@ -34,40 +33,18 @@ func init() {
} }
// 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 > 5000 || imgCount > 50 { if msgLen > message.MaxMessageSize || imgCount > 50 {
return nil return nil
} }
if !useFram && (msgLen > 100 || imgCount > 2) {
ret := c.sendGroupMessage(groupCode, false,
&message.SendingMessage{Elements: []message.IMessageElement{
c.uploadGroupLongMessage(groupCode,
message.NewForwardMessage().AddNode(&message.ForwardNode{
SenderId: c.Uin,
SenderName: c.Nickname,
Time: int32(time.Now().Unix()),
Message: m.Elements,
}),
),
}},
)
ret.Elements = m.Elements
return ret
}
return c.sendGroupMessage(groupCode, false, m) return c.sendGroupMessage(groupCode, false, m)
} }
@ -81,7 +58,7 @@ 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) {
seq, pkt := c.buildGetGroupMsgRequest(groupCode, beginSeq, endSeq) seq, pkt := c.buildGetGroupMsgRequest(groupCode, beginSeq, endSeq)
i, err := c.sendAndWait(seq, pkt, requestParams{"raw": false}) i, err := c.sendAndWait(seq, pkt, network.RequestParams{"raw": false})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,7 +96,7 @@ 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 {
@ -161,51 +138,6 @@ func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.Se
} }
} }
func (c *QQClient) uploadGroupLongMessage(groupCode int64, m *message.ForwardMessage) *message.ServiceElement {
if m.Length() >= 200 {
return nil
}
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 {
c.Error("upload long message error: %v", err)
return nil
}
for i, ip := range rsp.Uint32UpIp {
err := c.highwayUpload(uint32(ip), int(rsp.Uint32UpPort[i]), rsp.MsgSig, body, 27)
if err != nil {
c.Error("highway upload long message error: %v", err)
continue
}
return genLongTemplate(rsp.MsgResid, m.Brief(), ts)
}
c.Error("upload long message error: highway server list is empty or not available server.")
return nil
}
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 {
err := c.highwayUpload(uint32(ip), int(rsp.Uint32UpPort[i]), rsp.MsgSig, body, 27)
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.sendAndWait(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 {
@ -231,7 +163,6 @@ func (c *QQClient) multiMsgApplyUp(groupCode int64, data []byte, hash []byte, bu
// MessageSvc.PbSendMsg // MessageSvc.PbSendMsg
func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex, pkgDiv int32, forward bool, m []message.IMessageElement) (uint16, []byte) { func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex, pkgDiv int32, forward bool, m []message.IMessageElement) (uint16, []byte) {
seq := c.nextSeq()
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 {
@ -240,8 +171,8 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
} }
} }
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),
@ -254,7 +185,7 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
}, },
}, },
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 {
@ -265,12 +196,10 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
}(), }(),
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbSendMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("MessageSvc.PbSendMsg", payload)
return seq, packet
} }
func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) (uint16, []byte) { func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) (uint16, []byte) {
seq := c.nextSeq()
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,12 +207,10 @@ func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) (u
PublicGroup: proto.Bool(false), PublicGroup: proto.Bool(false),
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbGetGroupMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("MessageSvc.PbGetGroupMsg", payload)
return seq, packet
} }
func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byte) { func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byte) {
seq := c.nextSeq()
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),
@ -291,99 +218,94 @@ func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byt
Uin: proto.Uint64(uint64(c.Uin)), Uin: proto.Uint64(uint64(c.Uin)),
GroupCode: proto.Uint64(uint64(groupCode)), GroupCode: proto.Uint64(uint64(groupCode)),
}) })
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0x8a7_0", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("OidbSvc.0x8a7_0", payload)
return seq, packet
} }
// OnlinePush.PbPushGroupMsg // OnlinePush.PbPushGroupMsg
func decodeGroupMessagePacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGroupMessagePacket(c *QQClient, packet *network.Packet) (any, error) {
pkt := msg.PushMessagePacket{} pkt := msg.PushMessagePacket{}
err := proto.Unmarshal(payload, &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 decodeMsgSendResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeMsgSendResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := msg.SendMessageResponse{} rsp := msg.SendMessageResponse{}
if err := proto.Unmarshal(payload, &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")
} }
switch rsp.GetResult() { switch rsp.Result.Unwrap() {
case 0: // OK. case 0: // OK.
case 46:
c.error("sendPacket msg error: 需要使用安全设备验证")
case 55: case 55:
c.Error("sendPacket msg error: %v Bot has blocked target's content", rsp.GetResult()) c.error("sendPacket msg error: %v Bot has been blocked ta.'s content", rsp.Result.Unwrap())
default: default:
c.Error("sendPacket msg error: %v %v", rsp.GetResult(), rsp.GetErrMsg()) c.error("sendPacket msg error: %v %v", rsp.Result.Unwrap(), rsp.ErrMsg.Unwrap())
} }
return nil, nil return nil, nil
} }
func decodeGetGroupMsgResponse(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGetGroupMsgResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := msg.GetGroupMsgResp{} rsp := msg.GetGroupMsgResp{}
if err := proto.Unmarshal(payload, &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 { if rsp.Result.Unwrap() != 0 {
c.Error("get msg error: %v %v", rsp.GetResult(), rsp.GetErrmsg()) c.error("get msg error: %v %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
return nil, errors.Errorf("get msg error: %v msg: %v", rsp.GetResult(), rsp.GetErrmsg()) 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 && !info.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())))
seq, pkt := c.buildGetGroupMsgRequest(m.Head.GroupInfo.GetGroupCode(), int64(i), int64(end)) seq, pkt := c.buildGetGroupMsgRequest(m.Head.GroupInfo.GroupCode.Unwrap(), int64(i), int64(end))
data, err := c.sendAndWait(seq, pkt, requestParams{"raw": true}) data, err := c.sendAndWait(seq, pkt, network.RequestParams{"raw": true})
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
@ -401,29 +323,26 @@ func decodeGetGroupMsgResponse(c *QQClient, info *incomingPacketInfo, payload []
return ret, nil return ret, nil
} }
func decodeAtAllRemainResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeAtAllRemainResponse(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D8A7RspBody{} rsp := oidb.D8A7RspBody{}
if err := proto.Unmarshal(payload, &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
@ -432,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
@ -455,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,
}) })
@ -486,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,
} }
@ -498,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,
} }
@ -516,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 {
@ -536,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,
@ -545,17 +464,49 @@ 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
} }
@ -567,8 +518,8 @@ func (c *QQClient) SetEssenceMessage(groupCode int64, msgID, msgInternalId int32
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
} }
@ -580,33 +531,28 @@ func (c *QQClient) DeleteEssenceMessage(groupCode int64, msgID, msgInternalId in
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) (uint16, []byte) { func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand, opType uint32) (uint16, []byte) {
seq := c.nextSeq()
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),
}) })
packet := packets.BuildUniPacket(c.Uin, seq, commandName, 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket(commandName, payload)
return seq, packet
} }
// OidbSvc.0xeac_1/2 // OidbSvc.0xeac_1/2
func decodeEssenceMsgResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeEssenceMsgResponse(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := &oidb.EACRspBody{} rsp := &oidb.EACRspBody{}
if err := proto.Unmarshal(payload, &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

@ -2,13 +2,21 @@ package client
import ( import (
"fmt" "fmt"
"math/rand"
"sort"
"strconv"
"time"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/topic"
"github.com/Mrs4s/MiraiGo/client/pb/qweb"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb/channel"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/Mrs4s/MiraiGo/utils"
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"github.com/Mrs4s/MiraiGo/client/pb/channel"
"github.com/Mrs4s/MiraiGo/utils"
) )
type ( type (
@ -32,9 +40,9 @@ type (
CoverUrl string CoverUrl string
AvatarUrl string AvatarUrl string
Channels []*ChannelInfo Channels []*ChannelInfo
Bots []*GuildMemberInfo // Bots []*GuildMemberInfo
Members []*GuildMemberInfo // Members []*GuildMemberInfo
Admins []*GuildMemberInfo // Admins []*GuildMemberInfo
} }
// GuildMeta 频道数据 // GuildMeta 频道数据
@ -50,12 +58,14 @@ type (
OwnerId uint64 OwnerId uint64
} }
// GuildMemberInfo 频道成员信息, 仅通过频道成员列表API获取
GuildMemberInfo struct { GuildMemberInfo struct {
TinyId uint64 TinyId uint64
Title string Title string
Nickname string Nickname string
LastSpeakTime int64 LastSpeakTime int64
Role int32 // 0 = member 1 = admin 2 = owner ? Role uint64
RoleName string
} }
// GuildUserProfile 频道系统用户资料 // GuildUserProfile 频道系统用户资料
@ -64,6 +74,19 @@ type (
Nickname string Nickname string
AvatarUrl string AvatarUrl string
JoinTime int64 // 只有 GetGuildMemberProfileInfo 函数才会有 JoinTime int64 // 只有 GetGuildMemberProfileInfo 函数才会有
Roles []*GuildRole
}
// GuildRole 频道身份组信息
GuildRole struct {
RoleId uint64
RoleName string
ArgbColor uint32
Independent bool
Num int32
Owned bool
Disabled bool
MaxNum int32
} }
// ChannelInfo 子频道信息 // ChannelInfo 子频道信息
@ -76,6 +99,8 @@ type (
ChannelType ChannelType ChannelType ChannelType
AtAllSeq uint64 AtAllSeq uint64
Meta *ChannelMeta Meta *ChannelMeta
fetchTime int64
} }
ChannelMeta struct { ChannelMeta struct {
@ -99,6 +124,14 @@ type (
SlowModeText string SlowModeText string
} }
FetchGuildMemberListWithRoleResult struct {
Members []*GuildMemberInfo
NextIndex uint32
NextRoleId uint64
NextQueryParam string
Finished bool
}
ChannelType int32 ChannelType int32
) )
@ -106,6 +139,7 @@ const (
ChannelTypeText ChannelType = 1 ChannelTypeText ChannelType = 1
ChannelTypeVoice ChannelType = 2 ChannelTypeVoice ChannelType = 2
ChannelTypeLive ChannelType = 5 ChannelTypeLive ChannelType = 5
ChannelTypeTopic ChannelType = 7
) )
func init() { func init() {
@ -121,25 +155,6 @@ func (s *GuildService) FindGuild(guildId uint64) *GuildInfo {
return nil return nil
} }
func (g *GuildInfo) FindMember(tinyId uint64) *GuildMemberInfo {
for i := 0; i < len(g.Members); i++ {
if g.Members[i].TinyId == tinyId {
return g.Members[i]
}
}
for i := 0; i < len(g.Admins); i++ {
if g.Admins[i].TinyId == tinyId {
return g.Admins[i]
}
}
for i := 0; i < len(g.Bots); i++ {
if g.Bots[i].TinyId == tinyId {
return g.Bots[i]
}
}
return nil
}
func (g *GuildInfo) FindChannel(channelId uint64) *ChannelInfo { func (g *GuildInfo) FindChannel(channelId uint64) *ChannelInfo {
for _, c := range g.Channels { for _, c := range g.Channels {
if c.ChannelId == channelId { if c.ChannelId == channelId {
@ -149,161 +164,309 @@ func (g *GuildInfo) FindChannel(channelId uint64) *ChannelInfo {
return nil return nil
} }
func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error) { func (g *GuildInfo) removeChannel(id uint64) {
seq := s.c.nextSeq() i := sort.Search(len(g.Channels), func(i int) bool {
flags := binary.DynamicProtoMessage{} return g.Channels[i].ChannelId >= id
for i := 3; i <= 29; i++ { })
flags[uint64(i)] = uint32(1) if i >= len(g.Channels) || g.Channels[i].ChannelId != id {
return
} }
flags[99] = uint32(1) g.Channels = append(g.Channels[:i], g.Channels[i+1:]...)
flags[100] = uint32(1) }
payload := s.c.packOIDBPackageDynamically(3976, 1, binary.DynamicProtoMessage{
func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error) {
flags := proto.DynamicMessage{}
for i := 3; i <= 29; i++ {
flags[uint64(i)] = 1
}
flags[99] = 1
flags[100] = 1
payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
1: flags, 1: flags,
3: tinyId, 3: tinyId,
4: uint32(0), 4: 0,
}) })
packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0xfc9_1", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload) rsp, err := s.c.sendAndWaitDynamic(s.c.uniPacket("OidbSvcTrpcTcp.0xfc9_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.ChannelOidb0Xfc9Rsp) body := new(channel.ChannelOidb0Xfc9Rsp)
if err = s.c.unpackOIDBPackage(rsp, 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
} }
func (s *GuildService) GetGuildMembers(guildId uint64) (bots []*GuildMemberInfo, members []*GuildMemberInfo, admins []*GuildMemberInfo, err error) { // FetchGuildMemberListWithRole 获取频道成员列表
// 第一次请求: startIndex = 0 , roleIdIndex = 2 param = ""
// 后续请求请根据上次请求的返回值进行设置
func (s *GuildService) FetchGuildMemberListWithRole(guildId, channelId uint64, startIndex uint32, roleIdIndex uint64, param string) (*FetchGuildMemberListWithRoleResult, error) {
seq := s.c.nextSeq() seq := s.c.nextSeq()
u1 := uint32(1) m := proto.DynamicMessage{
// todo: 这个包实际上是 fetchMemberListWithRole , 可以按channel, role等规则获取成员列表, 还需要修改
payload := s.c.packOIDBPackageDynamically(3931, 1, binary.DynamicProtoMessage{ // todo: 可能还需要处理翻页的情况?
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: uint32(0), 6: startIndex,
8: uint32(500), // count 8: 50, // count
14: uint32(2), 12: channelId,
}) }
packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0xf5b_1", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload) if param != "" {
m[13] = param
}
m[14] = roleIdIndex
packet := s.c.uniPacketWithSeq(seq, "OidbSvcTrpcTcp.0xf5b_1", s.c.packOIDBPackageDynamically(3931, 1, m))
rsp, err := s.c.sendAndWaitDynamic(seq, packet) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return nil, nil, 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 = s.c.unpackOIDBPackage(rsp, body); err != nil { if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, nil, nil, errors.Wrap(err, "decode packet error") return nil, errors.Wrap(err, "decode packet error")
} }
protoToMemberInfo := func(mem *channel.GuildMemberInfo) *GuildMemberInfo { var ret []*GuildMemberInfo
return &GuildMemberInfo{ for _, memberWithRole := range body.MemberWithRoles {
TinyId: mem.GetTinyId(), for _, mem := range memberWithRole.Members {
Title: mem.GetTitle(), ret = append(ret, &GuildMemberInfo{
Nickname: mem.GetNickname(), TinyId: mem.TinyId.Unwrap(),
LastSpeakTime: mem.GetLastSpeakTime(), Title: mem.Title.Unwrap(),
Role: mem.GetRole(), Nickname: mem.Nickname.Unwrap(),
LastSpeakTime: mem.LastSpeakTime.Unwrap(),
Role: memberWithRole.RoleId.Unwrap(),
RoleName: memberWithRole.RoleName.Unwrap(),
})
} }
} }
for _, mem := range body.Bots {
bots = append(bots, protoToMemberInfo(mem))
}
for _, mem := range body.Members { for _, mem := range body.Members {
members = append(members, protoToMemberInfo(mem)) ret = append(ret, &GuildMemberInfo{
TinyId: mem.TinyId.Unwrap(),
Title: mem.Title.Unwrap(),
Nickname: mem.Nickname.Unwrap(),
LastSpeakTime: mem.LastSpeakTime.Unwrap(),
Role: 1,
RoleName: "普通成员",
})
} }
for _, mem := range body.AdminInfo.Admins { return &FetchGuildMemberListWithRoleResult{
admins = append(admins, protoToMemberInfo(mem)) Members: ret,
} NextIndex: body.NextIndex.Unwrap(),
return NextRoleId: body.NextRoleIdIndex.Unwrap(),
NextQueryParam: body.NextQueryParam.Unwrap(),
Finished: body.NextIndex.IsNone(),
}, nil
} }
func (s *GuildService) GetGuildMemberProfileInfo(guildId, tinyId uint64) (*GuildUserProfile, error) { // FetchGuildMemberProfileInfo 获取单个频道成员资料
func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*GuildUserProfile, error) {
seq := s.c.nextSeq() seq := s.c.nextSeq()
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: guildId, 4: guildId,
}) })
packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0xf88_1", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload) packet := s.c.uniPacketWithSeq(seq, "OidbSvcTrpcTcp.0xf88_1", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet) 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 = s.c.unpackOIDBPackage(rsp, 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)
if err != nil {
return nil, errors.Wrap(err, "fetch roles 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(),
Roles: roles,
}, nil }, nil
} }
func (s *GuildService) GetGuildRoles(guildId uint64) ([]*GuildRole, error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1019_1",
s.c.packOIDBPackageDynamically(4121, 1, proto.DynamicMessage{1: guildId}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0X1019Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
roles := make([]*GuildRole, 0, len(body.Roles))
for _, role := range body.Roles {
roles = append(roles, &GuildRole{
RoleId: role.RoleId.Unwrap(),
RoleName: role.Name.Unwrap(),
ArgbColor: role.ArgbColor.Unwrap(),
Independent: role.Independent.Unwrap() == 1,
Num: role.Num.Unwrap(),
Owned: role.Owned.Unwrap() == 1,
Disabled: role.Disabled.Unwrap() == 1,
MaxNum: role.MaxNum.Unwrap(),
})
}
return roles, nil
}
func (s *GuildService) CreateGuildRole(guildId uint64, name string, color uint32, independent bool, initialUsers []uint64) (uint64, error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1016_1", s.c.packOIDBPackageDynamically(4118, 1, proto.DynamicMessage{
1: guildId,
2: proto.DynamicMessage{ // todo: 未知参数
1: 1,
2: 1,
3: 1,
},
3: proto.DynamicMessage{
1: name,
2: color,
3: independent,
},
4: initialUsers,
}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return 0, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0X1016Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
return 0, errors.Wrap(err, "decode packet error")
}
return body.RoleId.Unwrap(), nil
}
func (s *GuildService) DeleteGuildRole(guildId uint64, roleId uint64) error {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100e_1", s.c.packOIDBPackageDynamically(4110, 1, proto.DynamicMessage{
1: guildId,
2: roleId,
}))
_, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return errors.Wrap(err, "send packet error")
}
return nil
}
func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint64, user []uint64) error { // remove => p2 = false
setOrRemove := proto.DynamicMessage{
1: roleId,
}
if set {
setOrRemove[2] = user
} else {
setOrRemove[3] = user
}
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x101a_1", s.c.packOIDBPackageDynamically(4122, 1, proto.DynamicMessage{
1: guildId,
2: setOrRemove,
}))
_, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return errors.Wrap(err, "send packet error")
}
return nil
}
func (s *GuildService) ModifyRoleInGuild(guildId uint64, roleId uint64, name string, color uint32, indepedent bool) error {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100d_1", s.c.packOIDBPackageDynamically(4109, 1, proto.DynamicMessage{
1: guildId,
2: roleId,
3: proto.DynamicMessage{
1: 1,
2: 1,
3: 1,
},
4: proto.DynamicMessage{
1: name,
2: color,
3: indepedent,
},
}))
_, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return errors.Wrap(err, "send packet error")
}
return nil
}
func (s *GuildService) FetchGuestGuild(guildId uint64) (*GuildMeta, error) { func (s *GuildService) FetchGuestGuild(guildId uint64) (*GuildMeta, error) {
seq := s.c.nextSeq() payload := s.c.packOIDBPackageDynamically(3927, 9, proto.DynamicMessage{
u1 := uint32(1) 1: proto.DynamicMessage{
payload := s.c.packOIDBPackageDynamically(3927, 9, 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,
1: binary.DynamicProtoMessage{ 18: 1, 19: 1, 20: 1, 22: 1, 23: 1, 5002: 1, 5003: 1, 5004: 1, 5005: 1, 10007: 1,
2: u1, 4: u1, 5: u1, 6: u1, 7: u1, 8: u1, 11: u1, 12: u1, 13: u1, 14: u1, 45: u1,
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,
}, },
}) })
packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0xf57_9", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload) seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf57_9", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet) 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 = s.c.unpackOIDBPackage(rsp, 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) {
seq := s.c.nextSeq() seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf5d_1",
packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0xf5d_1", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, s.c.packOIDBPackageDynamically(3933, 1,
s.c.packOIDBPackageDynamically(3933, 1, binary.DynamicProtoMessage{1: guildId, 3: binary.DynamicProtoMessage{1: uint32(1)}})) proto.DynamicMessage{
1: guildId,
3: proto.DynamicMessage{
1: 1,
},
}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet) 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 = s.c.unpackOIDBPackage(rsp, 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 {
@ -313,20 +476,182 @@ 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) {
seq := s.c.nextSeq() seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf55_1", s.c.packOIDBPackageDynamically(3925, 1, proto.DynamicMessage{1: guildId, 2: channelId}))
packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0xf55_1", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key,
s.c.packOIDBPackageDynamically(3925, 1, binary.DynamicProtoMessage{1: guildId, 2: channelId}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet) 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.ChannelOidb0Xf55Rsp) body := new(channel.ChannelOidb0Xf55Rsp)
if err = s.c.unpackOIDBPackage(rsp, 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
} }
func (s *GuildService) GetTopicChannelFeeds(guildId, channelId uint64) ([]*topic.Feed, error) {
guild := s.FindGuild(guildId)
if guild == nil {
return nil, errors.New("guild not found")
}
channelInfo := guild.FindChannel(channelId)
if channelInfo == nil {
return nil, errors.New("channel not found")
}
if channelInfo.ChannelType != ChannelTypeTopic {
return nil, errors.New("channel type error")
}
req, _ := proto.Marshal(&channel.StGetChannelFeedsReq{
Count: proto.Uint32(12),
From: proto.Uint32(0),
ChannelSign: &channel.StChannelSign{
GuildId: proto.Some(guildId),
ChannelId: proto.Some(channelId),
},
FeedAttchInfo: proto.String(""), // isLoadMore
})
payload, _ := proto.Marshal(&qweb.QWebReq{
Seq: proto.Int64(s.c.nextQWebSeq()),
Qua: proto.String("V1_AND_SQ_8.8.50_2324_YYB_D"),
DeviceInfo: proto.String(s.c.getWebDeviceInfo()),
BusiBuff: req,
TraceId: proto.String(fmt.Sprintf("%v_%v_%v", s.c.Uin, time.Now().Format("0102150405"), rand.Int63())),
Extinfo: []*qweb.COMMEntry{
{
Key: proto.String("fc-appid"),
Value: proto.String("96"),
},
{
Key: proto.String("environment_id"),
Value: proto.String("production"),
},
{
Key: proto.String("tiny_id"),
Value: proto.String(fmt.Sprint(s.TinyId)),
},
},
})
seq, packet := s.c.uniPacket("QChannelSvr.trpc.qchannel.commreader.ComReader.GetChannelTimelineFeeds", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return nil, errors.New("send packet error")
}
pkg := new(qweb.QWebRsp)
body := new(channel.StGetChannelFeedsRsp)
if err = proto.Unmarshal(rsp, pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
feeds := make([]*topic.Feed, 0, len(body.VecFeed))
for _, f := range body.VecFeed {
feeds = append(feeds, topic.DecodeFeed(f))
}
return feeds, nil
}
func (s *GuildService) PostTopicChannelFeed(guildId, channelId uint64, feed *topic.Feed) error {
guild := s.FindGuild(guildId)
if guild == nil {
return errors.New("guild not found")
}
channelInfo := guild.FindChannel(channelId)
if channelInfo == nil {
return errors.New("channel not found")
}
if channelInfo.ChannelType != ChannelTypeTopic {
return errors.New("channel type error")
}
feed.Poster = &topic.FeedPoster{
TinyIdStr: strconv.FormatUint(s.TinyId, 10),
Nickname: s.Nickname,
}
feed.GuildId = guildId
feed.ChannelId = channelId
req, _ := proto.Marshal(&channel.StPublishFeedReq{
ExtInfo: &channel.StCommonExt{
MapInfo: []*channel.CommonEntry{
{
Key: proto.String("clientkey"), Value: proto.String("GuildMain" + utils.RandomStringRange(14, "0123456789")),
},
},
},
From: proto.Int32(0),
JsonFeed: proto.String(feed.ToSendingPayload(s.c.Uin)),
})
payload, _ := proto.Marshal(&qweb.QWebReq{
Seq: proto.Int64(s.c.nextQWebSeq()),
Qua: proto.String("V1_AND_SQ_8.8.50_2324_YYB_D"),
DeviceInfo: proto.String(s.c.getWebDeviceInfo()),
BusiBuff: req,
TraceId: proto.String(fmt.Sprintf("%v_%v_%v", s.c.Uin, time.Now().Format("0102150405"), rand.Int63())),
Extinfo: []*qweb.COMMEntry{
{
Key: proto.String("fc-appid"),
Value: proto.String("96"),
},
{
Key: proto.String("environment_id"),
Value: proto.String("production"),
},
{
Key: proto.String("tiny_id"),
Value: proto.String(fmt.Sprint(s.TinyId)),
},
},
})
seq, packet := s.c.uniPacket("QChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return errors.New("send packet error")
}
pkg := new(qweb.QWebRsp)
body := new(channel.StPublishFeedRsp)
if err = proto.Unmarshal(rsp, pkg); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message")
}
if body.Feed != nil && body.Feed.Id.IsNone() {
return nil
}
return errors.New("post feed error")
}
func (s *GuildService) fetchMemberRoles(guildId uint64, tinyId uint64) ([]*GuildRole, error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1017_1", s.c.packOIDBPackageDynamically(4119, 1, proto.DynamicMessage{
1: guildId,
2: tinyId,
4: proto.DynamicMessage{
1: 1,
2: 1,
3: 1,
},
}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0X1017Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
p1 := body.P1
if p1 == nil {
return nil, errors.New("packet OidbSvcTrpcTcp.0x1017_1: decode p1 error")
}
roles := make([]*GuildRole, 0, len(p1.Roles))
for _, role := range p1.Roles {
roles = append(roles, &GuildRole{
RoleId: role.RoleId.Unwrap(),
RoleName: role.Name.Unwrap(),
ArgbColor: role.ArgbColor.Unwrap(),
})
}
return roles, nil
}
/* need analysis /* need analysis
func (s *GuildService) fetchChannelListState(guildId uint64, channels []*ChannelInfo) { func (s *GuildService) fetchChannelListState(guildId uint64, channels []*ChannelInfo) {
seq := s.c.nextSeq() seq := s.c.nextSeq()
@ -334,13 +659,13 @@ 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,
}, },
}) })
packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0x1008_1", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload) packet := packets.BuildUniPacket(s.c.Uin, seq, "OidbSvcTrpcTcp.0x1008_1", 1, s.c.SessionId, []byte{}, s.c.sigInfo.d2Key, payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return return
@ -354,108 +679,106 @@ 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(),
} }
} }
func (c *QQClient) syncChannelFirstView() { func (c *QQClient) syncChannelFirstView() {
rsp, err := c.sendAndWaitDynamic(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, 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() (uint16, []byte) { func (c *QQClient) buildSyncChannelFirstViewPacket() (uint16, []byte) {
seq := c.nextSeq()
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)
packet := packets.BuildUniPacket(c.Uin, seq, "trpc.group_pro.synclogic.SyncLogic.SyncFirstView", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, payload) return c.uniPacket("trpc.group_pro.synclogic.SyncLogic.SyncFirstView", payload)
return seq, packet
} }
func decodeGuildPushFirstView(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGuildPushFirstView(c *QQClient, pkt *network.Packet) (any, error) {
firstViewMsg := new(channel.FirstViewMsg) firstViewMsg := new(channel.FirstViewMsg)
if err := proto.Unmarshal(payload, 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 {
info.Channels = channels info.Channels = channels
} }
info.Bots, info.Members, info.Admins, _ = c.GuildService.GetGuildMembers(info.GuildId) // info.Bots, info.Members, info.Admins, _ = c.GuildService.GetGuildMembers(info.GuildId)
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

@ -1,35 +1,61 @@
package client package client
import ( import (
"sync"
"time" "time"
"github.com/pierrec/lz4/v4"
"github.com/pkg/errors"
"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/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/internal/packets" "github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
) )
func init() { func init() {
decoders["MsgPush.PushGroupProMsg"] = decodeGuildEventFlowPacket decoders["MsgPush.PushGroupProMsg"] = decodeGuildEventFlowPacket
} }
func decodeGuildEventFlowPacket(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { var updateChanLock sync.Mutex
type tipsPushInfo struct {
TinyId uint64
// TargetMessageSenderUin int64
GuildId uint64
ChannelId uint64
}
func decodeGuildEventFlowPacket(c *QQClient, pkt *network.Packet) (any, error) {
push := new(channel.MsgOnlinePush) push := new(channel.MsgOnlinePush)
if err := proto.Unmarshal(payload, 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.CompressFlag.Unwrap() == 1 && len(push.CompressMsg) > 0 {
press := new(channel.PressMsg)
dst := make([]byte, len(push.CompressMsg)*2)
i, err := lz4.UncompressBlock(push.CompressMsg, dst)
for times := 0; err != nil && err.Error() == "lz4: invalid source or destination buffer too short" && times < 5; times++ {
dst = append(dst, make([]byte, 1024)...)
i, err = lz4.UncompressBlock(push.CompressMsg, dst)
}
if err != nil {
return nil, errors.Wrap(err, "failed to decompress guild event packet")
}
if err = proto.Unmarshal(dst[:i], press); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
push.Msgs = press.Msgs
}
for _, m := range push.Msgs { for _, m := range push.Msgs {
if m.Head.ContentHead.GetType() == 3841 { if m.Head == nil {
type tipsPushInfo struct { continue
TinyId uint64 }
TargetMessageSenderUin int64 if m.Head.ContentHead.Type.Unwrap() == 3841 {
GuildId uint64
ChannelId uint64
}
// 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
@ -37,92 +63,181 @@ func decodeGuildEventFlowPacket(c *QQClient, _ *incomingPacketInfo, payload []by
} }
} }
} }
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 {
tipsInfo.TargetMessageSenderUin = int64(m.CtrlHead.IncludeUin[0])
} }
/*
if len(m.CtrlHead.IncludeUin) > 0 {
tipsInfo.TargetMessageSenderUin = int64(m.CtrlHead.IncludeUin[0])
}
*/
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
} }
if eventBody.UpdateMsg != nil { c.processGuildEventBody(m, eventBody)
if eventBody.UpdateMsg.GetEventType() == 1 || eventBody.UpdateMsg.GetEventType() == 2 { // todo: 撤回消息
continue
}
if eventBody.UpdateMsg.GetEventType() == 4 { // 消息贴表情更新 (包含添加或删除)
t, err := c.GuildService.pullRoamMsgByEventFlow(m.Head.RoutingHead.GetGuildId(), m.Head.RoutingHead.GetChannelId(), eventBody.UpdateMsg.GetMsgSeq(), eventBody.UpdateMsg.GetMsgSeq(), eventBody.UpdateMsg.GetEventVersion()-1)
if err != nil || len(t) == 0 {
c.Error("process guild event flow error: pull eventMsg message error: %v", err)
continue
}
// 自己的消息被贴表情会单独推送一个tips, 这里不需要解析
if t[0].Head.RoutingHead.GetFromTinyid() == c.GuildService.TinyId {
continue
}
updatedEvent := &GuildMessageReactionsUpdatedEvent{
GuildId: m.Head.RoutingHead.GetGuildId(),
ChannelId: m.Head.RoutingHead.GetChannelId(),
MessageId: t[0].Head.ContentHead.GetSeq(),
CurrentReactions: decodeGuildMessageEmojiReactions(t[0]),
}
tipsInfo, err := c.waitPacketTimeoutSyncF("MsgPush.PushGroupProMsg", time.Second, func(i interface{}) bool {
if i == nil {
return false
}
_, ok := i.(*tipsPushInfo)
return ok
})
if err == nil {
updatedEvent.OperatorId = tipsInfo.(*tipsPushInfo).TinyId
updatedEvent.MessageSenderUin = tipsInfo.(*tipsPushInfo).TargetMessageSenderUin
}
c.dispatchGuildMessageReactionsUpdatedEvent(updatedEvent)
}
}
continue continue
} }
if cm := c.parseGuildChannelMessage(m); cm != nil { if m.Head.ContentHead.Type.Unwrap() == 3840 {
c.dispatchGuildChannelMessage(cm) if m.Head.RoutingHead.DirectMessageFlag.Unwrap() == 1 {
// todo: direct message decode
continue
}
if m.Head.RoutingHead.FromTinyid.Unwrap() == c.GuildService.TinyId {
continue
}
if cm := c.GuildService.parseGuildChannelMessage(m); cm != nil {
c.dispatchGuildChannelMessage(cm)
}
} }
} }
return nil, nil return nil, nil
} }
func (s *GuildService) pullRoamMsgByEventFlow(guildId, channelId, beginSeq, endSeq, eventVersion uint64) ([]*channel.ChannelMsgContent, error) { func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody *channel.EventBody) {
payload, _ := proto.Marshal(&channel.ChannelMsgReq{ var guild *GuildInfo
ChannelParam: &channel.ChannelParam{ if m.Head.RoutingHead.GuildId.Unwrap() != 0 {
GuildId: &guildId, if guild = c.GuildService.FindGuild(m.Head.RoutingHead.GuildId.Unwrap()); guild == nil {
ChannelId: &channelId, c.warning("process channel event error: guild not found.")
BeginSeq: &beginSeq, return
EndSeq: &endSeq, }
Version: []uint64{eventVersion},
},
WithVersionFlag: proto.Uint32(1),
DirectMessageFlag: proto.Uint32(0),
})
seq := s.c.nextSeq()
packet := packets.BuildUniPacket(s.c.Uin, seq, "trpc.group_pro.synclogic.SyncLogic.GetChannelMsg", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
} }
msgRsp := new(channel.ChannelMsgRsp) switch {
if err = proto.Unmarshal(rsp, msgRsp); err != nil { case eventBody.CreateChan != nil:
return nil, errors.Wrap(err, "failed to unmarshal protobuf message") for _, chanId := range eventBody.CreateChan.CreateId {
if guild.FindChannel(chanId.ChanId.Unwrap()) != nil {
continue
}
channelInfo, err := c.GuildService.FetchChannelInfo(guild.GuildId, chanId.ChanId.Unwrap())
if err != nil {
c.warning("process create channel event error: fetch channel info error: %v", err)
continue
}
guild.Channels = append(guild.Channels, channelInfo)
c.dispatchGuildChannelCreatedEvent(&GuildChannelOperationEvent{
OperatorId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelInfo: channelInfo,
})
}
case eventBody.DestroyChan != nil:
for _, chanId := range eventBody.DestroyChan.DeleteId {
channelInfo := guild.FindChannel(chanId.ChanId.Unwrap())
if channelInfo == nil {
continue
}
guild.removeChannel(chanId.ChanId.Unwrap())
c.dispatchGuildChannelDestroyedEvent(&GuildChannelOperationEvent{
OperatorId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: guild.GuildId,
ChannelInfo: channelInfo,
})
}
case eventBody.ChangeChanInfo != nil:
updateChanLock.Lock()
defer updateChanLock.Unlock()
oldInfo := guild.FindChannel(eventBody.ChangeChanInfo.ChanId.Unwrap())
if oldInfo == nil {
info, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GuildId.Unwrap(), eventBody.ChangeChanInfo.ChanId.Unwrap())
if err != nil {
c.error("failed to decode channel info updated event: fetch channel info failed: %v", err)
return
}
guild.Channels = append(guild.Channels, info)
oldInfo = info
}
if time.Now().Unix()-oldInfo.fetchTime <= 2 {
return
}
newInfo, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GuildId.Unwrap(), eventBody.ChangeChanInfo.ChanId.Unwrap())
if err != nil {
c.error("failed to decode channel info updated event: fetch channel info failed: %v", err)
return
}
for i := range guild.Channels {
if guild.Channels[i].ChannelId == newInfo.ChannelId {
guild.Channels[i] = newInfo
break
}
}
c.dispatchGuildChannelUpdatedEvent(&GuildChannelUpdatedEvent{
OperatorId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: eventBody.ChangeChanInfo.ChanId.Unwrap(),
OldChannelInfo: oldInfo,
NewChannelInfo: newInfo,
})
case eventBody.JoinGuild != nil:
/* 应该不会重复推送把, 不会吧不会吧
if mem := guild.FindMember(eventBody.JoinGuild.MemberTinyid.Unwrap()); mem != nil {
c.info("ignore join guild event: member %v already exists", mem.TinyId)
return
}
*/
profile, err := c.GuildService.FetchGuildMemberProfileInfo(guild.GuildId, eventBody.JoinGuild.MemberTinyid.Unwrap())
if err != nil {
c.error("failed to decode member join guild event: get member profile error: %v", err)
return
}
info := &GuildMemberInfo{
TinyId: profile.TinyId,
Nickname: profile.Nickname,
}
// guild.Members = append(guild.Members, info)
c.dispatchMemberJoinedGuildEvent(&MemberJoinGuildEvent{
Guild: guild,
Member: info,
})
case eventBody.UpdateMsg != nil:
if eventBody.UpdateMsg.EventType.Unwrap() == 1 || eventBody.UpdateMsg.EventType.Unwrap() == 2 {
c.dispatchGuildMessageRecalledEvent(&GuildMessageRecalledEvent{
OperatorId: eventBody.UpdateMsg.OperatorTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: m.Head.RoutingHead.ChannelId.Unwrap(),
MessageId: eventBody.UpdateMsg.MsgSeq.Unwrap(),
RecallTime: int64(m.Head.ContentHead.Time.Unwrap()),
})
return
}
if eventBody.UpdateMsg.EventType.Unwrap() == 4 { // 消息贴表情更新 (包含添加或删除)
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 {
c.error("process guild event flow error: pull eventMsg message error: %v", err)
return
}
// 自己的消息被贴表情会单独推送一个tips, 这里不需要解析
if t[0].Head.RoutingHead.FromTinyid.Unwrap() == c.GuildService.TinyId {
return
}
updatedEvent := &GuildMessageReactionsUpdatedEvent{
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: m.Head.RoutingHead.ChannelId.Unwrap(),
MessageId: t[0].Head.ContentHead.Seq.Unwrap(),
CurrentReactions: decodeGuildMessageEmojiReactions(t[0]),
}
tipsInfo, err := c.waitPacketTimeoutSyncF("MsgPush.PushGroupProMsg", time.Second, func(i any) bool {
if i == nil {
return false
}
_, ok := i.(*tipsPushInfo)
return ok
})
if err == nil {
updatedEvent.OperatorId = tipsInfo.(*tipsPushInfo).TinyId
// updatedEvent.MessageSenderUin = tipsInfo.(*tipsPushInfo).TargetMessageSenderUin
}
c.dispatchGuildMessageReactionsUpdatedEvent(updatedEvent)
}
} }
return msgRsp.ChannelMsg.Msgs, nil
} }

View File

@ -1,28 +1,40 @@
package client package client
import ( import (
"encoding/hex" "fmt"
"io"
"math/rand" "math/rand"
"strconv" "strconv"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x388"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/pkg/errors" "github.com/pkg/errors"
"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/msg" "github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
"google.golang.org/protobuf/proto"
) )
func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *message.SendingMessage) error { func init() {
decoders["ImgStore.QQMeetPicUp"] = decodeGuildImageStoreResponse
}
func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *message.SendingMessage) (*message.GuildChannelMessage, error) {
mr := rand.Uint32() // 客户端似乎是生成的 u32 虽然类型是u64 mr := rand.Uint32() // 客户端似乎是生成的 u32 虽然类型是u64
for _, elem := range m.Elements {
if elem.Type() == message.At {
at := elem.(*message.AtElement)
if at.SubType == message.AtTypeGroupMember {
at.SubType = message.AtTypeGuildMember
}
}
}
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{
@ -36,37 +48,136 @@ func (s *GuildService) SendGuildChannelMessage(guildId, channelId uint64, m *mes
}, },
}, },
}} }}
seq := s.c.nextSeq()
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(s.c.Uin, seq, "MsgProxy.SendMsg", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload) seq, packet := s.c.uniPacket("MsgProxy.SendMsg", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet) rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil { if err != nil {
return 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); err != nil { if err = proto.Unmarshal(rsp, body); err != nil {
return 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 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())
} }
// todo: 返回 *message.GuildMessage elements := m.Elements
return nil if body.Body != nil && body.Body.RichText != nil {
elements = message.ParseMessageElems(body.Body.RichText.Elems)
}
return &message.GuildChannelMessage{
Id: body.Head.ContentHead.Seq.Unwrap(),
InternalId: body.Head.ContentHead.Random.Unwrap(),
GuildId: guildId,
ChannelId: channelId,
Time: int64(body.SendTime.Unwrap()),
Sender: &message.GuildSender{
TinyId: body.Head.RoutingHead.FromTinyid.Unwrap(),
Nickname: s.Nickname,
},
Elements: elements,
}, nil
} }
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) {
seq := s.c.nextSeq() rsp, err := s.c.sendAndWait(s.c.buildGuildImageStorePacket(guildId, channelId, hash, size))
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := rsp.(*imageUploadResponse)
if body.IsExists {
return &message.GuildImageElement{
FileId: body.FileId,
FilePath: fmt.Sprintf("%x.jpg", hash),
Size: int32(size),
DownloadIndex: body.DownloadIndex,
Width: body.Width,
Height: body.Height,
Md5: hash,
}, nil
}
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) {
source := message.Source{
SourceType: message.SourceGuildChannel,
PrimaryID: int64(guildId),
SecondaryID: int64(channelId),
}
image, err := s.c.uploadGroupOrGuildImage(source, img)
if err != nil {
return nil, err
}
return image.(*message.GuildImageElement), nil
}
func (s *GuildService) PullGuildChannelMessage(guildId, channelId, beginSeq, endSeq uint64) (r []*message.GuildChannelMessage, e error) {
contents, err := s.pullChannelMessages(guildId, channelId, beginSeq, endSeq, 0, false)
if err != nil {
return nil, errors.Wrap(err, "pull channel message error")
}
for _, c := range contents {
if cm := s.parseGuildChannelMessage(c); cm != nil {
cm.Reactions = decodeGuildMessageEmojiReactions(c)
r = append(r, cm)
}
}
if len(r) == 0 {
return nil, errors.New("message not found")
}
return
}
func (s *GuildService) pullChannelMessages(guildId, channelId, beginSeq, endSeq, eventVersion uint64, direct bool) ([]*channel.ChannelMsgContent, error) {
param := &channel.ChannelParam{
GuildId: proto.Some(guildId),
ChannelId: proto.Some(channelId),
BeginSeq: proto.Some(beginSeq),
EndSeq: proto.Some(endSeq),
}
if eventVersion != 0 {
param.Version = []uint64{eventVersion}
}
withVersionFlag := uint32(0)
if eventVersion != 0 {
withVersionFlag = 1
}
directFlag := uint32(0)
if direct {
directFlag = 1
}
payload, _ := proto.Marshal(&channel.ChannelMsgReq{
ChannelParam: param,
WithVersionFlag: proto.Some(withVersionFlag),
DirectMessageFlag: proto.Some(directFlag),
})
seq, packet := s.c.uniPacket("trpc.group_pro.synclogic.SyncLogic.GetChannelMsg", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
msgRsp := new(channel.ChannelMsgRsp)
if err = proto.Unmarshal(rsp, msgRsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return msgRsp.ChannelMsg.Msgs, nil
}
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(s.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),
@ -74,41 +185,20 @@ func (s *GuildService) QueryImage(guildId, channelId uint64, hash []byte, size u
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),
}) })
packet := packets.BuildUniPacket(s.c.Uin, seq, "ImgStore.QQMeetPicUp", 1, s.c.OutGoingPacketSessionId, []byte{}, s.c.sigInfo.d2Key, payload) return c.uniPacket("ImgStore.QQMeetPicUp", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(cmd0x388.D388RspBody)
if err = proto.Unmarshal(rsp, body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if len(body.TryupImgRsp) == 0 {
return nil, errors.New("response is empty")
}
if body.TryupImgRsp[0].GetFileExit() {
return &message.GuildImageElement{
FileId: body.TryupImgRsp[0].GetFileid(),
FilePath: hex.EncodeToString(hash) + ".jpg",
Size: int32(size),
DownloadIndex: string(body.TryupImgRsp[0].GetDownloadIndex()),
Md5: hash,
}, nil
}
return nil, errors.New("image is not exists")
} }
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
} }
@ -126,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)
@ -140,21 +230,64 @@ func decodeGuildMessageEmojiReactions(content *channel.ChannelMsgContent) (r []*
return return
} }
func (c *QQClient) parseGuildChannelMessage(msg *channel.ChannelMsgContent) *message.GuildChannelMessage { func decodeGuildImageStoreResponse(_ *QQClient, pkt *network.Packet) (any, error) {
guild := c.GuildService.FindGuild(msg.Head.RoutingHead.GetGuildId()) body := new(cmd0x388.D388RspBody)
if err := proto.Unmarshal(pkt.Payload, body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if len(body.TryupImgRsp) == 0 {
return nil, errors.New("response is empty")
}
rsp := body.TryupImgRsp[0]
if rsp.Result.Unwrap() != 0 {
return &imageUploadResponse{
ResultCode: int32(rsp.Result.Unwrap()),
Message: string(rsp.FailMsg),
}, nil
}
if rsp.FileExit.Unwrap() {
resp := &imageUploadResponse{
IsExists: true,
FileId: int64(rsp.Fileid.Unwrap()),
DownloadIndex: string(rsp.DownloadIndex),
}
if rsp.ImgInfo != nil {
resp.Width = int32(rsp.ImgInfo.FileWidth.Unwrap())
resp.Height = int32(rsp.ImgInfo.FileHeight.Unwrap())
}
return resp, nil
}
return &imageUploadResponse{
FileId: int64(rsp.Fileid.Unwrap()),
UploadKey: rsp.UpUkey,
UploadIp: rsp.UpIp,
UploadPort: rsp.UpPort,
DownloadIndex: string(rsp.DownloadIndex),
}, nil
}
func (s *GuildService) parseGuildChannelMessage(msg *channel.ChannelMsgContent) *message.GuildChannelMessage {
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
} }
// mem := guild.FindMember(msg.Head.RoutingHead.GetFromTinyid()) if msg.Body == nil || msg.Body.RichText == nil {
return nil
}
// mem := guild.FindMember(msg.Head.RoutingHead.FromTinyid.Unwrap())
memberName := msg.ExtInfo.MemberName
if memberName == nil {
memberName = msg.ExtInfo.FromNick
}
return &message.GuildChannelMessage{ return &message.GuildChannelMessage{
Id: msg.Head.ContentHead.GetSeq(), Id: msg.Head.ContentHead.Seq.Unwrap(),
InternalId: msg.Body.RichText.Attr.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(msg.ExtInfo.GetFromNick()), Nickname: string(memberName),
}, },
Elements: message.ParseMessageElems(msg.Body.RichText.Elems), Elements: message.ParseMessageElems(msg.Body.RichText.Elems),
} }

View File

@ -1,387 +0,0 @@
// Code generated by syncmap; DO NOT EDIT.
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package client
import (
"sync"
"sync/atomic"
"unsafe"
)
// Map is like a Go map[interface{}]interface{} but is safe for concurrent use
// by multiple goroutines without additional locking or coordination.
// Loads, stores, and deletes run in amortized constant time.
//
// The Map type is specialized. Most code should use a plain Go map instead,
// with separate locking or coordination, for better type safety and to make it
// easier to maintain other invariants along with the map content.
//
// The Map type is optimized for two common use cases: (1) when the entry for a given
// key is only ever written once but read many times, as in caches that only grow,
// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
// sets of keys. In these two cases, use of a Map may significantly reduce lock
// contention compared to a Go map paired with a separate Mutex or RWMutex.
//
// The zero Map is empty and ready for use. A Map must not be copied after first use.
type HandlerMap struct {
mu sync.Mutex
// read contains the portion of the map's contents that are safe for
// concurrent access (with or without mu held).
//
// The read field itself is always safe to load, but must only be stored with
// mu held.
//
// Entries stored in read may be updated concurrently without mu, but updating
// a previously-expunged entry requires that the entry be copied to the dirty
// map and unexpunged with mu held.
read atomic.Value // readOnly
// dirty contains the portion of the map's contents that require mu to be
// held. To ensure that the dirty map can be promoted to the read map quickly,
// it also includes all of the non-expunged entries in the read map.
//
// Expunged entries are not stored in the dirty map. An expunged entry in the
// clean map must be unexpunged and added to the dirty map before a new value
// can be stored to it.
//
// If the dirty map is nil, the next write to the map will initialize it by
// making a shallow copy of the clean map, omitting stale entries.
dirty map[uint16]*entryHandlerMap
// misses counts the number of loads since the read map was last updated that
// needed to lock mu to determine whether the key was present.
//
// Once enough misses have occurred to cover the cost of copying the dirty
// map, the dirty map will be promoted to the read map (in the unamended
// state) and the next store to the map will make a new dirty copy.
misses int
}
// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnlyHandlerMap struct {
m map[uint16]*entryHandlerMap
amended bool // true if the dirty map contains some key not in m.
}
// expunged is an arbitrary pointer that marks entries which have been deleted
// from the dirty map.
var expungedHandlerMap = unsafe.Pointer(new(*handlerInfo))
// An entry is a slot in the map corresponding to a particular key.
type entryHandlerMap struct {
// p points to the interface{} value stored for the entry.
//
// If p == nil, the entry has been deleted and m.dirty == nil.
//
// If p == expunged, the entry has been deleted, m.dirty != nil, and the entry
// is missing from m.dirty.
//
// Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty
// != nil, in m.dirty[key].
//
// An entry can be deleted by atomic replacement with nil: when m.dirty is
// next created, it will atomically replace nil with expunged and leave
// m.dirty[key] unset.
//
// An entry's associated value can be updated by atomic replacement, provided
// p != expunged. If p == expunged, an entry's associated value can be updated
// only after first setting m.dirty[key] = e so that lookups using the dirty
// map find the entry.
p unsafe.Pointer // *interface{}
}
func newEntryHandlerMap(i *handlerInfo) *entryHandlerMap {
return &entryHandlerMap{p: unsafe.Pointer(&i)}
}
// Load returns the value stored in the map for a key, or nil if no
// value is present.
// The ok result indicates whether value was found in the map.
func (m *HandlerMap) Load(key uint16) (value *handlerInfo, ok bool) {
read, _ := m.read.Load().(readOnlyHandlerMap)
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
// Avoid reporting a spurious miss if m.dirty got promoted while we were
// blocked on m.mu. (If further loads of the same key will not miss, it's
// not worth copying the dirty map for this key.)
read, _ = m.read.Load().(readOnlyHandlerMap)
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
// Regardless of whether the entry was present, record a miss: this key
// will take the slow path until the dirty map is promoted to the read
// map.
m.missLocked()
}
m.mu.Unlock()
}
if !ok {
return value, false
}
return e.load()
}
func (e *entryHandlerMap) load() (value *handlerInfo, ok bool) {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expungedHandlerMap {
return value, false
}
return *(**handlerInfo)(p), true
}
// Store sets the value for a key.
func (m *HandlerMap) Store(key uint16, value *handlerInfo) {
read, _ := m.read.Load().(readOnlyHandlerMap)
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
}
m.mu.Lock()
read, _ = m.read.Load().(readOnlyHandlerMap)
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
// The entry was previously expunged, which implies that there is a
// non-nil dirty map and this entry is not in it.
m.dirty[key] = e
}
e.storeLocked(&value)
} else if e, ok := m.dirty[key]; ok {
e.storeLocked(&value)
} else {
if !read.amended {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
m.read.Store(readOnlyHandlerMap{m: read.m, amended: true})
}
m.dirty[key] = newEntryHandlerMap(value)
}
m.mu.Unlock()
}
// tryStore stores a value if the entry has not been expunged.
//
// If the entry is expunged, tryStore returns false and leaves the entry
// unchanged.
func (e *entryHandlerMap) tryStore(i **handlerInfo) bool {
for {
p := atomic.LoadPointer(&e.p)
if p == expungedHandlerMap {
return false
}
if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
return true
}
}
}
// unexpungeLocked ensures that the entry is not marked as expunged.
//
// If the entry was previously expunged, it must be added to the dirty map
// before m.mu is unlocked.
func (e *entryHandlerMap) unexpungeLocked() (wasExpunged bool) {
return atomic.CompareAndSwapPointer(&e.p, expungedHandlerMap, nil)
}
// storeLocked unconditionally stores a value to the entry.
//
// The entry must be known not to be expunged.
func (e *entryHandlerMap) storeLocked(i **handlerInfo) {
atomic.StorePointer(&e.p, unsafe.Pointer(i))
}
// LoadOrStore returns the existing value for the key if present.
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored.
func (m *HandlerMap) LoadOrStore(key uint16, value *handlerInfo) (actual *handlerInfo, loaded bool) {
// Avoid locking if it's a clean hit.
read, _ := m.read.Load().(readOnlyHandlerMap)
if e, ok := read.m[key]; ok {
actual, loaded, ok := e.tryLoadOrStore(value)
if ok {
return actual, loaded
}
}
m.mu.Lock()
read, _ = m.read.Load().(readOnlyHandlerMap)
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
m.dirty[key] = e
}
actual, loaded, _ = e.tryLoadOrStore(value)
} else if e, ok := m.dirty[key]; ok {
actual, loaded, _ = e.tryLoadOrStore(value)
m.missLocked()
} else {
if !read.amended {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
m.read.Store(readOnlyHandlerMap{m: read.m, amended: true})
}
m.dirty[key] = newEntryHandlerMap(value)
actual, loaded = value, false
}
m.mu.Unlock()
return actual, loaded
}
// tryLoadOrStore atomically loads or stores a value if the entry is not
// expunged.
//
// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and
// returns with ok==false.
func (e *entryHandlerMap) tryLoadOrStore(i *handlerInfo) (actual *handlerInfo, loaded, ok bool) {
p := atomic.LoadPointer(&e.p)
if p == expungedHandlerMap {
return actual, false, false
}
if p != nil {
return *(**handlerInfo)(p), true, true
}
// Copy the interface after the first load to make this method more amenable
// to escape analysis: if we hit the "load" path or the entry is expunged, we
// shouldn't bother heap-allocating.
ic := i
for {
if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) {
return i, false, true
}
p = atomic.LoadPointer(&e.p)
if p == expungedHandlerMap {
return actual, false, false
}
if p != nil {
return *(**handlerInfo)(p), true, true
}
}
}
// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
func (m *HandlerMap) LoadAndDelete(key uint16) (value *handlerInfo, loaded bool) {
read, _ := m.read.Load().(readOnlyHandlerMap)
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
read, _ = m.read.Load().(readOnlyHandlerMap)
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
delete(m.dirty, key)
// Regardless of whether the entry was present, record a miss: this key
// will take the slow path until the dirty map is promoted to the read
// map.
m.missLocked()
}
m.mu.Unlock()
}
if ok {
return e.delete()
}
return value, false
}
// Delete deletes the value for a key.
func (m *HandlerMap) Delete(key uint16) {
m.LoadAndDelete(key)
}
func (e *entryHandlerMap) delete() (value *handlerInfo, ok bool) {
for {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expungedHandlerMap {
return value, false
}
if atomic.CompareAndSwapPointer(&e.p, p, nil) {
return *(**handlerInfo)(p), true
}
}
}
// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
//
// Range does not necessarily correspond to any consistent snapshot of the Map's
// contents: no key will be visited more than once, but if the value for any key
// is stored or deleted concurrently, Range may reflect any mapping for that key
// from any point during the Range call.
//
// Range may be O(N) with the number of elements in the map even if f returns
// false after a constant number of calls.
func (m *HandlerMap) Range(f func(key uint16, value *handlerInfo) bool) {
// We need to be able to iterate over all of the keys that were already
// present at the start of the call to Range.
// If read.amended is false, then read.m satisfies that property without
// requiring us to hold m.mu for a long time.
read, _ := m.read.Load().(readOnlyHandlerMap)
if read.amended {
// m.dirty contains keys not in read.m. Fortunately, Range is already O(N)
// (assuming the caller does not break out early), so a call to Range
// amortizes an entire copy of the map: we can promote the dirty copy
// immediately!
m.mu.Lock()
read, _ = m.read.Load().(readOnlyHandlerMap)
if read.amended {
read = readOnlyHandlerMap{m: m.dirty}
m.read.Store(read)
m.dirty = nil
m.misses = 0
}
m.mu.Unlock()
}
for k, e := range read.m {
v, ok := e.load()
if !ok {
continue
}
if !f(k, v) {
break
}
}
}
func (m *HandlerMap) missLocked() {
m.misses++
if m.misses < len(m.dirty) {
return
}
m.read.Store(readOnlyHandlerMap{m: m.dirty})
m.dirty = nil
m.misses = 0
}
func (m *HandlerMap) dirtyLocked() {
if m.dirty != nil {
return
}
read, _ := m.read.Load().(readOnlyHandlerMap)
m.dirty = make(map[uint16]*entryHandlerMap, len(read.m))
for k, e := range read.m {
if !e.tryExpungeLocked() {
m.dirty[k] = e
}
}
}
func (e *entryHandlerMap) tryExpungeLocked() (isExpunged bool) {
p := atomic.LoadPointer(&e.p)
for p == nil {
if atomic.CompareAndSwapPointer(&e.p, nil, expungedHandlerMap) {
return true
}
p = atomic.LoadPointer(&e.p)
}
return p == expungedHandlerMap
}

View File

@ -1,499 +0,0 @@
package client
import (
"bytes"
"crypto/md5"
binary2 "encoding/binary"
"fmt"
"io"
"net"
"net/http"
"os"
"strconv"
"sync"
"sync/atomic"
"time"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"google.golang.org/protobuf/proto"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/utils"
)
func (c *QQClient) highwayUpload(ip uint32, port int, updKey, data []byte, cmdID int32) error {
return c.highwayUploadStream(ip, port, updKey, bytes.NewReader(data), cmdID)
}
func (c *QQClient) highwayUploadStream(ip uint32, port int, updKey []byte, stream io.ReadSeeker, cmdId int32) error {
addr := net.TCPAddr{
IP: make([]byte, 4),
Port: port,
}
binary2.LittleEndian.PutUint32(addr.IP, ip)
h := md5.New()
length, _ := io.Copy(h, stream)
fh := h.Sum(nil)
const chunkSize = 8192 * 8
_, _ = stream.Seek(0, io.SeekStart)
conn, err := net.DialTCP("tcp", nil, &addr)
if err != nil {
return errors.Wrap(err, "connect error")
}
defer conn.Close()
offset := 0
reader := binary.NewNetworkReader(conn)
buf := binary.Get256KBytes()
chunk := *buf
defer binary.Put256KBytes(buf)
w := binary.NewWriter()
defer binary.PutWriter(w)
for {
chunk = chunk[:chunkSize]
rl, err := io.ReadFull(stream, 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: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.DataUp",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 4096,
CommandId: cmdId,
LocaleId: 2052,
},
MsgSeghead: &pb.SegHead{
Filesize: length,
Dataoffset: int64(offset),
Datalength: int32(rl),
Serviceticket: updKey,
Md5: ch[:],
FileMd5: fh,
},
ReqExtendinfo: EmptyBytes,
})
offset += rl
w.Reset()
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(uint32(len(chunk)))
w.Write(head)
w.Write(chunk)
w.WriteByte(41)
_, err = conn.Write(w.Bytes())
if err != nil {
return errors.Wrap(err, "write conn error")
}
rspHead, _, err := highwayReadResponse(reader)
if err != nil {
return errors.Wrap(err, "highway upload error")
}
if rspHead.ErrorCode != 0 {
return errors.New("upload failed")
}
}
return nil
}
func (c *QQClient) highwayUploadByBDH(stream io.Reader, length int64, cmdId int32, ticket, sum, ext []byte, encrypt bool) ([]byte, error) {
if len(c.srvSsoAddrs) == 0 {
return nil, errors.New("srv addrs not found. maybe miss some packet?")
}
if encrypt {
if c.bigDataSession == nil || len(c.bigDataSession.SessionKey) == 0 {
return nil, errors.New("session key not found. maybe miss some packet?")
}
ext = binary.NewTeaCipher(c.bigDataSession.SessionKey).Encrypt(ext)
}
const chunkSize = 256 * 1024
conn, err := net.DialTimeout("tcp", c.srvSsoAddrs[0], time.Second*20)
if err != nil {
return nil, errors.Wrap(err, "connect error")
}
defer conn.Close()
offset := 0
reader := binary.NewNetworkReader(conn)
if err = c.highwaySendHeartbreak(conn); err != nil {
return nil, errors.Wrap(err, "echo error")
}
if _, _, err = highwayReadResponse(reader); err != nil {
return nil, errors.Wrap(err, "echo error")
}
var rspExt []byte
buf := binary.Get256KBytes()
chunk := *buf
defer binary.Put256KBytes(buf)
w := binary.NewWriter()
defer binary.PutWriter(w)
for {
chunk = chunk[:chunkSize]
rl, err := io.ReadFull(stream, 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: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.DataUp",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 4096,
CommandId: cmdId,
LocaleId: 2052,
},
MsgSeghead: &pb.SegHead{
Filesize: length,
Dataoffset: int64(offset),
Datalength: int32(rl),
Serviceticket: ticket,
Md5: ch[:],
FileMd5: sum,
},
ReqExtendinfo: ext,
})
offset += rl
w.Reset()
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(uint32(len(chunk)))
w.Write(head)
w.Write(chunk)
w.WriteByte(41)
_, err = conn.Write(w.Bytes())
if err != nil {
return nil, errors.Wrap(err, "write conn error")
}
rspHead, _, err := highwayReadResponse(reader)
if err != nil {
return nil, errors.Wrap(err, "highway upload error")
}
if rspHead.ErrorCode != 0 {
return nil, errors.Errorf("upload failed: %d", rspHead.ErrorCode)
}
if rspHead.RspExtendinfo != nil {
rspExt = rspHead.RspExtendinfo
}
if rspHead.MsgSeghead != nil && rspHead.MsgSeghead.Serviceticket != nil {
ticket = rspHead.MsgSeghead.Serviceticket
}
}
return rspExt, nil
}
func (c *QQClient) highwayUploadFileMultiThreadingByBDH(path string, cmdId int32, threadCount int, ticket, ext []byte, encrypt bool) ([]byte, error) {
if len(c.srvSsoAddrs) == 0 {
return nil, errors.New("srv addrs not found. maybe miss some packet?")
}
if encrypt {
if c.bigDataSession == nil || len(c.bigDataSession.SessionKey) == 0 {
return nil, errors.New("session key not found. maybe miss some packet?")
}
ext = binary.NewTeaCipher(c.bigDataSession.SessionKey).Encrypt(ext)
}
stat, err := os.Stat(path)
if err != nil {
return nil, errors.Wrap(err, "get stat error")
}
file, err := os.OpenFile(path, os.O_RDONLY, 0o666)
if err != nil {
return nil, errors.Wrap(err, "open file error")
}
defer file.Close()
h := md5.New()
length, _ := io.Copy(h, file)
fh := h.Sum(nil)
_, _ = file.Seek(0, io.SeekStart)
if stat.Size() < 1024*1024*3 || threadCount < 2 {
return c.highwayUploadByBDH(file, length, cmdId, ticket, fh, ext, false)
}
type BlockMetaData struct {
Id int
BeginOffset int64
EndOffset int64
}
const blockSize int64 = 1024 * 512
var (
blocks []*BlockMetaData
rspExt []byte
BlockId = ^uint32(0) // -1
uploadedCount uint32
cond = sync.NewCond(&sync.Mutex{})
)
// 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 {
defer cond.Signal()
conn, err := net.DialTimeout("tcp", c.srvSsoAddrs[0], time.Second*20)
if err != nil {
return errors.Wrap(err, "connect error")
}
defer conn.Close()
chunk, _ := os.OpenFile(path, os.O_RDONLY, 0o666)
defer chunk.Close()
reader := binary.NewNetworkReader(conn)
if err = c.highwaySendHeartbreak(conn); err != nil {
return errors.Wrap(err, "echo error")
}
if _, _, err = highwayReadResponse(reader); err != nil {
return errors.Wrap(err, "echo error")
}
buffer := make([]byte, blockSize)
w := binary.NewWriter()
w.Reset()
w.Grow(600 * 1024) // 复用,600k 不要放回池中
for {
nextId := atomic.AddUint32(&BlockId, 1)
if nextId >= uint32(len(blocks)) {
break
}
block := blocks[nextId]
if block.Id == len(blocks)-1 {
cond.L.Lock()
for atomic.LoadUint32(&uploadedCount) != uint32(len(blocks))-1 {
cond.Wait()
}
cond.L.Unlock()
}
buffer = buffer[:blockSize]
_, _ = chunk.Seek(block.BeginOffset, io.SeekStart)
ri, err := io.ReadFull(chunk, buffer)
if err != nil {
if err == io.EOF {
break
}
if err == io.ErrUnexpectedEOF {
buffer = buffer[:ri]
} else {
return err
}
}
ch := md5.Sum(buffer)
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.DataUp",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 4096,
CommandId: cmdId,
LocaleId: 2052,
},
MsgSeghead: &pb.SegHead{
Filesize: stat.Size(),
Dataoffset: block.BeginOffset,
Datalength: int32(ri),
Serviceticket: ticket,
Md5: ch[:],
FileMd5: fh,
},
ReqExtendinfo: ext,
})
w.Reset()
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(uint32(len(buffer)))
w.Write(head)
w.Write(buffer)
w.WriteByte(41)
_, err = conn.Write(w.Bytes())
if err != nil {
return errors.Wrap(err, "write conn error")
}
rspHead, _, err := highwayReadResponse(reader)
if err != nil {
return errors.Wrap(err, "highway upload error")
}
if rspHead.ErrorCode != 0 {
return errors.Errorf("upload failed: %d", rspHead.ErrorCode)
}
if rspHead.RspExtendinfo != nil {
rspExt = rspHead.RspExtendinfo
}
atomic.AddUint32(&uploadedCount, 1)
}
return nil
}
group := errgroup.Group{}
for i := 0; i < threadCount; i++ {
group.Go(doUpload)
}
err = group.Wait()
return rspExt, err
}
func (c *QQClient) highwaySendHeartbreak(conn net.Conn) error {
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.Echo",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 4096,
CommandId: 0,
LocaleId: 2052,
},
})
w := binary.NewWriter()
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(0)
w.Write(head)
w.WriteByte(41)
_, err := conn.Write(w.Bytes())
binary.PutWriter(w)
return err
}
func highwayReadResponse(r *binary.NetworkReader) (*pb.RspDataHighwayHead, []byte, error) {
_, err := r.ReadByte()
if err != nil {
return nil, nil, errors.Wrap(err, "failed to read byte")
}
hl, _ := r.ReadInt32()
a2, _ := r.ReadInt32()
head, _ := r.ReadBytes(int(hl))
payload, _ := r.ReadBytes(int(a2))
_, _ = r.ReadByte()
rsp := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(head, rsp); err != nil {
return nil, nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return rsp, payload, nil
}
func (c *QQClient) excitingUploadStream(stream io.ReadSeeker, cmdId int32, ticket, ext []byte) ([]byte, error) {
fileMd5, fileLength := utils.ComputeMd5AndLength(stream)
_, _ = stream.Seek(0, io.SeekStart)
url := fmt.Sprintf("http://%v/cgi-bin/httpconn?htcmd=0x6FF0087&uin=%v", c.srvSsoAddrs[0], c.Uin)
var (
rspExt []byte
offset int64 = 0
chunkSize = 524288
)
chunk := make([]byte, chunkSize)
w := binary.NewWriter()
w.Reset()
w.Grow(600 * 1024) // 复用,600k 不要放回池中
for {
chunk = chunk[:chunkSize]
rl, err := io.ReadFull(stream, 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: strconv.FormatInt(c.Uin, 10),
Command: "PicUp.DataUp",
Seq: c.nextGroupDataTransSeq(),
Appid: int32(c.version.AppId),
Dataflag: 0,
CommandId: cmdId,
LocaleId: 0,
},
MsgSeghead: &pb.SegHead{
Filesize: fileLength,
Dataoffset: offset,
Datalength: int32(rl),
Serviceticket: ticket,
Md5: ch[:],
FileMd5: fileMd5,
},
ReqExtendinfo: ext,
})
offset += int64(rl)
w.Reset()
w.WriteByte(40)
w.WriteUInt32(uint32(len(head)))
w.WriteUInt32(uint32(len(chunk)))
w.Write(head)
w.Write(chunk)
w.WriteByte(41)
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 (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",
c.getSKey(),
c.Uin,
groupCode,
len(img),
)
req, _ := http.NewRequest("POST", url, bytes.NewReader(img))
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=****"}
rsp, err := http.DefaultClient.Do(req)
if err != nil {
return errors.Wrap(err, "failed to upload group head portrait")
}
rsp.Body.Close()
return nil
}

View File

@ -5,67 +5,21 @@ 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"
"google.golang.org/protobuf/proto"
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb/richmedia" "github.com/Mrs4s/MiraiGo/client/pb/richmedia"
"github.com/Mrs4s/MiraiGo/internal/proto"
"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
} }
@ -124,15 +83,14 @@ func (c *QQClient) GetGroupHonorInfo(groupCode int64, honorType HonorType) (*Gro
/* -------- TextToSpeech -------- */ /* -------- TextToSpeech -------- */
func (c *QQClient) GetTts(text string) ([]byte, error) { func (c *QQClient) GetTts(text string) ([]byte, error) {
url := "https://textts.qq.com/cgi-bin/tts" apiUrl := "https://textts.qq.com/cgi-bin/tts"
bt, _ := json.Marshal(text) data := fmt.Sprintf(`{"appid": "201908021016","sendUin": %v,"text": %q}`, c.Uin, text)
data := fmt.Sprintf(`{"appid": "201908021016","sendUin": %v,"text": %s}`, c.Uin, bt) rsp, err := utils.HttpPostBytesWithCookie(apiUrl, []byte(data), c.getCookies())
rsp, err := utils.HttpPostBytesWithCookie(url, []byte(data), c.getCookies())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to post to tts server") return nil, errors.Wrap(err, "failed to post to tts server")
} }
ttsReader := binary.NewReader(rsp) ttsReader := binary.NewReader(rsp)
ttsWriter := binary.NewWriter() ttsWriter := binary.SelectWriter()
for { for {
// 数据格式 69e(字符串) 十六进制 数据长度 0 为结尾 // 数据格式 69e(字符串) 十六进制 数据长度 0 为结尾
// 0D 0A (分隔符) payload 0D 0A // 0D 0A (分隔符) payload 0D 0A
@ -166,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"`
@ -178,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")
} }
@ -219,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")
} }
@ -240,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,27 +1,22 @@
package client package client
import ( import (
"bytes" "crypto/rand"
"encoding/hex"
"fmt" "fmt"
"image"
_ "image/gif"
"io" "io"
"math/rand"
"os"
"strings" "strings"
"time" "time"
"github.com/Mrs4s/MiraiGo/internal/packets" "github.com/fumiama/imgsz"
"github.com/Mrs4s/MiraiGo/client/pb/highway"
"github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"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/pb/cmd0x388" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x388"
highway2 "github.com/Mrs4s/MiraiGo/client/pb/highway"
"github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
@ -34,17 +29,61 @@ func init() {
var imgWaiter = utils.NewUploadWaiter() var imgWaiter = utils.NewUploadWaiter()
func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*message.GroupImageElement, error) { type imageUploadResponse struct {
UploadKey []byte
UploadIp []uint32
UploadPort []uint32
Width int32
Height int32
Message string
DownloadIndex string
ResourceId string
FileId int64
ResultCode int32
IsExists bool
}
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)
key := hex.EncodeToString(fh) key := string(fh)
imgWaiter.Wait(key) imgWaiter.Wait(key)
defer imgWaiter.Done(key) defer imgWaiter.Done(key)
seq, pkt := c.buildGroupImageStorePacket(groupCode, fh, int32(length)) cmd := int32(2)
r, err := c.sendAndWait(seq, pkt) 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
} }
@ -55,76 +94,55 @@ func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*messag
if rsp.IsExists { if rsp.IsExists {
goto ok goto ok
} }
if len(c.srvSsoAddrs) == 0 { if c.highwaySession.AddrLength() == 0 {
for i, addr := range rsp.UploadIp { for i, addr := range rsp.UploadIp {
c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr), rsp.UploadPort[i])) c.highwaySession.AppendAddr(addr, rsp.UploadPort[i])
} }
} }
if _, err = c.highwayUploadByBDH(img, length, 2, rsp.UploadKey, fh, EmptyBytes, false); err == nil {
goto ok input = highway.Transaction{
CommandID: cmd,
Body: img,
Size: length,
Sum: fh,
Ticket: rsp.UploadKey,
Ext: ext,
}
_, err = c.highwaySession.Upload(input)
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, _, _ := image.DecodeConfig(img) i, t, _ := imgsz.DecodeSize(img)
var imageType int32 = 1000 var imageType int32 = 1000
_, _ = img.Seek(0, io.SeekStart) if t == "gif" {
tmp := make([]byte, 4)
_, _ = img.Read(tmp)
if bytes.Equal(tmp, []byte{0x47, 0x49, 0x46, 0x38}) {
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
seq, pkt := c.buildGroupImageStorePacket(groupCode, fh, int32(length))
r, err := c.sendAndWait(seq, pkt)
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 len(c.srvSsoAddrs) == 0 { ImageType: imageType,
for i, addr := range rsp.UploadIp { Md5: fh,
c.srvSsoAddrs = append(c.srvSsoAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr), rsp.UploadPort[i])) }, nil
}
}
if _, err = c.highwayUploadFileMultiThreadingByBDH(path, 2, 1, rsp.UploadKey, EmptyBytes, false); err == nil {
goto ok
}
return nil, errors.Wrap(err, "upload failed")
ok:
_, _ = img.Seek(0, io.SeekStart)
i, _, _ := 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
}
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) {
@ -135,15 +153,23 @@ func (c *QQClient) GetGroupImageDownloadUrl(fileId, groupCode int64, fileMd5 []b
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 {
@ -154,21 +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, int64(e.Size), e.Md5); err != nil { if url, err = c.uploadOcrImage(b, e.Size, e.Md5); err != nil {
url = e.Url url = e.Url
} }
_ = b.Close() _ = b.Close()
} }
rsp, err := c.sendAndWait(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))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -205,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)
} }
@ -212,12 +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) buildGroupImageStorePacket(groupCode int64, md5 []byte, size int32) (uint16, []byte) { func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 []byte, size int32) (uint16, []byte) {
seq := c.nextSeq()
name := utils.RandomString(16) + ".gif" name := utils.RandomString(16) + ".gif"
req := &cmd0x388.D388ReqBody{ req := &cmd0x388.D388ReqBody{
NetType: proto.Uint32(3), NetType: proto.Uint32(3),
@ -242,12 +273,10 @@ func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 []byte, size
Extension: EmptyBytes, Extension: EmptyBytes,
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "ImgStore.GroupPicUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("ImgStore.GroupPicUp", payload)
return seq, packet
} }
func (c *QQClient) buildGroupImageDownloadPacket(fileId, groupCode int64, fileMd5 []byte) (uint16, []byte) { func (c *QQClient) buildGroupImageDownloadPacket(fileId, groupCode int64, fileMd5 []byte) (uint16, []byte) {
seq := c.nextSeq()
req := &cmd0x388.D388ReqBody{ req := &cmd0x388.D388ReqBody{
NetType: proto.Uint32(3), NetType: proto.Uint32(3),
Subcmd: proto.Uint32(2), Subcmd: proto.Uint32(2),
@ -268,31 +297,37 @@ func (c *QQClient) buildGroupImageDownloadPacket(fileId, groupCode int64, fileMd
}, },
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "ImgStore.GroupPicDown", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("ImgStore.GroupPicDown", payload)
return seq, packet
} }
func (c *QQClient) uploadOcrImage(img io.Reader, length int64, sum []byte) (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(&highway.CommFileExtReq{ ext, _ := proto.Marshal(&highway2.CommFileExtReq{
ActionType: proto.Uint32(0), ActionType: proto.Uint32(0),
Uuid: binary.GenUUID(r), Uuid: binary.GenUUID(r),
}) })
rsp, err := c.highwayUploadByBDH(img, length, 76, c.bigDataSession.SigSession, sum, ext, false)
rsp, err := c.highwaySession.Upload(highway.Transaction{
CommandID: 76,
Body: img,
Size: int64(size),
Sum: sum,
Ticket: c.highwaySession.SigSession,
Ext: ext,
})
if err != nil { if err != nil {
return "", errors.Wrap(err, "upload ocr image error") return "", errors.Wrap(err, "upload ocr image error")
} }
rspExt := highway.CommFileExtRsp{} rspExt := highway2.CommFileExtRsp{}
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) (uint16, []byte) { func (c *QQClient) buildImageOcrRequestPacket(url, md5 string, size, weight, height int32) (uint16, []byte) {
seq := c.nextSeq()
body := &oidb.DE07ReqBody{ body := &oidb.DE07ReqBody{
Version: 1, Version: 1,
Entrance: 3, Entrance: 3,
@ -308,61 +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)
packet := packets.BuildUniPacket(c.Uin, seq, "OidbSvc.0xe07_0", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("OidbSvc.0xe07_0", payload)
return seq, packet
} }
// ImgStore.GroupPicUp // ImgStore.GroupPicUp
func decodeGroupImageStoreResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGroupImageStoreResponse(_ *QQClient, packet *network.Packet) (any, error) {
pkt := cmd0x388.D388RspBody{} pkt := cmd0x388.D388RspBody{}
err := proto.Unmarshal(payload, &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, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeGroupImageDownloadResponse(_ *QQClient, pkt *network.Packet) (any, error) {
pkt := cmd0x388.D388RspBody{} rsp := cmd0x388.D388RspBody{}
if err := proto.Unmarshal(payload, &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, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeImageOcrResponse(_ *QQClient, pkt *network.Packet) (any, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.DE07RspBody{} rsp := oidb.DE07RspBody{}
if err := proto.Unmarshal(payload, &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

@ -0,0 +1,261 @@
package auth
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/pkg/errors"
)
//go:generate stringer -type=ProtocolType -linecomment
type ProtocolType int
const (
Unset ProtocolType = iota
AndroidPhone // Android Phone
AndroidWatch // Android Watch
MacOS // MacOS
QiDian // 企点
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 {
ApkSign []byte
ApkId string
SortVersionName string
SdkVersion string
AppId uint32
SubAppId uint32
AppKey string
BuildTime uint32
SSOVersion uint32
MiscBitmap uint32
SubSigmap uint32
MainSigMap uint32
QUA string
Protocol ProtocolType
}
func (v *AppVersion) String() string {
return fmt.Sprintf("%s %s", v.Protocol.String(), v.SortVersionName)
}
func (v *AppVersion) UpdateFromJson(d []byte) error {
var f appVersionFile
if err := json.Unmarshal(d, &f); err != nil {
return errors.Wrap(err, "failed to unmarshal json message")
}
// 按 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
}
func (i ProtocolType) Version() *AppVersion {
return AppVersions[i]
}
type SigInfo struct {
LoginBitmap uint64
TGT []byte
TGTKey []byte
SrmToken []byte // study room manager | 0x16a
T133 []byte
EncryptedA1 []byte
UserStKey []byte
UserStWebSig []byte
SKey []byte
SKeyExpiredTime int64
D2 []byte
D2Key []byte
DeviceToken []byte
PsKeyMap map[string][]byte
Pt4TokenMap map[string][]byte
// Others
OutPacketSessionID []byte
Dpwd []byte
// tlv cache
T104 []byte
T174 []byte
G []byte
T402 []byte
RandSeed []byte // t403
T547 []byte
// rollbackSig []byte
// t149 []byte
// t150 []byte
// t528 []byte
// t530 []byte
// sync info
SyncCookie []byte
PubAccountCookie []byte
Ksid []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

@ -0,0 +1,224 @@
package auth
import (
"crypto/md5"
"crypto/rand"
"encoding/hex"
"encoding/json"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/internal/proto"
)
type OSVersion struct {
Incremental []byte
Release []byte
CodeName []byte
SDK uint32
}
type Device struct {
Display []byte
Product []byte
Device []byte
Board []byte
Brand []byte
Model []byte
Bootloader []byte
FingerPrint []byte
BootId []byte
ProcVersion []byte
BaseBand []byte
SimInfo []byte
OSType []byte
MacAddress []byte
IpAddress []byte
WifiBSSID []byte
WifiSSID []byte
IMSIMd5 []byte
IMEI string
AndroidId []byte
APN []byte
VendorName []byte
VendorOSName []byte
Guid []byte
TgtgtKey []byte
QImei16 string
QImei36 string
Protocol ProtocolType
Version *OSVersion
}
func (info *Device) ToJson() []byte {
f := &deviceFile{
Display: string(info.Display),
Product: string(info.Product),
Device: string(info.Device),
Board: string(info.Board),
Model: string(info.Model),
FingerPrint: string(info.FingerPrint),
BootId: string(info.BootId),
ProcVersion: string(info.ProcVersion),
IMEI: info.IMEI,
Brand: string(info.Brand),
Bootloader: string(info.Bootloader),
BaseBand: string(info.BaseBand),
AndroidId: string(info.AndroidId),
Version: &osVersionFile{
Incremental: string(info.Version.Incremental),
Release: string(info.Version.Release),
Codename: string(info.Version.CodeName),
Sdk: info.Version.SDK,
},
SimInfo: string(info.SimInfo),
OsType: string(info.OSType),
MacAddress: string(info.MacAddress),
IpAddress: []int32{int32(info.IpAddress[0]), int32(info.IpAddress[1]), int32(info.IpAddress[2]), int32(info.IpAddress[3])},
WifiBSSID: string(info.WifiBSSID),
WifiSSID: string(info.WifiSSID),
ImsiMd5: hex.EncodeToString(info.IMSIMd5),
Apn: string(info.APN),
VendorName: string(info.VendorName),
VendorOSName: string(info.VendorOSName),
Protocol: int(info.Protocol),
}
d, _ := json.Marshal(f)
return d
}
func (info *Device) ReadJson(d []byte) error {
var f deviceFile
if err := json.Unmarshal(d, &f); err != nil {
return errors.Wrap(err, "failed to unmarshal json message")
}
setIfNotEmpty := func(trg *[]byte, str string) {
if str != "" {
*trg = []byte(str)
}
}
setIfNotEmpty(&info.Display, f.Display)
setIfNotEmpty(&info.Product, f.Product)
setIfNotEmpty(&info.Device, f.Device)
setIfNotEmpty(&info.Board, f.Board)
setIfNotEmpty(&info.Brand, f.Brand)
setIfNotEmpty(&info.Model, f.Model)
setIfNotEmpty(&info.Bootloader, f.Bootloader)
setIfNotEmpty(&info.FingerPrint, f.FingerPrint)
setIfNotEmpty(&info.BootId, f.BootId)
setIfNotEmpty(&info.ProcVersion, f.ProcVersion)
setIfNotEmpty(&info.BaseBand, f.BaseBand)
setIfNotEmpty(&info.SimInfo, f.SimInfo)
setIfNotEmpty(&info.OSType, f.OsType)
setIfNotEmpty(&info.MacAddress, f.MacAddress)
if len(f.IpAddress) == 4 {
info.IpAddress = []byte{byte(f.IpAddress[0]), byte(f.IpAddress[1]), byte(f.IpAddress[2]), byte(f.IpAddress[3])}
}
setIfNotEmpty(&info.WifiBSSID, f.WifiBSSID)
setIfNotEmpty(&info.WifiSSID, f.WifiSSID)
if len(f.ImsiMd5) != 0 {
imsiMd5, err := hex.DecodeString(f.ImsiMd5)
if err != nil {
info.IMSIMd5 = imsiMd5
}
}
if f.IMEI != "" {
info.IMEI = f.IMEI
}
setIfNotEmpty(&info.APN, f.Apn)
setIfNotEmpty(&info.VendorName, f.VendorName)
setIfNotEmpty(&info.VendorOSName, f.VendorOSName)
setIfNotEmpty(&info.AndroidId, f.AndroidId)
if f.AndroidId == "" {
info.AndroidId = info.Display // ?
}
switch f.Protocol {
case 1, 2, 3, 4, 5, 6:
info.Protocol = ProtocolType(f.Protocol)
default:
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.GenNewTgtgtKey()
info.RequestQImei() // 应该可以缓存, 理论上同一设备每次请求都是一样的
return nil
}
func (info *Device) GenNewGuid() {
t := md5.Sum(append(info.AndroidId, info.MacAddress...))
info.Guid = t[:]
}
func (info *Device) GenNewTgtgtKey() {
r := make([]byte, 16)
rand.Read(r)
h := md5.New()
h.Write(r)
h.Write(info.Guid)
info.TgtgtKey = h.Sum(nil)
}
func (info *Device) GenDeviceInfoData() []byte {
m := &pb.DeviceInfo{
Bootloader: string(info.Bootloader),
ProcVersion: string(info.ProcVersion),
Codename: string(info.Version.CodeName),
Incremental: string(info.Version.Incremental),
Fingerprint: string(info.FingerPrint),
BootId: string(info.BootId),
AndroidId: string(info.AndroidId),
BaseBand: string(info.BaseBand),
InnerVersion: string(info.Version.Incremental),
}
data, err := proto.Marshal(m)
if err != nil {
panic(errors.Wrap(err, "failed to unmarshal protobuf message"))
}
return data
}
type deviceFile struct {
Display string `json:"display"`
Product string `json:"product"`
Device string `json:"device"`
Board string `json:"board"`
Model string `json:"model"`
FingerPrint string `json:"finger_print"`
BootId string `json:"boot_id"`
ProcVersion string `json:"proc_version"`
Protocol int `json:"protocol"` // 0: Pad 1: Phone 2: Watch
IMEI string `json:"imei"`
Brand string `json:"brand"`
Bootloader string `json:"bootloader"`
BaseBand string `json:"base_band"`
Version *osVersionFile `json:"version"`
SimInfo string `json:"sim_info"`
OsType string `json:"os_type"`
MacAddress string `json:"mac_address"`
IpAddress []int32 `json:"ip_address"`
WifiBSSID string `json:"wifi_bssid"`
WifiSSID string `json:"wifi_ssid"`
ImsiMd5 string `json:"imsi_md5"`
AndroidId string `json:"android_id"`
Apn string `json:"apn"`
VendorName string `json:"vendor_name"`
VendorOSName string `json:"vendor_os_name"`
}
type osVersionFile struct {
Incremental string `json:"incremental"`
Release string `json:"release"`
Codename string `json:"codename"`
Sdk uint32 `json:"sdk"`
}

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

@ -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

@ -0,0 +1,224 @@
package highway
import (
"crypto/md5"
"io"
"sync"
"sync/atomic"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/internal/proto"
)
type Transaction struct {
CommandID int32
Body io.Reader
Sum []byte // md5 sum of body
Size int64 // body size
Ticket []byte
Ext []byte
Encrypt bool
}
func (bdh *Transaction) encrypt(key []byte) error {
if !bdh.Encrypt {
return nil
}
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
}
func (s *Session) uploadSingle(trans Transaction) ([]byte, error) {
pc, err := s.selectConn()
if err != nil {
return nil, err
}
defer s.putIdleConn(pc)
reader := binary.NewNetworkReader(pc.conn)
const chunkSize = 128 * 1024
var rspExt []byte
offset := 0
chunk := make([]byte, chunkSize)
for {
chunk = chunk[:cap(chunk)]
rl, err := io.ReadFull(trans.Body, chunk)
if rl == 0 {
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: _REQ_CMD_DATA,
Seq: s.nextSeq(),
Appid: s.AppID,
Dataflag: 4096,
CommandId: trans.CommandID,
LocaleId: 2052,
},
MsgSeghead: &pb.SegHead{
Filesize: trans.Size,
Dataoffset: int64(offset),
Datalength: int32(rl),
Serviceticket: trans.Ticket,
Md5: ch[:],
FileMd5: trans.Sum,
},
ReqExtendinfo: trans.Ext,
})
offset += rl
buffers := frame(head, chunk)
_, err = buffers.WriteTo(pc.conn)
if err != nil {
return nil, errors.Wrap(err, "write conn error")
}
rspHead, err := readResponse(reader)
if err != nil {
return nil, errors.Wrap(err, "highway upload error")
}
if rspHead.ErrorCode != 0 {
return nil, errors.Errorf("upload failed: %d", rspHead.ErrorCode)
}
if rspHead.RspExtendinfo != nil {
rspExt = rspHead.RspExtendinfo
}
if rspHead.MsgSeghead != nil && rspHead.MsgSeghead.Serviceticket != nil {
trans.Ticket = rspHead.MsgSeghead.Serviceticket
}
}
return rspExt, nil
}
func (s *Session) Upload(trans Transaction) ([]byte, error) {
// encrypt ext data
if err := trans.encrypt(s.SessionKey); err != nil {
return nil, err
}
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 {
return nil, err
}
addr := pc.addr
s.putIdleConn(pc)
const blockSize int64 = 256 * 1024
var (
rspExt []byte
completedThread uint32
cond = sync.NewCond(&sync.Mutex{})
offset = int64(0)
count = (trans.Size + blockSize - 1) / blockSize
id = 0
)
doUpload := func() error {
// send signal complete uploading
defer func() {
atomic.AddUint32(&completedThread, 1)
cond.Signal()
}()
// todo: get from pool?
pc, err := s.connect(addr)
if err != nil {
return err
}
defer s.putIdleConn(pc)
reader := binary.NewNetworkReader(pc.conn)
chunk := make([]byte, blockSize)
for {
cond.L.Lock() // lock protect reading
off := offset
offset += blockSize
id++
last := int64(id) == count
if last { // last
for atomic.LoadUint32(&completedThread) != uint32(threadCount-1) {
cond.Wait()
}
} else if int64(id) > count {
cond.L.Unlock()
break
}
chunk = chunk[:blockSize]
n, err := io.ReadFull(trans.Body, chunk)
cond.L.Unlock()
if n == 0 {
break
}
if errors.Is(err, io.ErrUnexpectedEOF) {
chunk = chunk[:n]
}
ch := md5.Sum(chunk)
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: s.Uin,
Command: _REQ_CMD_DATA,
Seq: s.nextSeq(),
Appid: s.AppID,
Dataflag: 4096,
CommandId: trans.CommandID,
LocaleId: 2052,
},
MsgSeghead: &pb.SegHead{
Filesize: trans.Size,
Dataoffset: off,
Datalength: int32(n),
Serviceticket: trans.Ticket,
Md5: ch[:],
FileMd5: trans.Sum,
},
ReqExtendinfo: trans.Ext,
})
buffers := frame(head, chunk)
_, err = buffers.WriteTo(pc.conn)
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.Errorf("upload failed: %d", rspHead.ErrorCode)
}
if last && rspHead.RspExtendinfo != nil {
rspExt = rspHead.RspExtendinfo
}
}
return nil
}
group := errgroup.Group{}
for i := 0; i < threadCount; i++ {
group.Go(doUpload)
}
return rspExt, group.Wait()
}

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

@ -0,0 +1,259 @@
package highway
import (
"fmt"
"net"
"runtime"
"sync"
"sync/atomic"
"time"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/internal/proto"
)
// 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 {
Uin string
AppID int32
SigSession []byte
SessionKey []byte
seq int32
addrMu sync.Mutex
idx int
SsoAddr []Addr
idleMu sync.Mutex
idleCount int
idle *idle
}
const highwayMaxResponseSize int32 = 1024 * 100 // 100k
func (s *Session) AddrLength() int {
s.addrMu.Lock()
defer s.addrMu.Unlock()
return len(s.SsoAddr)
}
func (s *Session) AppendAddr(ip, port uint32) {
s.addrMu.Lock()
defer s.addrMu.Unlock()
addr := Addr{
IP: ip,
Port: int(port),
}
s.SsoAddr = append(s.SsoAddr, addr)
}
func (s *Session) nextSeq() int32 {
return atomic.AddInt32(&s.seq, 2)
}
func (s *Session) sendHeartbreak(conn net.Conn) error {
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: s.Uin,
Command: _REQ_CMD_HEART_BREAK,
Seq: s.nextSeq(),
Appid: s.AppID,
Dataflag: 4096,
CommandId: 0,
LocaleId: 2052,
},
})
buffers := frame(head, nil)
_, err := buffers.WriteTo(conn)
return err
}
func (s *Session) ping(pc *persistConn) error {
start := time.Now()
err := s.sendHeartbreak(pc.conn)
if err != nil {
return errors.Wrap(err, "echo error")
}
if _, err = readResponse(binary.NewNetworkReader(pc.conn)); err != nil {
return errors.Wrap(err, "echo error")
}
// update delay
pc.ping = time.Since(start).Milliseconds()
return nil
}
func readResponse(r *binary.NetworkReader) (*pb.RspDataHighwayHead, error) {
_, err := r.ReadByte()
if err != nil {
return nil, errors.Wrap(err, "failed to read byte")
}
hl, _ := r.ReadInt32()
a2, _ := r.ReadInt32()
if hl > highwayMaxResponseSize || a2 > highwayMaxResponseSize {
return nil, errors.Errorf("highway response invild. head size: %v body size: %v", hl, a2)
}
head, _ := r.ReadBytes(int(hl))
_, _ = r.ReadBytes(int(a2)) // skip payload
_, _ = r.ReadByte()
rsp := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(head, rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
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

@ -1,4 +1,4 @@
package utils package network
import ( import (
"encoding/binary" "encoding/binary"
@ -9,43 +9,45 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type TCPListener struct { type TCPClient struct {
lock sync.RWMutex lock sync.RWMutex
conn net.Conn conn net.Conn
plannedDisconnect func(*TCPListener) connected bool
unexpectedDisconnect func(*TCPListener, error) plannedDisconnect func(*TCPClient)
unexpectedDisconnect func(*TCPClient, error)
} }
var ErrConnectionClosed = errors.New("connection closed") var ErrConnectionClosed = errors.New("connection closed")
// PlannedDisconnect 预料中的断开连接 // PlannedDisconnect 预料中的断开连接
// 如调用 Close() Connect() // 如调用 Close() Connect()
func (t *TCPListener) PlannedDisconnect(f func(*TCPListener)) { func (t *TCPClient) PlannedDisconnect(f func(*TCPClient)) {
t.lock.Lock() t.lock.Lock()
defer t.lock.Unlock() defer t.lock.Unlock()
t.plannedDisconnect = f t.plannedDisconnect = f
} }
// UnexpectedDisconnect 未预料的断开连接 // UnexpectedDisconnect 未预料的断开连接
func (t *TCPListener) UnexpectedDisconnect(f func(*TCPListener, error)) { func (t *TCPClient) UnexpectedDisconnect(f func(*TCPClient, error)) {
t.lock.Lock() t.lock.Lock()
defer t.lock.Unlock() defer t.lock.Unlock()
t.unexpectedDisconnect = f t.unexpectedDisconnect = f
} }
func (t *TCPListener) Connect(addr *net.TCPAddr) error { func (t *TCPClient) Connect(addr string) 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.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
} }
func (t *TCPListener) Write(buf []byte) error { func (t *TCPClient) 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 {
@ -58,7 +60,7 @@ func (t *TCPListener) Write(buf []byte) error {
return ErrConnectionClosed 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)
@ -73,7 +75,7 @@ func (t *TCPListener) ReadBytes(len int) ([]byte, error) {
return nil, ErrConnectionClosed 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
@ -81,17 +83,17 @@ 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() {
t.lock.Lock() t.lock.Lock()
defer t.lock.Unlock() defer t.lock.Unlock()
if t.conn != nil { if t.conn != nil {
@ -100,23 +102,25 @@ func (t *TCPListener) close() {
} }
} }
func (t *TCPListener) invokePlannedDisconnect() { func (t *TCPClient) invokePlannedDisconnect() {
t.lock.RLock() t.lock.RLock()
defer t.lock.RUnlock() defer t.lock.RUnlock()
if t.plannedDisconnect != nil { if t.plannedDisconnect != nil && t.connected {
go t.plannedDisconnect(t) go t.plannedDisconnect(t)
t.connected = false
} }
} }
func (t *TCPListener) invokeUnexpectedDisconnect(err error) { func (t *TCPClient) invokeUnexpectedDisconnect(err error) {
t.lock.RLock() t.lock.RLock()
defer t.lock.RUnlock() defer t.lock.RUnlock()
if t.unexpectedDisconnect != nil { if t.unexpectedDisconnect != nil && t.connected {
go t.unexpectedDisconnect(t, err) 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

@ -0,0 +1,25 @@
package network
type RequestType uint32
const (
RequestTypeLogin = 0x0A
RequestTypeSimple = 0x0B
)
type EncryptType uint32
const (
EncryptTypeNoEncrypt EncryptType = iota // 0x00
EncryptTypeD2Key // 0x01
EncryptTypeEmptyKey // 0x02
)
type Request struct {
Type RequestType
EncryptType EncryptType
SequenceID int32
Uin int64
CommandName string
Body []byte
}

View File

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

View File

@ -0,0 +1,232 @@
package network
import (
"strconv"
"strings"
"github.com/Mrs4s/MiraiGo/binary"
"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.
type Transport struct {
Sig *auth.SigInfo
Version *auth.AppVersion
Device *auth.Device
// connection
// conn *TCPClient
}
var WhiteListCommands = `
ConnAuthSvr.fast_qq_login
ConnAuthSvr.sdk_auth_api
ConnAuthSvr.sdk_auth_api_emp
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoBarrage
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoComment
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoFollow
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoLike
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoPush
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.DoReply
FeedCloudSvr.trpc.feedcloud.commwriter.ComWriter.PublishFeed
FeedCloudSvr.trpc.videocircle.circleprofile.CircleProfile.SetProfile
friendlist.addFriend
friendlist.AddFriendReq
friendlist.ModifyGroupInfoReq
MessageSvc.PbSendMsg
MsgProxy.SendMsg
OidbSvc.0x4ff_9
OidbSvc.0x4ff_9_IMCore
OidbSvc.0x56c_6
OidbSvc.0x6d9_4
OidbSvc.0x758
OidbSvc.0x758_0
OidbSvc.0x758_1
OidbSvc.0x88d_0
OidbSvc.0x89a_0
OidbSvc.0x89b_1
OidbSvc.0x8a1_0
OidbSvc.0x8a1_7
OidbSvc.0x8ba
OidbSvc.0x9fa
OidbSvc.oidb_0x758
OidbSvcTrpcTcp.0x101e_1
OidbSvcTrpcTcp.0x101e_2
OidbSvcTrpcTcp.0x1100_1
OidbSvcTrpcTcp.0x1105_1
OidbSvcTrpcTcp.0x1107_1
OidbSvcTrpcTcp.0x55f_0
OidbSvcTrpcTcp.0x6d9_4
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) {
pos := w.FillUInt32()
if req.Type == RequestTypeLogin {
w.WriteUInt32(uint32(req.SequenceID))
w.WriteUInt32(t.Version.AppId)
w.WriteUInt32(t.Version.SubAppId)
w.Write([]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00})
tgt := t.Sig.TGT
if len(tgt) == 0 || len(tgt) == 4 {
w.WriteUInt32(0x04)
} else {
w.WriteUInt32(uint32(len(tgt) + 4))
w.Write(tgt)
}
}
w.WriteString(req.CommandName)
w.WriteUInt32(uint32(len(t.Sig.OutPacketSessionID) + 4))
w.Write(t.Sig.OutPacketSessionID)
if req.Type == RequestTypeLogin {
w.WriteString((*t.Device).IMEI)
w.WriteUInt32(0x04)
w.WriteUInt16(uint16(len(t.Sig.Ksid)) + 2)
w.Write(t.Sig.Ksid)
}
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.WriteUInt32(uint32(len(req.Body) + 4))
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.
func (t *Transport) PackPacket(req *Request) []byte {
// todo(wdvxdr): combine pack packet, send packet and return the response
if len(t.Sig.D2) == 0 {
req.EncryptType = EncryptTypeEmptyKey
}
w := binary.SelectWriter()
defer binary.PutWriter(w)
pos := w.FillUInt32()
// vvv w.Write(head) vvv
w.WriteUInt32(uint32(req.Type))
w.WriteByte(byte(req.EncryptType))
switch req.Type {
case RequestTypeLogin:
switch req.EncryptType {
case EncryptTypeD2Key:
w.WriteUInt32(uint32(len(t.Sig.D2) + 4))
w.Write(t.Sig.D2)
default:
w.WriteUInt32(4)
}
case RequestTypeSimple:
w.WriteUInt32(uint32(req.SequenceID))
}
w.WriteByte(0x00)
w.WriteString(strconv.FormatInt(req.Uin, 10))
// ^^^ w.Write(head) ^^^
w2 := binary.SelectWriter()
t.packBody(req, w2)
body := w2.Bytes()
// encrypt body
switch req.EncryptType {
case EncryptTypeD2Key:
body = binary.NewTeaCipher(t.Sig.D2Key).Encrypt(body)
case EncryptTypeEmptyKey:
emptyKey := make([]byte, 16)
body = binary.NewTeaCipher(emptyKey).Encrypt(body)
}
w.Write(body)
binary.PutWriter(w2)
w.WriteUInt32At(pos, uint32(w.Len()))
return append([]byte(nil), w.Bytes()...)
}

View File

@ -0,0 +1,65 @@
package oicq
import (
"crypto/ecdh"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"encoding/json"
"net/http"
"strconv"
)
// session is ecdh session in oicq.
type session struct {
SvrPublicKeyVer uint16
PublicKey []byte
ShareKey []byte
}
const serverPublicKey = "04EBCA94D733E399B2DB96EACDD3F69A8BB0F74224E2B44E3357812211D2E62EFBC91BB553098E25E33A799ADC7F76FEB208DA7C6522CDB0719A305180CC54A82E"
func newSession() *session {
e := &session{
SvrPublicKeyVer: 1,
}
key, _ := hex.DecodeString(serverPublicKey)
e.init(key)
return e
}
type pubKeyResp struct {
Meta struct {
PubKeyVer uint16 `json:"KeyVer"`
PubKey string `json:"PubKey"`
} `json:"PubKeyMeta"`
}
// fetchPubKey 从服务器获取PubKey
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))
if err != nil {
return
}
defer func() { _ = resp.Body.Close() }()
pubKey := pubKeyResp{}
err = json.NewDecoder(resp.Body).Decode(&pubKey)
if err != nil {
return
}
e.SvrPublicKeyVer = pubKey.Meta.PubKeyVer
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

@ -0,0 +1,149 @@
package oicq
import (
"crypto/rand"
goBinary "encoding/binary"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
)
type Codec struct {
ecdh *session
randomKey []byte
WtSessionTicketKey []byte
}
func NewCodec(uin int64) *Codec {
c := &Codec{
ecdh: newSession(),
randomKey: make([]byte, 16),
}
rand.Read(c.randomKey)
c.ecdh.fetchPubKey(uin)
return c
}
type EncryptionMethod byte
const (
EM_ECDH EncryptionMethod = iota
EM_ST
)
type Message struct {
Uin uint32
Command uint16
EncryptionMethod EncryptionMethod
Body []byte
}
func (c *Codec) Marshal(m *Message) []byte {
w := binary.SelectWriter()
defer binary.PutWriter(w)
w.WriteByte(0x02)
w.WriteUInt16(0) // len 占位
w.WriteUInt16(8001) // version?
w.WriteUInt16(m.Command)
w.WriteUInt16(1)
w.WriteUInt32(m.Uin)
w.WriteByte(0x03)
switch m.EncryptionMethod {
case EM_ECDH:
w.WriteByte(0x87)
case EM_ST:
w.WriteByte(0x45)
}
w.WriteByte(0)
w.WriteUInt32(2)
w.WriteUInt32(0)
w.WriteUInt32(0)
switch m.EncryptionMethod {
case EM_ECDH:
w.WriteByte(0x02)
w.WriteByte(0x01)
w.Write(c.randomKey)
w.WriteUInt16(0x01_31)
w.WriteUInt16(c.ecdh.SvrPublicKeyVer)
w.WriteUInt16(uint16(len(c.ecdh.PublicKey)))
w.Write(c.ecdh.PublicKey)
w.EncryptAndWrite(c.ecdh.ShareKey, m.Body)
case EM_ST:
w.WriteByte(0x01)
w.WriteByte(0x03)
w.Write(c.randomKey)
w.WriteUInt16(0x0102)
w.WriteUInt16(0x0000)
w.EncryptAndWrite(c.randomKey, m.Body)
}
w.WriteByte(0x03)
buf := make([]byte, len(w.Bytes()))
copy(buf, w.Bytes())
goBinary.BigEndian.PutUint16(buf[1:3], uint16(len(buf)))
return buf
}
var (
ErrUnknownFlag = errors.New("unknown flag")
ErrUnknownEncryptType = errors.New("unknown encrypt type")
)
func (c *Codec) Unmarshal(data []byte) (*Message, error) {
reader := binary.NewReader(data)
if flag := reader.ReadByte(); flag != 2 {
return nil, ErrUnknownFlag
}
m := new(Message)
reader.ReadUInt16() // len
reader.ReadUInt16() // version?
m.Command = reader.ReadUInt16()
reader.ReadUInt16() // 1?
m.Uin = uint32(reader.ReadInt32())
reader.ReadByte()
encryptType := reader.ReadByte()
reader.ReadByte()
switch encryptType {
case 0:
d := reader.ReadBytes(reader.Len() - 1)
defer func() {
if pan := recover(); pan != nil {
m.Body = binary.NewTeaCipher(c.randomKey).Decrypt(d)
}
}()
m.Body = binary.NewTeaCipher(c.ecdh.ShareKey).Decrypt(d)
case 3:
d := reader.ReadBytes(reader.Len() - 1)
m.Body = binary.NewTeaCipher(c.WtSessionTicketKey).Decrypt(d)
default:
return nil, ErrUnknownEncryptType
}
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

@ -42,15 +42,14 @@ type (
) )
func (c *QQClient) getGtk(domain string) int { func (c *QQClient) getGtk(domain string) int {
if psKey, ok := c.sigInfo.psKeyMap[domain]; ok { if psKey, ok := c.sig.PsKeyMap[domain]; ok {
accu := 5381 accu := 5381
for _, b := range psKey { for _, b := range psKey {
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,17 +1,26 @@
package client package client
import ( import (
"bytes"
"crypto/md5"
"fmt" "fmt"
"math"
"math/rand"
"strconv"
"strings"
"time"
"github.com/Mrs4s/MiraiGo/internal/packets" "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/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"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
) )
func init() { func init() {
@ -21,7 +30,6 @@ func init() {
// MultiMsg.ApplyUp // MultiMsg.ApplyUp
func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) (uint16, []byte) { func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) (uint16, []byte) {
seq := c.nextSeq()
req := &multimsg.MultiReqBody{ req := &multimsg.MultiReqBody{
Subcmd: 1, Subcmd: 1,
TermType: 5, TermType: 5,
@ -39,14 +47,13 @@ func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, grou
BuType: buType, BuType: buType,
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "MultiMsg.ApplyUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("MultiMsg.ApplyUp", payload)
return seq, packet
} }
// MultiMsg.ApplyUp // MultiMsg.ApplyUp
func decodeMultiApplyUpResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeMultiApplyUpResponse(_ *QQClient, pkt *network.Packet) (any, error) {
body := multimsg.MultiRspBody{} body := multimsg.MultiRspBody{}
if err := proto.Unmarshal(payload, &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 {
@ -64,7 +71,6 @@ func decodeMultiApplyUpResponse(_ *QQClient, _ *incomingPacketInfo, payload []by
// MultiMsg.ApplyDown // MultiMsg.ApplyDown
func (c *QQClient) buildMultiApplyDownPacket(resID string) (uint16, []byte) { func (c *QQClient) buildMultiApplyDownPacket(resID string) (uint16, []byte) {
seq := c.nextSeq()
req := &multimsg.MultiReqBody{ req := &multimsg.MultiReqBody{
Subcmd: 2, Subcmd: 2,
TermType: 5, TermType: 5,
@ -81,26 +87,34 @@ func (c *QQClient) buildMultiApplyDownPacket(resID string) (uint16, []byte) {
ReqChannelType: 2, ReqChannelType: 2,
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "MultiMsg.ApplyDown", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) return c.uniPacket("MultiMsg.ApplyDown", payload)
return seq, packet
} }
// MultiMsg.ApplyDown // MultiMsg.ApplyDown
func decodeMultiApplyDownResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeMultiApplyDownResponse(_ *QQClient, pkt *network.Packet) (any, error) {
body := multimsg.MultiRspBody{} body := multimsg.MultiRspBody{}
if err := proto.Unmarshal(payload, &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]
prefix := func() string {
if rsp.MsgExternInfo != nil && rsp.MsgExternInfo.ChannelType == 2 { if rsp.ThumbDownPara == nil {
return "https://ssl.htdata.qq.com" return nil, errors.New("message not found")
}
var prefix string
if rsp.MsgExternInfo != nil && rsp.MsgExternInfo.ChannelType == 2 {
prefix = "https://ssl.htdata.qq.com"
} else {
ma := body.MultimsgApplydownRsp[0]
if len(rsp.Uint32DownIp) == 0 || len(ma.Uint32DownPort) == 0 {
return nil, errors.New("message not found")
} }
return fmt.Sprintf("http://%s:%d", binary.UInt32ToIPV4Address(uint32(rsp.Uint32DownIp[0])), body.MultimsgApplydownRsp[0].Uint32DownPort[0]) 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 {
return nil, errors.Wrap(err, "failed to download by multi apply down") return nil, errors.Wrap(err, "failed to download by multi apply down")
@ -131,3 +145,181 @@ func decodeMultiApplyDownResponse(_ *QQClient, _ *incomingPacketInfo, payload []
} }
return &mt, nil return &mt, nil
} }
type forwardMsgLinker struct {
items map[string]*msg.PbMultiMsgItem
}
func (l *forwardMsgLinker) link(name string) *message.ForwardMessage {
item := l.items[name]
if item == nil {
return nil
}
nodes := make([]*message.ForwardNode, 0, len(item.Buffer.Msg))
for _, m := range item.Buffer.Msg {
name := m.Head.FromNick.Unwrap()
if m.Head.MsgType.Unwrap() == 82 && m.Head.GroupInfo != nil {
name = m.Head.GroupInfo.GroupCard.Unwrap()
}
msgElems := message.ParseMessageElems(m.Body.RichText.Elems)
for i, elem := range msgElems {
if forward, ok := elem.(*message.ForwardElement); ok {
if forward.FileName != "" {
msgElems[i] = l.link(forward.FileName) // 递归处理嵌套转发
}
}
}
gid := int64(0) // 给群号一个缺省值0防止在读合并转发的私聊内容时候会报错
if m.Head.GroupInfo != nil {
gid = m.Head.GroupInfo.GroupCode.Unwrap()
}
nodes = append(nodes, &message.ForwardNode{
GroupId: gid,
SenderId: m.Head.FromUin.Unwrap(),
SenderName: name,
Time: m.Head.MsgTime.Unwrap(),
Message: msgElems,
})
}
return &message.ForwardMessage{Nodes: nodes}
}
func (c *QQClient) GetForwardMessage(resID string) *message.ForwardMessage {
m := c.DownloadForwardMessage(resID)
if m == nil {
return nil
}
linker := forwardMsgLinker{
items: make(map[string]*msg.PbMultiMsgItem),
}
for _, item := range m.Items {
linker.items[item.FileName.Unwrap()] = item
}
return linker.link("MultiMsg")
}
func (c *QQClient) DownloadForwardMessage(resId string) *message.ForwardElement {
i, err := c.sendAndWait(c.buildMultiApplyDownPacket(resId))
if err != nil {
return nil
}
multiMsg := i.(*msg.PbMultiMsgTransmit)
if multiMsg.PbItemList == nil {
return nil
}
var pv bytes.Buffer
for i := 0; i < int(math.Min(4, float64(len(multiMsg.Msg)))); i++ {
m := multiMsg.Msg[i]
sender := m.Head.FromNick.Unwrap()
if m.Head.MsgType.Unwrap() == 82 && m.Head.GroupInfo != nil {
sender = m.Head.GroupInfo.GroupCard.Unwrap()
}
brief := message.ToReadableString(message.ParseMessageElems(multiMsg.Msg[i].Body.RichText.Elems))
fmt.Fprintf(&pv, `<title size="26" color="#777777">%s: %s</title>`, sender, brief)
}
return genForwardTemplate(
resId, pv.String(),
fmt.Sprintf("查看 %d 条转发消息", len(multiMsg.Msg)),
time.Now().UnixNano(),
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,16 @@ package client
import ( import (
"net" "net"
"net/netip"
"runtime/debug" "runtime/debug"
"strings" "sort"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/utils"
) )
@ -35,62 +35,121 @@ type ConnectionQualityInfo struct {
} }
func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo {
if !c.Online { if !c.Online.Load() {
return nil return nil
} }
r := &ConnectionQualityInfo{} r := &ConnectionQualityInfo{}
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(2) wg.Add(2)
go func(w *sync.WaitGroup) {
currentServerAddr := c.servers[c.currServerIndex].String()
go func() {
defer wg.Done()
var err error var err error
if r.ChatServerLatency, err = qualityTest(c.servers[c.currServerIndex].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 len(c.srvSsoAddrs) > 0 { if c.highwaySession.AddrLength() > 0 {
if r.SrvServerLatency, err = qualityTest(c.srvSsoAddrs[0]); 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
} }
} }
}()
w.Done() go func() {
}(&wg) defer wg.Done()
go func(w *sync.WaitGroup) { res := utils.RunTCPPingLoop(currentServerAddr, 10)
res := utils.RunICMPPingLoop(&net.IPAddr{IP: c.servers[c.currServerIndex].IP}, 10)
r.ChatServerPacketLoss = res.PacketsLoss r.ChatServerPacketLoss = res.PacketsLoss
if len(c.srvSsoAddrs) > 0 { if c.highwaySession.AddrLength() > 0 {
res = utils.RunICMPPingLoop(&net.IPAddr{IP: net.ParseIP(strings.Split(c.srvSsoAddrs[0], ":")[0])}, 10) res = utils.RunTCPPingLoop(c.highwaySession.SsoAddr[0].String(), 10)
r.SrvServerPacketLoss = res.PacketsLoss r.SrvServerPacketLoss = res.PacketsLoss
} }
w.Done() }()
}(&wg)
start := time.Now() start := time.Now()
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.Now().Sub(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) initServers() {
if c.Device() == nil {
// must have device. Use c.UseDevice to set it!
panic("client device is nil")
}
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
}
}
// connect 连接到 QQClient.servers 中的服务器 // connect 连接到 QQClient.servers 中的服务器
func (c *QQClient) connect() error { func (c *QQClient) connect() error {
c.Info("connect to server: %v", c.servers[c.currServerIndex].String()) // init qq servers
err := c.TCP.Connect(c.servers[c.currServerIndex]) c.initServerOnce.Do(c.initServers)
addr := c.servers[c.currServerIndex].String()
c.info("connect to server: %v", addr)
err := c.TCP.Connect(addr)
c.currServerIndex++ c.currServerIndex++
if c.currServerIndex == len(c.servers) { if c.currServerIndex == len(c.servers) {
c.currServerIndex = 0 c.currServerIndex = 0
@ -100,24 +159,24 @@ 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.once.Do(func() {
c.OnGroupMessage(func(_ *QQClient, _ *message.GroupMessage) { c.GroupMessageEvent.Subscribe(func(_ *QQClient, _ *message.GroupMessage) {
atomic.AddUint64(&c.stat.MessageReceived, 1) c.stat.MessageReceived.Add(1)
atomic.StoreInt64(&c.stat.LastMessageTime, time.Now().Unix()) c.stat.LastMessageTime.Store(time.Now().Unix())
}) })
c.OnPrivateMessage(func(_ *QQClient, _ *message.PrivateMessage) { c.PrivateMessageEvent.Subscribe(func(_ *QQClient, _ *message.PrivateMessage) {
atomic.AddUint64(&c.stat.MessageReceived, 1) c.stat.MessageReceived.Add(1)
atomic.StoreInt64(&c.stat.LastMessageTime, time.Now().Unix()) c.stat.LastMessageTime.Store(time.Now().Unix())
}) })
c.OnTempMessage(func(_ *QQClient, _ *TempMessageEvent) { c.TempMessageEvent.Subscribe(func(_ *QQClient, _ *TempMessageEvent) {
atomic.AddUint64(&c.stat.MessageReceived, 1) c.stat.MessageReceived.Add(1)
atomic.StoreInt64(&c.stat.LastMessageTime, time.Now().Unix()) c.stat.LastMessageTime.Store(time.Now().Unix())
}) })
c.onGroupMessageReceipt("internal", func(_ *QQClient, _ *groupMessageReceiptEvent) { c.onGroupMessageReceipt("internal", func(_ *QQClient, _ *groupMessageReceiptEvent) {
atomic.AddUint64(&c.stat.MessageSent, 1) c.stat.MessageSent.Add(1)
}) })
go c.netLoop() go c.netLoop()
}) })
@ -131,38 +190,38 @@ 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: "quick reconnect failed"}) 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
} }
} }
// Disconnect 中断连接, 不释放资源 // Disconnect 中断连接, 不释放资源
func (c *QQClient) Disconnect() { func (c *QQClient) Disconnect() {
c.Online = false c.Online.Store(false)
c.TCP.Close() c.TCP.Close()
} }
// sendAndWait 向服务器发送一个数据包, 并等待返回 // sendAndWait 向服务器发送一个数据包, 并等待返回
func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...requestParams) (interface{}, error) { func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.RequestParams) (any, error) {
type T struct { type T struct {
Response interface{} Response any
Error error Error error
} }
ch := make(chan T, 1) ch := make(chan T, 1)
var p requestParams var p network.RequestParams
if len(params) != 0 { if len(params) != 0 {
p = params[0] p = params[0]
} }
c.handlers.Store(seq, &handlerInfo{fun: func(i interface{}, err error) { c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) {
ch <- T{ ch <- T{
Response: i, Response: i,
Error: err, Error: err,
@ -196,9 +255,9 @@ func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...requestParams)
func (c *QQClient) sendPacket(pkt []byte) error { func (c *QQClient) sendPacket(pkt []byte) error {
err := c.TCP.Write(pkt) err := c.TCP.Write(pkt)
if err != nil { if err != nil {
atomic.AddUint64(&c.stat.PacketLost, 1) c.stat.PacketLost.Add(1)
} else { } else {
atomic.AddUint64(&c.stat.PacketSent, 1) c.stat.PacketSent.Add(1)
} }
return errors.Wrap(err, "Packet failed to sendPacket") return errors.Wrap(err, "Packet failed to sendPacket")
} }
@ -206,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)
@ -215,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
@ -236,7 +295,7 @@ func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, fil
// 发送数据包并返回需要解析的 response // 发送数据包并返回需要解析的 response
func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) { func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) {
ch := make(chan []byte, 1) ch := make(chan []byte, 1)
c.handlers.Store(seq, &handlerInfo{fun: func(i interface{}, err error) { ch <- i.([]byte) }, dynamic: true}) c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) { ch <- i.([]byte) }, dynamic: true})
err := c.sendPacket(pkt) err := c.sendPacket(pkt)
if err != nil { if err != nil {
c.handlers.Delete(seq) c.handlers.Delete(seq)
@ -251,27 +310,34 @@ func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) {
} }
} }
// 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(_ *utils.TCPListener) { func (c *QQClient) plannedDisconnect(_ *network.TCPClient) {
c.Debug("planned disconnect.") c.debug("planned disconnect.")
atomic.AddUint32(&c.stat.DisconnectTimes, 1) c.stat.DisconnectTimes.Add(1)
c.Online = false c.Online.Store(false)
} }
// unexpectedDisconnect 非预期断线事件 // unexpectedDisconnect 非预期断线事件
func (c *QQClient) unexpectedDisconnect(_ *utils.TCPListener, e error) { func (c *QQClient) unexpectedDisconnect(_ *network.TCPClient, e error) {
c.Error("unexpected disconnect: %v", e) c.error("unexpected disconnect: %v", e)
atomic.AddUint32(&c.stat.DisconnectTimes, 1) c.stat.DisconnectTimes.Add(1)
c.Online = 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
} }
} }
@ -286,7 +352,7 @@ func (c *QQClient) netLoop() {
continue continue
} }
if l < 4 || l > 1024*1024*10 { // max 10MB if l < 4 || l > 1024*1024*10 { // max 10MB
c.Error("parse incoming packet error: invalid packet length %v", l) c.error("parse incoming packet error: invalid packet length %v", l)
errCount++ errCount++
if errCount > 2 { if errCount > 2 {
go c.quickReconnect() go c.quickReconnect()
@ -294,12 +360,13 @@ func (c *QQClient) netLoop() {
continue continue
} }
data, _ := c.TCP.ReadBytes(int(l) - 4) data, _ := c.TCP.ReadBytes(int(l) - 4)
pkt, err := packets.ParseIncomingPacket(data, c.sigInfo.d2Key) resp, err := c.transport.ReadResponse(data)
// pkt, err := packets.ParseIncomingPacket(data, c.sig.D2Key)
if err != nil { if err != nil {
c.Error("parse incoming packet error: %v", err) c.error("parse incoming packet error: %v", err)
if errors.Is(err, packets.ErrSessionExpired) || errors.Is(err, packets.ErrPacketDropped) { if errors.Is(err, network.ErrSessionExpired) || errors.Is(err, network.ErrPacketDropped) {
c.Disconnect() c.Disconnect()
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "session expired"}) go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "session expired"})
continue continue
} }
errCount++ errCount++
@ -308,53 +375,55 @@ func (c *QQClient) netLoop() {
} }
continue continue
} }
if pkt.Flag2 == 2 { if resp.EncryptType == network.EncryptTypeEmptyKey {
pkt.Payload, err = pkt.DecryptPayload(c.ecdh.InitialShareKey, c.RandomKey, c.sigInfo.wtSessionTicketKey) m, err := c.oicq.Unmarshal(resp.Body)
if err != nil { if err != nil {
c.Error("decrypt payload error: %v", err) c.error("decrypt payload error: %v", err)
if errors.Is(err, oicq.ErrUnknownFlag) {
go c.quickReconnect()
}
continue continue
} }
resp.Body = m.Body
} }
errCount = 0 errCount = 0
c.Debug("rev pkt: %v seq: %v", pkt.CommandName, pkt.SequenceId) c.debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID)
atomic.AddUint64(&c.stat.PacketReceived, 1) c.stat.PacketReceived.Add(1)
go func(pkt *packets.IncomingPacket) { pkt := &network.Packet{
SequenceId: uint16(resp.SequenceID),
CommandName: resp.CommandName,
Payload: resp.Body,
}
go func(pkt *network.Packet) {
defer func() { defer func() {
if pan := recover(); pan != nil { if pan := recover(); pan != nil {
c.Error("panic on decoder %v : %v\n%s", pkt.CommandName, pan, debug.Stack()) 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 { if decoder, ok := decoders[pkt.CommandName]; ok {
// found predefined decoder // found predefined decoder
info, ok := c.handlers.LoadAndDelete(pkt.SequenceId) info, ok := c.handlers.LoadAndDelete(pkt.SequenceId)
var decoded interface{} var decoded any
decoded = pkt.Payload decoded = pkt.Payload
if info == nil || !info.dynamic { if info == nil || !info.dynamic {
decoded, err = decoder(c, &incomingPacketInfo{ pkt.Params = info.getParams()
SequenceId: pkt.SequenceId, decoded, err = decoder(c, pkt)
CommandName: pkt.CommandName,
Params: func() requestParams {
if !ok {
return nil
}
return info.params
}(),
}, pkt.Payload)
if err != nil { if err != nil {
c.Debug("decode pkt %v error: %+v", pkt.CommandName, err) c.debug("decode pkt %v error: %+v", pkt.CommandName, err)
} }
} }
if ok { if ok {
info.fun(decoded, err) info.fun(decoded, err)
} else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait } else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait
f.(func(interface{}, error))(decoded, err) f(decoded, err)
} }
} else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok { } else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok {
// does not need decoder // does not need decoder
f.fun(pkt.Payload, nil) f.fun(pkt.Payload, nil)
} else { } else {
c.Debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId) c.debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId)
} }
}(pkt) }(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

@ -1,11 +1,11 @@
package client package client
import ( import (
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x346" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x346"
"github.com/Mrs4s/MiraiGo/internal/proto"
) )
func init() { func init() {
@ -29,22 +29,22 @@ func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, [
}, },
} }
payload, _ := proto.Marshal(req) payload, _ := proto.Marshal(req)
packet := packets.BuildUniPacket(c.Uin, seq, "OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload) packet := c.uniPacketWithSeq(seq, "OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200", payload)
return seq, packet return seq, packet
} }
func decodeOfflineFileDownloadResponse(c *QQClient, _ *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeOfflineFileDownloadResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := cmd0x346.C346RspBody{} rsp := cmd0x346.C346RspBody{}
if err := proto.Unmarshal(payload, &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

@ -5,15 +5,15 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/Mrs4s/MiraiGo/client/pb/msgtype0x210"
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"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/network"
"github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/client/pb/msgtype0x210"
"github.com/Mrs4s/MiraiGo/client/pb/notify" "github.com/Mrs4s/MiraiGo/client/pb/notify"
"github.com/Mrs4s/MiraiGo/internal/proto"
) )
var msg0x210Decoders = map[int64]func(*QQClient, []byte) error{ var msg0x210Decoders = map[int64]func(*QQClient, []byte) error{
@ -23,22 +23,21 @@ var msg0x210Decoders = map[int64]func(*QQClient, []byte) error{
} }
// OnlinePush.ReqPush // OnlinePush.ReqPush
func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []byte) (interface{}, error) { func decodeOnlinePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{} request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload)) 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:])
msgInfos := []jce.PushMessageInfo{}
uin := jr.ReadInt64(0) uin := jr.ReadInt64(0)
jr.ReadSlice(&msgInfos, 2) msgInfos := jr.ReadPushMessageInfos(2)
_ = c.sendPacket(c.buildDeleteOnlinePushPacket(uin, 0, nil, info.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)
@ -54,7 +53,17 @@ func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []
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,
@ -69,7 +78,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []
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,
@ -83,7 +92,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []
} }
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),
@ -92,7 +101,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []
} }
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),
@ -119,7 +128,7 @@ func decodeOnlinePushReqPacket(c *QQClient, info *incomingPacketInfo, payload []
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))
} }
} }
} }
@ -133,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,
@ -153,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
} }
@ -168,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
@ -182,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()),
}) })
} }
} }
@ -199,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")
} }
@ -222,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,
}) })
@ -239,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()
@ -258,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,
}) })

44
client/packet.go Normal file
View File

@ -0,0 +1,44 @@
package client
import (
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
)
//go:noinline
func (c *QQClient) buildOicqRequestPacket(uin int64, command uint16, body *oicq.TLV) []byte {
req := oicq.Message{
Uin: uint32(uin),
Command: command,
EncryptionMethod: oicq.EM_ECDH,
Body: body.Marshal(),
}
return c.oicq.Marshal(&req)
}
//go:noinline
func (c *QQClient) uniPacket(command string, body []byte) (uint16, []byte) {
seq := c.nextSeq()
req := network.Request{
Type: network.RequestTypeSimple,
EncryptType: network.EncryptTypeD2Key,
Uin: c.Uin,
SequenceID: int32(seq),
CommandName: command,
Body: body,
}
return seq, c.transport.PackPacket(&req)
}
//go:noinline
func (c *QQClient) uniPacketWithSeq(seq uint16, command string, body []byte) []byte {
req := network.Request{
Type: network.RequestTypeSimple,
EncryptType: network.EncryptTypeD2Key,
Uin: c.Uin,
SequenceID: int32(seq),
CommandName: command,
Body: body,
}
return c.transport.PackPacket(&req)
}

View File

@ -0,0 +1,76 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/channel/GuildChannelBase.proto
package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type ChannelUserInfo struct {
ClientIdentity *ClientIdentity `protobuf:"bytes,1,opt"`
MemberType proto.Option[uint32] `protobuf:"varint,2,opt"`
Permission *ChannelUserPermission `protobuf:"bytes,3,opt"`
RoleGroups []*BaseRoleGroupInfo `protobuf:"bytes,4,rep"`
}
type ChannelUserPermission struct {
AllowReadFeed proto.Option[bool] `protobuf:"varint,1,opt"`
AllowWriteFeed proto.Option[bool] `protobuf:"varint,2,opt"`
_ [0]func()
}
type ClientIdentity struct {
ClientId proto.Option[uint32] `protobuf:"varint,1,opt"`
Desc proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}
type BaseGuildInfo struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
JoinTime proto.Option[uint64] `protobuf:"varint,3,opt"`
_ [0]func()
}
type BaseRoleGroupInfo struct {
RoleId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
Color proto.Option[uint32] `protobuf:"varint,3,opt"`
_ [0]func()
}
type StChannelInfo struct {
Sign *StChannelSign `protobuf:"bytes,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
IconUrl proto.Option[string] `protobuf:"bytes,3,opt"`
_ [0]func()
}
type StChannelSign struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelId proto.Option[uint64] `protobuf:"varint,2,opt"`
_ [0]func()
}
type StEmotionReactionInfo struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
EmojiReactionList []*EmojiReaction `protobuf:"bytes,2,rep"`
}
type StCommonExt struct {
MapInfo []*CommonEntry `protobuf:"bytes,1,rep"`
AttachInfo proto.Option[string] `protobuf:"bytes,2,opt"`
MapBytesInfo []*BytesEntry `protobuf:"bytes,3,rep"`
}
type BytesEntry struct {
Key proto.Option[string] `protobuf:"bytes,1,opt"`
Value []byte `protobuf:"bytes,2,opt"`
}
type CommonEntry struct {
Key proto.Option[string] `protobuf:"bytes,1,opt"`
Value proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}

View File

@ -0,0 +1,78 @@
syntax = "proto2";
package channel;
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/MsgResponsesSvr.proto";
message ChannelUserInfo {
optional ClientIdentity clientIdentity = 1;
optional uint32 memberType = 2;
optional ChannelUserPermission permission = 3;
repeated BaseRoleGroupInfo roleGroups = 4;
}
message ChannelUserPermission {
optional bool allowReadFeed = 1;
optional bool allowWriteFeed = 2;
}
message ClientIdentity {
optional uint32 clientId = 1;
optional string desc = 2;
}
message BaseGuildInfo {
optional uint64 guildId = 1;
optional string name = 2;
optional uint64 joinTime = 3;
}
message BaseRoleGroupInfo {
optional uint64 roleId = 1;
optional string name = 2;
optional uint32 color = 3;
}
message StChannelInfo {
optional StChannelSign sign = 1;
optional string name = 2;
optional string iconUrl = 3;
}
message StChannelSign {
optional uint64 guildId = 1;
optional uint64 channelId = 2;
}
/*
message StEmojiReaction {
optional string emojiId = 1;
optional uint64 emojiType = 2;
optional uint64 cnt = 3;
optional bool isClicked = 4;
optional bool isDefaultEmoji = 10001;
}
*/
message StEmotionReactionInfo {
optional string id = 1;
repeated EmojiReaction emojiReactionList = 2;
}
message StCommonExt {
repeated CommonEntry mapInfo = 1;
optional string attachInfo = 2;
repeated BytesEntry mapBytesInfo = 3;
}
message BytesEntry {
optional string key = 1;
optional bytes value = 2;
}
message CommonEntry {
optional string key = 1;
optional string value = 2;
}

View File

@ -0,0 +1,686 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/channel/GuildFeedCloudMeta.proto
package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type ContentMetaData struct {
Count *RichTextContentCount `protobuf:"bytes,1,opt"`
ContentID proto.Option[int64] `protobuf:"varint,2,opt"`
_ [0]func()
}
type FeedMetaData struct {
Content *ContentMetaData `protobuf:"bytes,1,opt"`
LastModifiedTime proto.Option[uint64] `protobuf:"varint,2,opt"`
_ [0]func()
}
type FeedRedTouchTransInfo struct {
FeedId proto.Option[string] `protobuf:"bytes,1,opt"`
Author proto.Option[string] `protobuf:"bytes,2,opt"`
CreateTs proto.Option[int64] `protobuf:"varint,3,opt"`
MsgType proto.Option[int32] `protobuf:"varint,4,opt"`
PageType proto.Option[int32] `protobuf:"varint,5,opt"`
RedType proto.Option[int32] `protobuf:"varint,6,opt"`
InsertPageType proto.Option[int32] `protobuf:"varint,7,opt"`
_ [0]func()
}
type NoticeOperation struct {
Type proto.Option[uint32] `protobuf:"varint,1,opt"`
Schema proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}
type RichTextContentCount struct {
TextWord proto.Option[uint64] `protobuf:"varint,1,opt"`
At proto.Option[uint64] `protobuf:"varint,2,opt"`
Url proto.Option[uint64] `protobuf:"varint,3,opt"`
Emoji proto.Option[uint64] `protobuf:"varint,4,opt"`
Image proto.Option[uint64] `protobuf:"varint,5,opt"`
Video proto.Option[uint64] `protobuf:"varint,6,opt"`
_ [0]func()
}
type StAnimation struct {
Width proto.Option[uint32] `protobuf:"varint,1,opt"`
Height proto.Option[uint32] `protobuf:"varint,2,opt"`
AnimationUrl proto.Option[string] `protobuf:"bytes,3,opt"`
BusiData []byte `protobuf:"bytes,4,opt"`
}
type StBusiReportInfo struct {
RecomReport *StRecomReportInfo `protobuf:"bytes,1,opt"`
TraceID proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StChannelShareInfo struct {
FeedID proto.Option[string] `protobuf:"bytes,1,opt"`
PosterID proto.Option[string] `protobuf:"bytes,2,opt"`
FeedPublishAt proto.Option[uint64] `protobuf:"varint,3,opt"`
ChannelSign *StChannelSign `protobuf:"bytes,4,opt"`
UpdateDurationMs proto.Option[uint64] `protobuf:"varint,5,opt"`
Sign *StChannelShareSign `protobuf:"bytes,6,opt"`
_ [0]func()
}
type StChannelShareSign struct {
CreateAt proto.Option[uint64] `protobuf:"varint,1,opt"`
Token proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StCircleRankItem struct {
RankNo proto.Option[int32] `protobuf:"varint,1,opt"`
CircleName proto.Option[string] `protobuf:"bytes,2,opt"`
FuelValue proto.Option[int64] `protobuf:"varint,3,opt"`
FeedNum proto.Option[int64] `protobuf:"varint,4,opt"`
CircleID proto.Option[string] `protobuf:"bytes,5,opt"`
_ [0]func()
}
type StClientInfo struct {
Feedclientkey proto.Option[string] `protobuf:"bytes,1,opt"`
ClientMap []*CommonEntry `protobuf:"bytes,2,rep"`
}
type StComment struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
PostUser *StUser `protobuf:"bytes,2,opt"`
CreateTime proto.Option[uint64] `protobuf:"varint,3,opt"`
Content proto.Option[string] `protobuf:"bytes,4,opt"`
ReplyCount proto.Option[uint32] `protobuf:"varint,5,opt"`
VecReply []*StReply `protobuf:"bytes,6,rep"`
BusiData []byte `protobuf:"bytes,7,opt"`
LikeInfo *StLike `protobuf:"bytes,8,opt"`
TypeFlag proto.Option[uint32] `protobuf:"varint,9,opt"`
AtUinList []string `protobuf:"bytes,10,rep"`
TypeFlag2 proto.Option[uint32] `protobuf:"varint,11,opt"`
CreateTimeNs proto.Option[uint64] `protobuf:"varint,12,opt"`
StoreExtInfo []*CommonEntry `protobuf:"bytes,13,rep"`
ThirdId proto.Option[string] `protobuf:"bytes,14,opt"`
SourceType proto.Option[uint32] `protobuf:"varint,15,opt"`
RichContents *StRichText `protobuf:"bytes,16,opt"`
}
type StDebugInfo struct {
DebugMap []*CommonEntry `protobuf:"bytes,1,rep"`
}
type StDittoFeed struct {
DittoId proto.Option[uint32] `protobuf:"varint,1,opt"`
DittoPatternId proto.Option[uint32] `protobuf:"varint,2,opt"`
DittoData []byte `protobuf:"bytes,3,opt"`
DittoDataNew []byte `protobuf:"bytes,4,opt"`
}
type StExifInfo struct {
Kvs []*CommonEntry `protobuf:"bytes,1,rep"`
}
type StExternalMedalWallInfo struct {
NeedRedPoint proto.Option[bool] `protobuf:"varint,1,opt"`
MedalInfos []*StMedalInfo `protobuf:"bytes,2,rep"`
MedalWallJumpUrl proto.Option[string] `protobuf:"bytes,3,opt"`
NeedShowEntrance proto.Option[bool] `protobuf:"varint,4,opt"`
}
type StFeed struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Title *StRichText `protobuf:"bytes,2,opt"`
Subtitle *StRichText `protobuf:"bytes,3,opt"`
Poster *StUser `protobuf:"bytes,4,opt"`
Videos []*StVideo `protobuf:"bytes,5,rep"`
Contents *StRichText `protobuf:"bytes,6,opt"`
CreateTime proto.Option[uint64] `protobuf:"varint,7,opt"`
EmotionReaction *StEmotionReactionInfo `protobuf:"bytes,8,opt"`
CommentCount proto.Option[uint32] `protobuf:"varint,9,opt"`
VecComment []*StComment `protobuf:"bytes,10,rep"`
Share *StShare `protobuf:"bytes,11,opt"`
VisitorInfo *StVisitor `protobuf:"bytes,12,opt"`
Images []*StImage `protobuf:"bytes,13,rep"`
PoiInfo *StPoiInfoV2 `protobuf:"bytes,14,opt"`
TagInfos []*StTagInfo `protobuf:"bytes,15,rep"`
BusiReport []byte `protobuf:"bytes,16,opt"`
OpMask []uint32 `protobuf:"varint,17,rep"`
Opinfo *StOpinfo `protobuf:"bytes,18,opt"`
ExtInfo []*CommonEntry `protobuf:"bytes,19,rep"`
PatternInfo proto.Option[string] `protobuf:"bytes,20,opt"`
ChannelInfo *StChannelInfo `protobuf:"bytes,21,opt"`
CreateTimeNs proto.Option[uint64] `protobuf:"varint,22,opt"`
Summary *StFeedSummary `protobuf:"bytes,23,opt"`
RecomInfo *StRecomInfo `protobuf:"bytes,24,opt"`
Meta *FeedMetaData `protobuf:"bytes,25,opt"`
}
type StFeedAbstract struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Title proto.Option[string] `protobuf:"bytes,2,opt"`
Poster *StUser `protobuf:"bytes,3,opt"`
Pic *StImage `protobuf:"bytes,4,opt"`
Type proto.Option[uint32] `protobuf:"varint,5,opt"`
CreateTime proto.Option[uint64] `protobuf:"varint,6,opt"`
Video *StVideo `protobuf:"bytes,7,opt"`
FuelNum proto.Option[uint32] `protobuf:"varint,8,opt"`
Content proto.Option[string] `protobuf:"bytes,9,opt"`
Images []*StImage `protobuf:"bytes,10,rep"`
CountInfo *StFeedCount `protobuf:"bytes,11,opt"`
}
type StFeedCount struct {
Liked proto.Option[int64] `protobuf:"varint,1,opt"`
Push proto.Option[int64] `protobuf:"varint,2,opt"`
Comment proto.Option[int64] `protobuf:"varint,3,opt"`
Visitor proto.Option[int64] `protobuf:"varint,4,opt"`
_ [0]func()
}
type StFeedSummary struct {
LayoutType proto.Option[uint32] `protobuf:"varint,1,opt"`
_ [0]func()
}
type StFollowRecomInfo struct {
FollowText proto.Option[string] `protobuf:"bytes,1,opt"`
FollowUsers []*StFollowUser `protobuf:"bytes,4,rep"`
CommFriendText proto.Option[string] `protobuf:"bytes,6,opt"`
CommGroupText proto.Option[string] `protobuf:"bytes,7,opt"`
}
type StFollowUser struct {
Uid proto.Option[uint64] `protobuf:"varint,1,opt"`
Nick proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StGPSV2 struct {
Lat proto.Option[int64] `protobuf:"varint,1,opt"`
Lon proto.Option[int64] `protobuf:"varint,2,opt"`
EType proto.Option[int64] `protobuf:"varint,3,opt"`
Alt proto.Option[int64] `protobuf:"varint,4,opt"`
_ [0]func()
}
type StGuidePublishBubble struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
BackgroundImage *StImage `protobuf:"bytes,2,opt"`
JumpUrl proto.Option[string] `protobuf:"bytes,3,opt"`
_ [0]func()
}
type StIconInfo struct {
IconUrl40 proto.Option[string] `protobuf:"bytes,1,opt"`
IconUrl100 proto.Option[string] `protobuf:"bytes,2,opt"`
IconUrl140 proto.Option[string] `protobuf:"bytes,3,opt"`
IconUrl640 proto.Option[string] `protobuf:"bytes,4,opt"`
IconUrl proto.Option[string] `protobuf:"bytes,5,opt"`
_ [0]func()
}
type StImage struct {
Width proto.Option[uint32] `protobuf:"varint,1,opt"`
Height proto.Option[uint32] `protobuf:"varint,2,opt"`
PicUrl proto.Option[string] `protobuf:"bytes,3,opt"`
VecImageUrl []*StImageUrl `protobuf:"bytes,4,rep"`
PicId proto.Option[string] `protobuf:"bytes,5,opt"`
BusiData []byte `protobuf:"bytes,6,opt"`
ImageMD5 proto.Option[string] `protobuf:"bytes,7,opt"`
LayerPicUrl proto.Option[string] `protobuf:"bytes,8,opt"`
PatternId proto.Option[string] `protobuf:"bytes,9,opt"`
DisplayIndex proto.Option[uint32] `protobuf:"varint,10,opt"`
}
type StImageUrl struct {
LevelType proto.Option[uint32] `protobuf:"varint,1,opt"`
Url proto.Option[string] `protobuf:"bytes,2,opt"`
Width proto.Option[uint32] `protobuf:"varint,3,opt"`
Height proto.Option[uint32] `protobuf:"varint,4,opt"`
BusiData []byte `protobuf:"bytes,5,opt"`
}
type StLightInteractInfo struct {
User *StUser `protobuf:"bytes,1,opt"`
Relation *StRelationInfo `protobuf:"bytes,2,opt"`
Count proto.Option[uint32] `protobuf:"varint,3,opt"`
BusiData []byte `protobuf:"bytes,4,opt"`
}
type StLike struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Count proto.Option[uint32] `protobuf:"varint,2,opt"`
Status proto.Option[uint32] `protobuf:"varint,3,opt"`
VecUser []*StUser `protobuf:"bytes,4,rep"`
BusiData []byte `protobuf:"bytes,5,opt"`
PostUser *StUser `protobuf:"bytes,6,opt"`
HasLikedCount proto.Option[uint32] `protobuf:"varint,7,opt"`
OwnerStatus proto.Option[uint32] `protobuf:"varint,8,opt"`
JumpUrl proto.Option[string] `protobuf:"bytes,9,opt"`
}
type StLiteBanner struct {
Icon *StImage `protobuf:"bytes,1,opt"`
Title proto.Option[string] `protobuf:"bytes,2,opt"`
JumpUrl proto.Option[string] `protobuf:"bytes,3,opt"`
ActivityID proto.Option[string] `protobuf:"bytes,4,opt"`
JsonStyle proto.Option[string] `protobuf:"bytes,5,opt"`
ExtInfo []*CommonEntry `protobuf:"bytes,6,rep"`
}
type StMaterialDataNew struct {
MaterialType proto.Option[string] `protobuf:"bytes,1,opt"`
MaterialList []*StSingleMaterial `protobuf:"bytes,2,rep"`
}
type StMedalInfo struct {
Type proto.Option[int32] `protobuf:"varint,1,opt"`
MedalName proto.Option[string] `protobuf:"bytes,2,opt"`
MedalID proto.Option[string] `protobuf:"bytes,3,opt"`
Rank proto.Option[int32] `protobuf:"varint,4,opt"`
IsHighLight proto.Option[bool] `protobuf:"varint,5,opt"`
IsNew proto.Option[bool] `protobuf:"varint,6,opt"`
JumpUrl proto.Option[string] `protobuf:"bytes,7,opt"`
IconUrl proto.Option[string] `protobuf:"bytes,8,opt"`
BackgroundUrl proto.Option[string] `protobuf:"bytes,9,opt"`
Describe proto.Option[string] `protobuf:"bytes,10,opt"`
ReportValue proto.Option[int32] `protobuf:"varint,11,opt"`
_ [0]func()
}
type StNotice struct {
PsvFeed *StFeed `protobuf:"bytes,1,opt"`
OrigineFeed *StFeed `protobuf:"bytes,2,opt"`
PattonInfo *StNoticePattonInfo `protobuf:"bytes,3,opt"`
_ [0]func()
}
type StNoticePattonInfo struct {
PattonType proto.Option[uint32] `protobuf:"varint,1,opt"`
PlainTxt *StPlainTxtInfo `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StNoticeTxtInfo struct {
Content *StRichText `protobuf:"bytes,1,opt"`
ContentOfReference *StRichText `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StOpinfo struct {
CreateTime []uint64 `protobuf:"varint,1,rep"`
}
type StPlainTxtInfo struct {
TxtInfo *StNoticeTxtInfo `protobuf:"bytes,1,opt"`
Operation *NoticeOperation `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StPoiInfoV2 struct {
PoiId proto.Option[string] `protobuf:"bytes,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
PoiType proto.Option[int32] `protobuf:"varint,3,opt"`
TypeName proto.Option[string] `protobuf:"bytes,4,opt"`
Address proto.Option[string] `protobuf:"bytes,5,opt"`
DistrictCode proto.Option[int32] `protobuf:"varint,6,opt"`
Gps *StGPSV2 `protobuf:"bytes,7,opt"`
Distance proto.Option[int32] `protobuf:"varint,8,opt"`
HotValue proto.Option[int32] `protobuf:"varint,9,opt"`
Phone proto.Option[string] `protobuf:"bytes,10,opt"`
Country proto.Option[string] `protobuf:"bytes,11,opt"`
Province proto.Option[string] `protobuf:"bytes,12,opt"`
City proto.Option[string] `protobuf:"bytes,13,opt"`
PoiNum proto.Option[int32] `protobuf:"varint,14,opt"`
PoiOrderType proto.Option[int32] `protobuf:"varint,15,opt"`
DefaultName proto.Option[string] `protobuf:"bytes,16,opt"`
District proto.Option[string] `protobuf:"bytes,17,opt"`
DianPingId proto.Option[string] `protobuf:"bytes,18,opt"`
DistanceText proto.Option[string] `protobuf:"bytes,19,opt"`
DisplayName proto.Option[string] `protobuf:"bytes,20,opt"`
_ [0]func()
}
type StPrePullCacheFeed struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Poster *StUser `protobuf:"bytes,2,opt"`
CreateTime proto.Option[uint64] `protobuf:"varint,3,opt"`
BusiTranparent []*BytesEntry `protobuf:"bytes,4,rep"`
}
type StProxyInfo struct {
CmdId proto.Option[int32] `protobuf:"varint,1,opt"`
SubCmdId proto.Option[int32] `protobuf:"varint,2,opt"`
AppProtocol proto.Option[string] `protobuf:"bytes,3,opt"`
ReqBody []byte `protobuf:"bytes,4,opt"`
}
type StRankingItem struct {
User *StUser `protobuf:"bytes,1,opt"`
Relation *StRelationInfo `protobuf:"bytes,2,opt"`
Score proto.Option[int64] `protobuf:"varint,3,opt"`
Grade proto.Option[int32] `protobuf:"varint,4,opt"`
BusiData []byte `protobuf:"bytes,5,opt"`
RankNo proto.Option[int32] `protobuf:"varint,6,opt"`
InTopicList proto.Option[int32] `protobuf:"varint,7,opt"`
}
type StRecomForward struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Title proto.Option[string] `protobuf:"bytes,2,opt"`
Subtitle proto.Option[string] `protobuf:"bytes,3,opt"`
Poster *StUser `protobuf:"bytes,4,opt"`
CreateTime proto.Option[uint64] `protobuf:"varint,5,opt"`
Type proto.Option[uint32] `protobuf:"varint,6,opt"`
BusiData []byte `protobuf:"bytes,7,opt"`
}
type StRecomInfo struct {
RecomReason proto.Option[string] `protobuf:"bytes,1,opt"`
RecomAttachInfo []byte `protobuf:"bytes,2,opt"`
RecomTrace proto.Option[string] `protobuf:"bytes,3,opt"`
ClientSealData []byte `protobuf:"bytes,4,opt"`
IconUrl proto.Option[string] `protobuf:"bytes,5,opt"`
RecomReasonType proto.Option[int32] `protobuf:"varint,6,opt"`
}
type StRecomReportInfo struct {
RecomInfos []*StSingleRecomReportInfo `protobuf:"bytes,1,rep"`
}
type StRelationInfo struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Relation proto.Option[uint32] `protobuf:"varint,2,opt"`
BusiData []byte `protobuf:"bytes,3,opt"`
RelationState proto.Option[uint32] `protobuf:"varint,4,opt"`
Score proto.Option[uint32] `protobuf:"varint,5,opt"`
IsBlock proto.Option[bool] `protobuf:"varint,6,opt"`
IsBlocked proto.Option[bool] `protobuf:"varint,7,opt"`
IsFriend proto.Option[bool] `protobuf:"varint,8,opt"`
IsUncare proto.Option[bool] `protobuf:"varint,9,opt"`
ImBitMap proto.Option[uint64] `protobuf:"varint,10,opt"`
}
type StReply struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
PostUser *StUser `protobuf:"bytes,2,opt"`
CreateTime proto.Option[uint64] `protobuf:"varint,3,opt"`
Content proto.Option[string] `protobuf:"bytes,4,opt"`
TargetUser *StUser `protobuf:"bytes,5,opt"`
BusiData []byte `protobuf:"bytes,6,opt"`
LikeInfo *StLike `protobuf:"bytes,7,opt"`
TypeFlag proto.Option[uint32] `protobuf:"varint,8,opt"`
Modifyflag proto.Option[uint32] `protobuf:"varint,9,opt"`
AtUinList []string `protobuf:"bytes,10,rep"`
TypeFlag2 proto.Option[uint32] `protobuf:"varint,11,opt"`
CreateTimeNs proto.Option[uint64] `protobuf:"varint,12,opt"`
StoreExtInfo []*CommonEntry `protobuf:"bytes,13,rep"`
ThirdId proto.Option[string] `protobuf:"bytes,14,opt"`
TargetReplyID proto.Option[string] `protobuf:"bytes,15,opt"`
SourceType proto.Option[uint32] `protobuf:"varint,16,opt"`
RichContents *StRichText `protobuf:"bytes,17,opt"`
}
type StReportInfo struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
BusiReport []byte `protobuf:"bytes,2,opt"`
}
type StRichText struct {
Contents []*StRichTextContent `protobuf:"bytes,1,rep"`
}
type StRichTextAtContent struct {
Type proto.Option[uint32] `protobuf:"varint,1,opt"`
GuildInfo *GuildChannelBaseGuildInfo `protobuf:"bytes,2,opt"`
RoleGroupId *GuildChannelBaseRoleGroupInfo `protobuf:"bytes,3,opt"`
User *StUser `protobuf:"bytes,4,opt"`
_ [0]func()
}
type GuildChannelBaseGuildInfo struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
JoinTime proto.Option[uint64] `protobuf:"varint,3,opt"`
_ [0]func()
}
type GuildChannelBaseRoleGroupInfo struct {
RoleId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
Color proto.Option[uint32] `protobuf:"varint,3,opt"`
_ [0]func()
}
type StRichTextChannelContent struct {
ChannelInfo *StChannelInfo `protobuf:"bytes,1,opt"`
_ [0]func()
}
type StRichTextContent struct {
Type proto.Option[uint32] `protobuf:"varint,1,opt"`
PatternId proto.Option[string] `protobuf:"bytes,2,opt"`
TextContent *StRichTextTextContent `protobuf:"bytes,3,opt"`
AtContent *StRichTextAtContent `protobuf:"bytes,4,opt"`
UrlContent *StRichTextURLContent `protobuf:"bytes,5,opt"`
EmojiContent *StRichTextEmojiContent `protobuf:"bytes,6,opt"`
ChannelContent *StRichTextChannelContent `protobuf:"bytes,7,opt"`
_ [0]func()
}
type StRichTextEmojiContent struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Type proto.Option[string] `protobuf:"bytes,2,opt"`
Name proto.Option[string] `protobuf:"bytes,3,opt"`
Url proto.Option[string] `protobuf:"bytes,4,opt"`
_ [0]func()
}
type StRichTextTextContent struct {
Text proto.Option[string] `protobuf:"bytes,1,opt"`
_ [0]func()
}
type StRichTextURLContent struct {
Url proto.Option[string] `protobuf:"bytes,1,opt"`
DisplayText proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StSameTopicGuideInfo struct {
IsSameTopicGuide proto.Option[uint32] `protobuf:"varint,1,opt"`
StayShowTime proto.Option[int64] `protobuf:"varint,2,opt"`
HashTag proto.Option[string] `protobuf:"bytes,3,opt"`
Words proto.Option[string] `protobuf:"bytes,4,opt"`
JumpUrl proto.Option[string] `protobuf:"bytes,5,opt"`
ReportExt proto.Option[string] `protobuf:"bytes,6,opt"`
_ [0]func()
}
type StShare struct {
Title proto.Option[string] `protobuf:"bytes,1,opt"`
Desc proto.Option[string] `protobuf:"bytes,2,opt"`
Type proto.Option[uint32] `protobuf:"varint,3,opt"`
Url proto.Option[string] `protobuf:"bytes,4,opt"`
Author *StUser `protobuf:"bytes,5,opt"`
Poster *StUser `protobuf:"bytes,6,opt"`
Videos []*StVideo `protobuf:"bytes,7,rep"`
Shorturl proto.Option[string] `protobuf:"bytes,8,opt"`
ShareCardInfo proto.Option[string] `protobuf:"bytes,9,opt"`
ShareQzoneInfo *StShareQzoneInfo `protobuf:"bytes,10,opt"`
Images []*StImage `protobuf:"bytes,11,rep"`
PublishTotalUser proto.Option[uint32] `protobuf:"varint,12,opt"`
SharedCount proto.Option[uint32] `protobuf:"varint,13,opt"`
ChannelShareInfo *StChannelShareInfo `protobuf:"bytes,14,opt"`
}
type StShareQzoneInfo struct {
Entrys []*CommonEntry `protobuf:"bytes,1,rep"`
}
type StSingleMaterial struct {
MaterialId proto.Option[string] `protobuf:"bytes,1,opt"`
_ [0]func()
}
type StSingleRecomReportInfo struct {
ReportID proto.Option[string] `protobuf:"bytes,1,opt"`
ReportData []byte `protobuf:"bytes,2,opt"`
}
type StTagInfo struct {
TagId proto.Option[string] `protobuf:"bytes,1,opt"`
TagName proto.Option[string] `protobuf:"bytes,2,opt"`
TagDec proto.Option[string] `protobuf:"bytes,3,opt"`
UserList []*StUser `protobuf:"bytes,4,rep"`
FeedList []*StFeedAbstract `protobuf:"bytes,5,rep"`
TagTotalUser proto.Option[uint32] `protobuf:"varint,6,opt"`
TagTotalFeed proto.Option[uint32] `protobuf:"varint,7,opt"`
TagWording proto.Option[string] `protobuf:"bytes,8,opt"`
TagType proto.Option[uint32] `protobuf:"varint,9,opt"`
FollowState proto.Option[uint32] `protobuf:"varint,10,opt"`
ShareInfo *StShare `protobuf:"bytes,11,opt"`
IsTop proto.Option[uint32] `protobuf:"varint,12,opt"`
IsSelected proto.Option[uint32] `protobuf:"varint,13,opt"`
UserViewHistory proto.Option[int64] `protobuf:"varint,14,opt"`
Medal *StTagMedalInfo `protobuf:"bytes,15,opt"`
Status proto.Option[uint32] `protobuf:"varint,16,opt"`
OptInfo *StTagOperateInfo `protobuf:"bytes,17,opt"`
TagBaseStatus proto.Option[uint32] `protobuf:"varint,18,opt"`
IsRecommend proto.Option[int32] `protobuf:"varint,19,opt"`
TagViewHistory proto.Option[int64] `protobuf:"varint,20,opt"`
OperateIconUrl proto.Option[string] `protobuf:"bytes,21,opt"`
TagReport proto.Option[string] `protobuf:"bytes,99,opt"`
TagIconUrl proto.Option[string] `protobuf:"bytes,100,opt"`
}
type StTagMedalInfo struct {
TagID proto.Option[string] `protobuf:"bytes,1,opt"`
TagName proto.Option[string] `protobuf:"bytes,2,opt"`
Rank proto.Option[uint64] `protobuf:"varint,3,opt"`
_ [0]func()
}
type StTagOperateInfo struct {
CreateUser proto.Option[string] `protobuf:"bytes,1,opt"`
CoverURL proto.Option[string] `protobuf:"bytes,2,opt"`
Desc proto.Option[string] `protobuf:"bytes,3,opt"`
BackgroundURL proto.Option[string] `protobuf:"bytes,4,opt"`
BannerURL proto.Option[string] `protobuf:"bytes,5,opt"`
BannerSkipLink proto.Option[string] `protobuf:"bytes,6,opt"`
ActivityStartTime proto.Option[int64] `protobuf:"varint,7,opt"`
ActivityEndTime proto.Option[int64] `protobuf:"varint,8,opt"`
RecommendReason proto.Option[string] `protobuf:"bytes,9,opt"`
IsWhite proto.Option[int32] `protobuf:"varint,10,opt"`
BeWhiteStartTime proto.Option[int64] `protobuf:"varint,11,opt"`
BeWhiteEndTime proto.Option[int64] `protobuf:"varint,12,opt"`
PublishSchema proto.Option[string] `protobuf:"bytes,13,opt"`
_ [0]func()
}
type StUnifiedTag struct {
UnifiedType proto.Option[string] `protobuf:"bytes,1,opt"`
UnifiedId proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StUser struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Nick proto.Option[string] `protobuf:"bytes,2,opt"`
Icon *StIconInfo `protobuf:"bytes,3,opt"`
Desc proto.Option[string] `protobuf:"bytes,4,opt"`
FollowState proto.Option[uint32] `protobuf:"varint,5,opt"`
Type proto.Option[uint32] `protobuf:"varint,6,opt"`
Sex proto.Option[uint32] `protobuf:"varint,7,opt"`
Birthday proto.Option[uint64] `protobuf:"varint,8,opt"`
School proto.Option[string] `protobuf:"bytes,9,opt"`
Location proto.Option[string] `protobuf:"bytes,11,opt"`
BusiData []byte `protobuf:"bytes,12,opt"`
FrdState proto.Option[uint32] `protobuf:"varint,13,opt"`
RelationState proto.Option[uint32] `protobuf:"varint,14,opt"`
BlackState proto.Option[uint32] `protobuf:"varint,15,opt"`
Medal *StTagMedalInfo `protobuf:"bytes,16,opt"`
Constellation proto.Option[int32] `protobuf:"varint,17,opt"`
JumpUrl proto.Option[string] `protobuf:"bytes,18,opt"`
LocationCode proto.Option[string] `protobuf:"bytes,19,opt"`
ThirdId proto.Option[string] `protobuf:"bytes,20,opt"`
Company proto.Option[string] `protobuf:"bytes,21,opt"`
CertificationDesc proto.Option[string] `protobuf:"bytes,22,opt"`
DescType proto.Option[uint32] `protobuf:"varint,23,opt"`
ChannelUserInfo *GuildChannelBaseChannelUserInfo `protobuf:"bytes,24,opt"`
LoginId proto.Option[string] `protobuf:"bytes,25,opt"`
}
type GuildChannelBaseChannelUserInfo struct {
ClientIdentity *ClientIdentity `protobuf:"bytes,1,opt"`
MemberType proto.Option[uint32] `protobuf:"varint,2,opt"`
// optional ChannelUserPermission permission = 3;
RoleGroups []*GuildChannelBaseRoleGroupInfo `protobuf:"bytes,4,rep"`
}
type StUserGroupInfo struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
UserList []*StUser `protobuf:"bytes,3,rep"`
}
type StUserRecomInfo struct {
User *StUser `protobuf:"bytes,1,opt"`
FeedList []*StFeedAbstract `protobuf:"bytes,2,rep"`
BusiData []byte `protobuf:"bytes,3,opt"`
}
type StVideo struct {
FileId proto.Option[string] `protobuf:"bytes,1,opt"`
FileSize proto.Option[uint32] `protobuf:"varint,2,opt"`
Duration proto.Option[uint32] `protobuf:"varint,3,opt"`
Width proto.Option[uint32] `protobuf:"varint,4,opt"`
Height proto.Option[uint32] `protobuf:"varint,5,opt"`
PlayUrl proto.Option[string] `protobuf:"bytes,6,opt"`
TransStatus proto.Option[uint32] `protobuf:"varint,7,opt"`
VideoPrior proto.Option[uint32] `protobuf:"varint,8,opt"`
VideoRate proto.Option[uint32] `protobuf:"varint,9,opt"`
VecVideoUrl []*StVideoUrl `protobuf:"bytes,10,rep"`
BusiData []byte `protobuf:"bytes,11,opt"`
ApprovalStatus proto.Option[uint32] `protobuf:"varint,12,opt"`
VideoSource proto.Option[uint32] `protobuf:"varint,13,opt"`
MediaQualityRank proto.Option[uint32] `protobuf:"varint,14,opt"`
MediaQualityScore proto.Option[float32] `protobuf:"fixed32,15,opt"`
VideoMD5 proto.Option[string] `protobuf:"bytes,16,opt"`
IsQuic proto.Option[uint32] `protobuf:"varint,17,opt"`
Orientation proto.Option[uint32] `protobuf:"varint,18,opt"`
Cover *StImage `protobuf:"bytes,19,opt"`
PatternId proto.Option[string] `protobuf:"bytes,20,opt"`
DisplayIndex proto.Option[uint32] `protobuf:"varint,21,opt"`
}
type StVideoUrl struct {
LevelType proto.Option[uint32] `protobuf:"varint,1,opt"`
PlayUrl proto.Option[string] `protobuf:"bytes,2,opt"`
VideoPrior proto.Option[uint32] `protobuf:"varint,3,opt"`
VideoRate proto.Option[uint32] `protobuf:"varint,4,opt"`
TransStatus proto.Option[uint32] `protobuf:"varint,5,opt"`
BusiData []byte `protobuf:"bytes,6,opt"`
HasWatermark proto.Option[bool] `protobuf:"varint,7,opt"`
}
type StVisitor struct {
ViewCount proto.Option[uint32] `protobuf:"varint,1,opt"`
BusiData []byte `protobuf:"bytes,2,opt"`
RecomCount proto.Option[uint32] `protobuf:"varint,3,opt"`
ViewDesc proto.Option[string] `protobuf:"bytes,4,opt"`
}
type StWearingMedal struct {
MedalInfos []*StWearingMedalInfo `protobuf:"bytes,1,rep"`
}
type StWearingMedalInfo struct {
Type proto.Option[int32] `protobuf:"varint,1,opt"`
MedalName proto.Option[string] `protobuf:"bytes,2,opt"`
MedalID proto.Option[string] `protobuf:"bytes,3,opt"`
_ [0]func()
}

View File

@ -0,0 +1,651 @@
syntax = "proto2";
package channel;
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/GuildChannelBase.proto";
message ContentMetaData {
optional RichTextContentCount count = 1;
optional int64 ContentID = 2;
}
message FeedMetaData {
optional ContentMetaData content = 1;
optional uint64 lastModifiedTime = 2;
}
message FeedRedTouchTransInfo {
optional string feedId = 1;
optional string author = 2;
optional int64 createTs = 3;
optional int32 msgType = 4;
optional int32 pageType = 5;
optional int32 redType = 6;
optional int32 insertPageType = 7;
}
message NoticeOperation {
optional uint32 type = 1;
optional string schema = 2;
}
message RichTextContentCount {
optional uint64 textWord = 1;
optional uint64 at = 2;
optional uint64 url = 3;
optional uint64 emoji = 4;
optional uint64 image = 5;
optional uint64 video = 6;
}
message StAnimation {
optional uint32 width = 1;
optional uint32 height = 2;
optional string animationUrl = 3;
optional bytes busiData = 4;
}
message StBusiReportInfo {
optional StRecomReportInfo recomReport = 1;
optional string traceID = 2;
}
message StChannelShareInfo {
optional string feedID = 1;
optional string posterID = 2;
optional uint64 feedPublishAt = 3;
optional StChannelSign channelSign = 4;
optional uint64 updateDurationMs = 5;
optional StChannelShareSign sign = 6;
}
message StChannelShareSign {
optional uint64 createAt = 1;
optional string token = 2;
}
message StCircleRankItem {
optional int32 rankNo = 1;
optional string circleName = 2;
optional int64 fuelValue = 3;
optional int64 feedNum = 4;
optional string circleID = 5;
}
message StClientInfo {
optional string feedclientkey = 1;
repeated CommonEntry clientMap = 2;
}
message StComment {
optional string id = 1;
optional StUser postUser = 2;
optional uint64 createTime = 3;
optional string content = 4;
optional uint32 replyCount = 5;
repeated StReply vecReply = 6;
optional bytes busiData = 7;
optional StLike likeInfo = 8;
optional uint32 typeFlag = 9;
repeated string atUinList = 10;
optional uint32 typeFlag2 = 11;
optional uint64 createTimeNs = 12;
repeated CommonEntry storeExtInfo = 13;
optional string thirdId = 14;
optional uint32 sourceType = 15;
optional StRichText richContents = 16;
}
message StDebugInfo {
repeated CommonEntry debugMap = 1;
}
message StDittoFeed {
optional uint32 dittoId = 1;
optional uint32 dittoPatternId = 2;
optional bytes dittoData = 3;
optional bytes dittoDataNew = 4;
}
message StExifInfo {
repeated CommonEntry kvs = 1;
}
message StExternalMedalWallInfo {
optional bool needRedPoint = 1;
repeated StMedalInfo medalInfos = 2;
optional string medalWallJumpUrl = 3;
optional bool needShowEntrance = 4;
}
message StFeed {
optional string id = 1;
optional StRichText title = 2;
optional StRichText subtitle = 3;
optional StUser poster = 4;
repeated StVideo videos = 5;
optional StRichText contents = 6;
optional uint64 createTime = 7;
optional StEmotionReactionInfo emotionReaction = 8;
optional uint32 commentCount = 9;
repeated StComment vecComment = 10;
optional StShare share = 11;
optional StVisitor visitorInfo = 12;
repeated StImage images = 13;
optional StPoiInfoV2 poiInfo = 14;
repeated StTagInfo tagInfos = 15;
optional bytes busiReport = 16;
repeated uint32 opMask = 17;
optional StOpinfo opinfo = 18;
repeated CommonEntry extInfo = 19;
optional string patternInfo = 20;
optional StChannelInfo channelInfo = 21;
optional uint64 createTimeNs = 22;
optional StFeedSummary summary = 23;
optional StRecomInfo recomInfo = 24;
optional FeedMetaData meta = 25;
}
message StFeedAbstract {
optional string id = 1;
optional string title = 2;
optional StUser poster = 3;
optional StImage pic = 4;
optional uint32 type = 5;
optional uint64 createTime = 6;
optional StVideo video = 7;
optional uint32 fuelNum = 8;
optional string content = 9;
repeated StImage images = 10;
optional StFeedCount countInfo = 11;
}
message StFeedCount {
optional int64 liked = 1;
optional int64 push = 2;
optional int64 comment = 3;
optional int64 visitor = 4;
}
message StFeedSummary {
optional uint32 layoutType = 1;
}
message StFollowRecomInfo {
optional string followText = 1;
repeated StFollowUser followUsers = 4;
optional string commFriendText = 6;
optional string commGroupText = 7;
}
message StFollowUser {
optional uint64 uid = 1;
optional string nick = 2;
}
message StGPSV2 {
optional int64 lat = 1;
optional int64 lon = 2;
optional int64 eType = 3;
optional int64 alt = 4;
}
message StGuidePublishBubble {
optional string id = 1;
optional StImage backgroundImage = 2;
optional string jumpUrl = 3;
}
message StIconInfo {
optional string iconUrl40 = 1;
optional string iconUrl100 = 2;
optional string iconUrl140 = 3;
optional string iconUrl640 = 4;
optional string iconUrl = 5;
}
message StImage {
optional uint32 width = 1;
optional uint32 height = 2;
optional string picUrl = 3;
repeated StImageUrl vecImageUrl = 4;
optional string picId = 5;
optional bytes busiData = 6;
optional string imageMD5 = 7;
optional string layerPicUrl = 8;
optional string patternId = 9;
optional uint32 displayIndex = 10;
}
message StImageUrl {
optional uint32 levelType = 1;
optional string url = 2;
optional uint32 width = 3;
optional uint32 height = 4;
optional bytes busiData = 5;
}
message StLightInteractInfo {
optional StUser user = 1;
optional StRelationInfo relation = 2;
optional uint32 count = 3;
optional bytes busiData = 4;
}
message StLike {
optional string id = 1;
optional uint32 count = 2;
optional uint32 status = 3;
repeated StUser vecUser = 4;
optional bytes busiData = 5;
optional StUser postUser = 6;
optional uint32 hasLikedCount = 7;
optional uint32 ownerStatus = 8;
optional string jumpUrl = 9;
}
message StLiteBanner {
optional StImage icon = 1;
optional string title = 2;
optional string jumpUrl = 3;
optional string activityID = 4;
optional string jsonStyle = 5;
repeated CommonEntry extInfo = 6;
}
message StMaterialDataNew {
optional string materialType = 1;
repeated StSingleMaterial materialList = 2;
}
message StMedalInfo {
optional int32 type = 1;
optional string medalName = 2;
optional string medalID = 3;
optional int32 rank = 4;
optional bool isHighLight = 5;
optional bool isNew = 6;
optional string jumpUrl = 7;
optional string iconUrl = 8;
optional string backgroundUrl = 9;
optional string describe = 10;
optional int32 reportValue = 11;
}
message StNotice {
optional StFeed psvFeed = 1;
optional StFeed origineFeed = 2;
optional StNoticePattonInfo pattonInfo = 3;
}
message StNoticePattonInfo {
optional uint32 pattonType = 1;
optional StPlainTxtInfo plainTxt = 2;
}
message StNoticeTxtInfo {
optional StRichText content = 1;
optional StRichText contentOfReference = 2;
}
message StOpinfo {
repeated uint64 createTime = 1;
}
message StPlainTxtInfo {
optional StNoticeTxtInfo txtInfo = 1;
optional NoticeOperation operation = 2;
}
message StPoiInfoV2 {
optional string poiId = 1;
optional string name = 2;
optional int32 poiType = 3;
optional string typeName = 4;
optional string address = 5;
optional int32 districtCode = 6;
optional StGPSV2 gps = 7;
optional int32 distance = 8;
optional int32 hotValue = 9;
optional string phone = 10;
optional string country = 11;
optional string province = 12;
optional string city = 13;
optional int32 poiNum = 14;
optional int32 poiOrderType = 15;
optional string defaultName = 16;
optional string district = 17;
optional string dianPingId = 18;
optional string distanceText = 19;
optional string displayName = 20;
}
message StPrePullCacheFeed {
optional string id = 1;
optional StUser poster = 2;
optional uint64 createTime = 3;
repeated BytesEntry busiTranparent = 4;
}
message StProxyInfo {
optional int32 cmdId = 1;
optional int32 subCmdId = 2;
optional string appProtocol = 3;
optional bytes reqBody = 4;
}
message StRankingItem {
optional StUser user = 1;
optional StRelationInfo relation = 2;
optional int64 score = 3;
optional int32 grade = 4;
optional bytes busiData = 5;
optional int32 rankNo = 6;
optional int32 inTopicList = 7;
}
message StRecomForward {
optional string id = 1;
optional string title = 2;
optional string subtitle = 3;
optional StUser poster = 4;
optional uint64 createTime = 5;
optional uint32 type = 6;
optional bytes busiData = 7;
}
message StRecomInfo {
optional string recomReason = 1;
optional bytes recomAttachInfo = 2;
optional string recomTrace = 3;
optional bytes clientSealData = 4;
optional string iconUrl = 5;
optional int32 recomReasonType = 6;
}
message StRecomReportInfo {
repeated StSingleRecomReportInfo recomInfos = 1;
}
message StRelationInfo {
optional string id = 1;
optional uint32 relation = 2;
optional bytes busiData = 3;
optional uint32 relationState = 4;
optional uint32 score = 5;
optional bool isBlock = 6;
optional bool isBlocked = 7;
optional bool isFriend = 8;
optional bool isUncare = 9;
optional uint64 imBitMap = 10;
}
message StReply {
optional string id = 1;
optional StUser postUser = 2;
optional uint64 createTime = 3;
optional string content = 4;
optional StUser targetUser = 5;
optional bytes busiData = 6;
optional StLike likeInfo = 7;
optional uint32 typeFlag = 8;
optional uint32 modifyflag = 9;
repeated string atUinList = 10;
optional uint32 typeFlag2 = 11;
optional uint64 createTimeNs = 12;
repeated CommonEntry storeExtInfo = 13;
optional string thirdId = 14;
optional string targetReplyID = 15;
optional uint32 sourceType = 16;
optional StRichText richContents = 17;
}
message StReportInfo {
optional string id = 1;
optional bytes busiReport = 2;
}
message StRichText {
repeated StRichTextContent contents = 1;
}
message StRichTextAtContent {
optional uint32 type = 1;
optional GuildChannelBaseGuildInfo guildInfo = 2;
optional GuildChannelBaseRoleGroupInfo roleGroupId = 3;
optional StUser user = 4;
}
message GuildChannelBaseGuildInfo {
optional uint64 guildId = 1;
optional string name = 2;
optional uint64 joinTime = 3;
}
message GuildChannelBaseRoleGroupInfo {
optional uint64 roleId = 1;
optional string name = 2;
optional uint32 color = 3;
}
message StRichTextChannelContent {
optional StChannelInfo channelInfo = 1;
}
message StRichTextContent {
optional uint32 type = 1;
optional string patternId = 2;
optional StRichTextTextContent textContent = 3;
optional StRichTextAtContent atContent = 4;
optional StRichTextURLContent urlContent = 5;
optional StRichTextEmojiContent emojiContent = 6;
optional StRichTextChannelContent channelContent = 7;
}
message StRichTextEmojiContent {
optional string id = 1;
optional string type = 2;
optional string name = 3;
optional string url = 4;
}
message StRichTextTextContent {
optional string text = 1;
}
message StRichTextURLContent {
optional string url = 1;
optional string displayText = 2;
}
message StSameTopicGuideInfo {
optional uint32 isSameTopicGuide = 1;
optional int64 stayShowTime = 2;
optional string hashTag = 3;
optional string words = 4;
optional string jumpUrl = 5;
optional string reportExt = 6;
}
message StShare {
optional string title = 1;
optional string desc = 2;
optional uint32 type = 3;
optional string url = 4;
optional StUser author = 5;
optional StUser poster = 6;
repeated StVideo videos = 7;
optional string shorturl = 8;
optional string shareCardInfo = 9;
optional StShareQzoneInfo shareQzoneInfo = 10;
repeated StImage images = 11;
optional uint32 publishTotalUser = 12;
optional uint32 sharedCount = 13;
optional StChannelShareInfo channelShareInfo = 14;
}
message StShareQzoneInfo {
repeated CommonEntry entrys = 1;
}
message StSingleMaterial {
optional string materialId = 1;
}
message StSingleRecomReportInfo {
optional string reportID = 1;
optional bytes reportData = 2;
}
message StTagInfo {
optional string tagId = 1;
optional string tagName = 2;
optional string tagDec = 3;
repeated StUser userList = 4;
repeated StFeedAbstract feedList = 5;
optional uint32 tagTotalUser = 6;
optional uint32 tagTotalFeed = 7;
optional string tagWording = 8;
optional uint32 tagType = 9;
optional uint32 followState = 10;
optional StShare shareInfo = 11;
optional uint32 isTop = 12;
optional uint32 isSelected = 13;
optional int64 userViewHistory = 14;
optional StTagMedalInfo medal = 15;
optional uint32 status = 16;
optional StTagOperateInfo optInfo = 17;
optional uint32 tagBaseStatus = 18;
optional int32 isRecommend = 19;
optional int64 tagViewHistory = 20;
optional string operateIconUrl = 21;
optional string tagReport = 99;
optional string tagIconUrl = 100;
}
message StTagMedalInfo {
optional string tagID = 1;
optional string tagName = 2;
optional uint64 rank = 3;
}
message StTagOperateInfo {
optional string createUser = 1;
optional string coverURL = 2;
optional string desc = 3;
optional string backgroundURL = 4;
optional string bannerURL = 5;
optional string bannerSkipLink = 6;
optional int64 activityStartTime = 7;
optional int64 activityEndTime = 8;
optional string recommendReason = 9;
optional int32 isWhite = 10;
optional int64 beWhiteStartTime = 11;
optional int64 beWhiteEndTime = 12;
optional string publishSchema = 13;
}
message StUnifiedTag {
optional string unifiedType = 1;
optional string unifiedId = 2;
}
message StUser {
optional string id = 1;
optional string nick = 2;
optional StIconInfo icon = 3;
optional string desc = 4;
optional uint32 followState = 5;
optional uint32 type = 6;
optional uint32 sex = 7;
optional uint64 birthday = 8;
optional string school = 9;
optional string location = 11;
optional bytes busiData = 12;
optional uint32 frdState = 13;
optional uint32 relationState = 14;
optional uint32 blackState = 15;
optional StTagMedalInfo medal = 16;
optional int32 constellation = 17;
optional string jumpUrl = 18;
optional string locationCode = 19;
optional string thirdId = 20;
optional string company = 21;
optional string certificationDesc = 22;
optional uint32 descType = 23;
optional GuildChannelBaseChannelUserInfo channelUserInfo = 24;
optional string loginId = 25;
}
message GuildChannelBaseChannelUserInfo {
optional ClientIdentity clientIdentity = 1;
optional uint32 memberType = 2;
// optional ChannelUserPermission permission = 3;
repeated GuildChannelBaseRoleGroupInfo roleGroups = 4;
}
message StUserGroupInfo {
optional string id = 1;
optional string name = 2;
repeated StUser userList = 3;
}
message StUserRecomInfo {
optional StUser user = 1;
repeated StFeedAbstract feedList = 2;
optional bytes busiData = 3;
}
message StVideo {
optional string fileId = 1;
optional uint32 fileSize = 2;
optional uint32 duration = 3;
optional uint32 width = 4;
optional uint32 height = 5;
optional string playUrl = 6;
optional uint32 transStatus = 7;
optional uint32 videoPrior = 8;
optional uint32 videoRate = 9;
repeated StVideoUrl vecVideoUrl = 10;
optional bytes busiData = 11;
optional uint32 approvalStatus = 12;
optional uint32 videoSource = 13;
optional uint32 mediaQualityRank = 14;
optional float mediaQualityScore = 15;
optional string videoMD5 = 16;
optional uint32 isQuic = 17;
optional uint32 orientation = 18;
optional StImage cover = 19;
optional string patternId = 20;
optional uint32 displayIndex = 21;
}
message StVideoUrl {
optional uint32 levelType = 1;
optional string playUrl = 2;
optional uint32 videoPrior = 3;
optional uint32 videoRate = 4;
optional uint32 transStatus = 5;
optional bytes busiData = 6;
optional bool hasWatermark = 7;
}
message StVisitor {
optional uint32 viewCount = 1;
optional bytes busiData = 2;
optional uint32 recomCount = 3;
optional string viewDesc = 4;
}
message StWearingMedal {
repeated StWearingMedalInfo medalInfos = 1;
}
message StWearingMedalInfo {
optional int32 type = 1;
optional string medalName = 2;
optional string medalID = 3;
}

View File

@ -0,0 +1,101 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/channel/GuildFeedCloudRead.proto
package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type GetNoticesReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
PageNum proto.Option[uint32] `protobuf:"varint,2,opt"`
AttachInfo proto.Option[string] `protobuf:"bytes,3,opt"`
_ [0]func()
}
type GetNoticesRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Notices []*StNotice `protobuf:"bytes,2,rep"`
TotalNum proto.Option[uint32] `protobuf:"varint,3,opt"`
IsFinish proto.Option[bool] `protobuf:"varint,4,opt"`
AttachInfo proto.Option[string] `protobuf:"bytes,5,opt"`
}
type NeedInsertCommentInfo struct {
CommentID proto.Option[string] `protobuf:"bytes,1,opt"`
_ [0]func()
}
type RefreshToast struct {
Text proto.Option[string] `protobuf:"bytes,1,opt"`
_ [0]func()
}
type StGetChannelFeedsReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Count proto.Option[uint32] `protobuf:"varint,2,opt"`
From proto.Option[uint32] `protobuf:"varint,3,opt"`
ChannelSign *StChannelSign `protobuf:"bytes,4,opt"`
FeedAttchInfo proto.Option[string] `protobuf:"bytes,5,opt"`
_ [0]func()
}
type StGetChannelFeedsRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
VecFeed []*StFeed `protobuf:"bytes,2,rep"`
IsFinish proto.Option[uint32] `protobuf:"varint,3,opt"`
User *StUser `protobuf:"bytes,4,opt"`
FeedAttchInfo proto.Option[string] `protobuf:"bytes,5,opt"`
RefreshToast *RefreshToast `protobuf:"bytes,6,opt"`
}
type StGetChannelShareFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
From proto.Option[uint32] `protobuf:"varint,2,opt"`
ChannelShareInfo *StChannelShareInfo `protobuf:"bytes,3,opt"`
_ [0]func()
}
type StGetChannelShareFeedRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
_ [0]func()
}
type StGetFeedCommentsReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
UserId proto.Option[string] `protobuf:"bytes,2,opt"`
FeedId proto.Option[string] `protobuf:"bytes,3,opt"`
ListNum proto.Option[uint32] `protobuf:"varint,4,opt"`
From proto.Option[uint32] `protobuf:"varint,5,opt"`
AttchInfo proto.Option[string] `protobuf:"bytes,6,opt"`
EntrySchema proto.Option[string] `protobuf:"bytes,7,opt"`
_ [0]func()
}
type StGetFeedCommentsRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
VecComment []*StComment `protobuf:"bytes,2,rep"`
TotalNum proto.Option[uint32] `protobuf:"varint,3,opt"`
IsFinish proto.Option[uint32] `protobuf:"varint,4,opt"`
AttchInfo proto.Option[string] `protobuf:"bytes,5,opt"`
}
type StGetFeedDetailReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
From proto.Option[uint32] `protobuf:"varint,2,opt"`
UserId proto.Option[string] `protobuf:"bytes,3,opt"`
FeedId proto.Option[string] `protobuf:"bytes,4,opt"`
CreateTime proto.Option[uint64] `protobuf:"varint,5,opt"`
DetailType proto.Option[uint32] `protobuf:"varint,6,opt"`
ChannelSign *StChannelSign `protobuf:"bytes,7,opt"`
_ [0]func()
}
type StGetFeedDetailRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
LoginUser *StUser `protobuf:"bytes,3,opt"`
_ [0]func()
}

View File

@ -0,0 +1,93 @@
syntax = "proto2";
package channel;
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/GuildFeedCloudMeta.proto";
import "pb/channel/GuildChannelBase.proto";
message GetNoticesReq {
optional StCommonExt extInfo = 1;
optional uint32 pageNum = 2;
optional string attachInfo = 3;
}
message GetNoticesRsp {
optional StCommonExt extInfo = 1;
repeated StNotice notices = 2;
optional uint32 totalNum = 3;
optional bool isFinish = 4;
optional string attachInfo = 5;
}
message NeedInsertCommentInfo {
optional string commentID = 1;
}
message RefreshToast {
optional string text = 1;
}
message StGetChannelFeedsReq {
optional StCommonExt extInfo = 1;
optional uint32 count = 2;
optional uint32 from = 3;
optional StChannelSign channelSign = 4;
optional string feedAttchInfo = 5;
}
message StGetChannelFeedsRsp {
optional StCommonExt extInfo = 1;
repeated StFeed vecFeed = 2;
optional uint32 isFinish = 3;
optional StUser user = 4;
optional string feedAttchInfo = 5;
optional RefreshToast refreshToast = 6;
}
message StGetChannelShareFeedReq {
optional StCommonExt extInfo = 1;
optional uint32 from = 2;
optional StChannelShareInfo channelShareInfo = 3;
}
message StGetChannelShareFeedRsp {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
}
message StGetFeedCommentsReq {
optional StCommonExt extInfo = 1;
optional string userId = 2;
optional string feedId = 3;
optional uint32 listNum = 4;
optional uint32 from = 5;
optional string attchInfo = 6;
optional string entrySchema = 7;
}
message StGetFeedCommentsRsp {
optional StCommonExt extInfo = 1;
repeated StComment vecComment = 2;
optional uint32 totalNum = 3;
optional uint32 isFinish = 4;
optional string attchInfo = 5;
}
message StGetFeedDetailReq {
optional StCommonExt extInfo = 1;
optional uint32 from = 2;
optional string userId = 3;
optional string feedId = 4;
optional uint64 createTime = 5;
optional uint32 detailType = 6;
optional StChannelSign channelSign = 7;
}
message StGetFeedDetailRsp {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional StUser loginUser = 3;
}

View File

@ -0,0 +1,159 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/channel/GuildWriter.proto
package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type StAlterFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
BusiReqData []byte `protobuf:"bytes,3,opt"`
MBitmap proto.Option[uint64] `protobuf:"varint,4,opt"`
From proto.Option[int32] `protobuf:"varint,5,opt"`
Src proto.Option[int32] `protobuf:"varint,6,opt"`
AlterFeedExtInfo []*CommonEntry `protobuf:"bytes,7,rep"`
JsonFeed proto.Option[string] `protobuf:"bytes,8,opt"`
ClientContent *StClientContent `protobuf:"bytes,9,opt"`
}
type StAlterFeedRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
BusiRspData []byte `protobuf:"bytes,3,opt"`
}
type StClientContent struct {
ClientImageContents []*StClientImageContent `protobuf:"bytes,1,rep"`
ClientVideoContents []*StClientVideoContent `protobuf:"bytes,2,rep"`
}
type StClientImageContent struct {
TaskId proto.Option[string] `protobuf:"bytes,1,opt"`
PicId proto.Option[string] `protobuf:"bytes,2,opt"`
Url proto.Option[string] `protobuf:"bytes,3,opt"`
_ [0]func()
}
type StClientVideoContent struct {
TaskId proto.Option[string] `protobuf:"bytes,1,opt"`
VideoId proto.Option[string] `protobuf:"bytes,2,opt"`
VideoUrl proto.Option[string] `protobuf:"bytes,3,opt"`
CoverUrl proto.Option[string] `protobuf:"bytes,4,opt"`
_ [0]func()
}
type StDelFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
From proto.Option[int32] `protobuf:"varint,3,opt"`
Src proto.Option[int32] `protobuf:"varint,4,opt"`
_ [0]func()
}
type StDelFeedRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
_ [0]func()
}
type StDoCommentReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
CommentType proto.Option[uint32] `protobuf:"varint,2,opt"`
Comment *StComment `protobuf:"bytes,3,opt"`
Feed *StFeed `protobuf:"bytes,4,opt"`
From proto.Option[int32] `protobuf:"varint,5,opt"`
BusiReqData []byte `protobuf:"bytes,6,opt"`
Src proto.Option[int32] `protobuf:"varint,7,opt"`
}
type StDoCommentRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Comment *StComment `protobuf:"bytes,2,opt"`
BusiRspData []byte `protobuf:"bytes,3,opt"`
}
type StDoLikeReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
LikeType proto.Option[uint32] `protobuf:"varint,2,opt"`
Like *StLike `protobuf:"bytes,3,opt"`
Feed *StFeed `protobuf:"bytes,4,opt"`
BusiReqData []byte `protobuf:"bytes,5,opt"`
Comment *StComment `protobuf:"bytes,6,opt"`
Reply *StReply `protobuf:"bytes,7,opt"`
From proto.Option[int32] `protobuf:"varint,8,opt"`
Src proto.Option[int32] `protobuf:"varint,9,opt"`
EmotionReaction *StEmotionReactionInfo `protobuf:"bytes,10,opt"`
}
type StDoLikeRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Like *StLike `protobuf:"bytes,2,opt"`
BusiRspData []byte `protobuf:"bytes,3,opt"`
EmotionReaction *StEmotionReactionInfo `protobuf:"bytes,4,opt"`
}
type StDoReplyReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
ReplyType proto.Option[uint32] `protobuf:"varint,2,opt"`
Reply *StReply `protobuf:"bytes,3,opt"`
Comment *StComment `protobuf:"bytes,4,opt"`
Feed *StFeed `protobuf:"bytes,5,opt"`
From proto.Option[int32] `protobuf:"varint,6,opt"`
BusiReqData []byte `protobuf:"bytes,7,opt"`
Src proto.Option[int32] `protobuf:"varint,8,opt"`
}
type StDoReplyRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Reply *StReply `protobuf:"bytes,2,opt"`
BusiRspData []byte `protobuf:"bytes,3,opt"`
}
type StDoSecurityReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
Comment *StComment `protobuf:"bytes,3,opt"`
Reply *StReply `protobuf:"bytes,4,opt"`
Poster *StUser `protobuf:"bytes,5,opt"`
SecType proto.Option[int32] `protobuf:"varint,6,opt"`
_ [0]func()
}
type StDoSecurityRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
_ [0]func()
}
type StModifyFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
MBitmap proto.Option[uint64] `protobuf:"varint,3,opt"`
From proto.Option[int32] `protobuf:"varint,4,opt"`
Src proto.Option[int32] `protobuf:"varint,5,opt"`
ModifyFeedExtInfo []*CommonEntry `protobuf:"bytes,6,rep"`
}
type StModifyFeedRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
BusiRspData []byte `protobuf:"bytes,3,opt"`
}
type StPublishFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
BusiReqData []byte `protobuf:"bytes,3,opt"`
From proto.Option[int32] `protobuf:"varint,4,opt"`
Src proto.Option[int32] `protobuf:"varint,5,opt"`
StoreFeedExtInfo []*CommonEntry `protobuf:"bytes,6,rep"`
JsonFeed proto.Option[string] `protobuf:"bytes,7,opt"`
ClientContent *StClientContent `protobuf:"bytes,8,opt"`
}
type StPublishFeedRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
BusiRspData []byte `protobuf:"bytes,3,opt"`
}

View File

@ -0,0 +1,154 @@
syntax = "proto2";
package channel;
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
import "pb/channel/GuildFeedCloudMeta.proto";
import "pb/channel/GuildChannelBase.proto";
message StAlterFeedReq {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional bytes busiReqData = 3;
optional uint64 mBitmap = 4;
optional int32 from = 5;
optional int32 src = 6;
repeated CommonEntry alterFeedExtInfo = 7;
optional string jsonFeed = 8;
optional StClientContent clientContent = 9;
}
message StAlterFeedRsp {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional bytes busiRspData = 3;
}
message StClientContent {
repeated StClientImageContent clientImageContents = 1;
repeated StClientVideoContent clientVideoContents = 2;
}
message StClientImageContent {
optional string taskId = 1;
optional string picId = 2;
optional string url = 3;
}
message StClientVideoContent {
optional string taskId = 1;
optional string videoId = 2;
optional string videoUrl = 3;
optional string coverUrl = 4;
}
message StDelFeedReq {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional int32 from = 3;
optional int32 src = 4;
}
message StDelFeedRsp {
optional StCommonExt extInfo = 1;
}
message StDoCommentReq {
optional StCommonExt extInfo = 1;
optional uint32 commentType = 2;
optional StComment comment = 3;
optional StFeed feed = 4;
optional int32 from = 5;
optional bytes busiReqData = 6;
optional int32 src = 7;
}
message StDoCommentRsp {
optional StCommonExt extInfo = 1;
optional StComment comment = 2;
optional bytes busiRspData = 3;
}
message StDoLikeReq {
optional StCommonExt extInfo = 1;
optional uint32 likeType = 2;
optional StLike like = 3;
optional StFeed feed = 4;
optional bytes busiReqData = 5;
optional StComment comment = 6;
optional StReply reply = 7;
optional int32 from = 8;
optional int32 src = 9;
optional StEmotionReactionInfo emotionReaction = 10;
}
message StDoLikeRsp {
optional StCommonExt extInfo = 1;
optional StLike like = 2;
optional bytes busiRspData = 3;
optional StEmotionReactionInfo emotionReaction = 4;
}
message StDoReplyReq {
optional StCommonExt extInfo = 1;
optional uint32 replyType = 2;
optional StReply reply = 3;
optional StComment comment = 4;
optional StFeed feed = 5;
optional int32 from = 6;
optional bytes busiReqData = 7;
optional int32 src = 8;
}
message StDoReplyRsp {
optional StCommonExt extInfo = 1;
optional StReply reply = 2;
optional bytes busiRspData = 3;
}
message StDoSecurityReq {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional StComment comment = 3;
optional StReply reply = 4;
optional StUser poster = 5;
optional int32 secType = 6;
}
message StDoSecurityRsp {
optional StCommonExt extInfo = 1;
}
message StModifyFeedReq {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional uint64 mBitmap = 3;
optional int32 from = 4;
optional int32 src = 5;
repeated CommonEntry modifyFeedExtInfo = 6;
}
message StModifyFeedRsp {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional bytes busiRspData = 3;
}
message StPublishFeedReq {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional bytes busiReqData = 3;
optional int32 from = 4;
optional int32 src = 5;
repeated CommonEntry storeFeedExtInfo = 6;
optional string jsonFeed = 7;
optional StClientContent clientContent = 8;
}
message StPublishFeedRsp {
optional StCommonExt extInfo = 1;
optional StFeed feed = 2;
optional bytes busiRspData = 3;
}

View File

@ -1,822 +1,61 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.14.0
// source: pb/channel/MsgResponsesSvr.proto // source: pb/channel/MsgResponsesSvr.proto
package channel package channel
import ( import (
reflect "reflect" proto "github.com/RomiChan/protobuf/proto"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type BatchGetMsgRspCountReq struct { type BatchGetMsgRspCountReq struct {
state protoimpl.MessageState GuildMsgList []*GuildMsg `protobuf:"bytes,1,rep"`
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
GuildMsgList []*GuildMsg `protobuf:"bytes,1,rep,name=guildMsgList" json:"guildMsgList,omitempty"`
}
func (x *BatchGetMsgRspCountReq) Reset() {
*x = BatchGetMsgRspCountReq{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BatchGetMsgRspCountReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BatchGetMsgRspCountReq) ProtoMessage() {}
func (x *BatchGetMsgRspCountReq) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BatchGetMsgRspCountReq.ProtoReflect.Descriptor instead.
func (*BatchGetMsgRspCountReq) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{0}
}
func (x *BatchGetMsgRspCountReq) GetGuildMsgList() []*GuildMsg {
if x != nil {
return x.GuildMsgList
}
return nil
} }
type BatchGetMsgRspCountRsp struct { type BatchGetMsgRspCountRsp struct {
state protoimpl.MessageState GuildMsgInfoList []*GuildMsgInfo `protobuf:"bytes,1,rep"`
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
GuildMsgInfoList []*GuildMsgInfo `protobuf:"bytes,1,rep,name=guildMsgInfoList" json:"guildMsgInfoList,omitempty"`
}
func (x *BatchGetMsgRspCountRsp) Reset() {
*x = BatchGetMsgRspCountRsp{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BatchGetMsgRspCountRsp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BatchGetMsgRspCountRsp) ProtoMessage() {}
func (x *BatchGetMsgRspCountRsp) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BatchGetMsgRspCountRsp.ProtoReflect.Descriptor instead.
func (*BatchGetMsgRspCountRsp) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{1}
}
func (x *BatchGetMsgRspCountRsp) GetGuildMsgInfoList() []*GuildMsgInfo {
if x != nil {
return x.GuildMsgInfoList
}
return nil
} }
type SvrChannelMsg struct { type SvrChannelMsg struct {
state protoimpl.MessageState ChannelId proto.Option[uint64] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache Id []*MsgId `protobuf:"bytes,2,rep"`
unknownFields protoimpl.UnknownFields
ChannelId *uint64 `protobuf:"varint,1,opt,name=channelId" json:"channelId,omitempty"`
Id []*MsgId `protobuf:"bytes,2,rep,name=id" json:"id,omitempty"`
}
func (x *SvrChannelMsg) Reset() {
*x = SvrChannelMsg{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SvrChannelMsg) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SvrChannelMsg) ProtoMessage() {}
func (x *SvrChannelMsg) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SvrChannelMsg.ProtoReflect.Descriptor instead.
func (*SvrChannelMsg) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{2}
}
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 {
state protoimpl.MessageState ChannelId proto.Option[uint64] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache RespData []*MsgRespData `protobuf:"bytes,2,rep"`
unknownFields protoimpl.UnknownFields
ChannelId *uint64 `protobuf:"varint,1,opt,name=channelId" json:"channelId,omitempty"`
RespData []*MsgRespData `protobuf:"bytes,2,rep,name=respData" json:"respData,omitempty"`
}
func (x *ChannelMsgInfo) Reset() {
*x = ChannelMsgInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ChannelMsgInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChannelMsgInfo) ProtoMessage() {}
func (x *ChannelMsgInfo) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ChannelMsgInfo.ProtoReflect.Descriptor instead.
func (*ChannelMsgInfo) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{3}
}
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 {
state protoimpl.MessageState EmojiId proto.Option[string] `protobuf:"bytes,1,opt"`
sizeCache protoimpl.SizeCache EmojiType proto.Option[uint64] `protobuf:"varint,2,opt"`
unknownFields protoimpl.UnknownFields Cnt proto.Option[uint64] `protobuf:"varint,3,opt"`
IsClicked proto.Option[bool] `protobuf:"varint,4,opt"`
EmojiId *string `protobuf:"bytes,1,opt,name=emojiId" json:"emojiId,omitempty"` IsDefaultEmoji proto.Option[bool] `protobuf:"varint,10001,opt"`
EmojiType *uint64 `protobuf:"varint,2,opt,name=emojiType" json:"emojiType,omitempty"` _ [0]func()
Cnt *uint64 `protobuf:"varint,3,opt,name=cnt" json:"cnt,omitempty"`
IsClicked *bool `protobuf:"varint,4,opt,name=isClicked" json:"isClicked,omitempty"`
}
func (x *EmojiReaction) Reset() {
*x = EmojiReaction{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *EmojiReaction) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EmojiReaction) ProtoMessage() {}
func (x *EmojiReaction) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EmojiReaction.ProtoReflect.Descriptor instead.
func (*EmojiReaction) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{4}
}
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
} }
type GuildMsg struct { type GuildMsg struct {
state protoimpl.MessageState GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache ChannelMsgList []*SvrChannelMsg `protobuf:"bytes,2,rep"`
unknownFields protoimpl.UnknownFields
GuildId *uint64 `protobuf:"varint,1,opt,name=guildId" json:"guildId,omitempty"`
ChannelMsgList []*SvrChannelMsg `protobuf:"bytes,2,rep,name=channelMsgList" json:"channelMsgList,omitempty"`
}
func (x *GuildMsg) Reset() {
*x = GuildMsg{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GuildMsg) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GuildMsg) ProtoMessage() {}
func (x *GuildMsg) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GuildMsg.ProtoReflect.Descriptor instead.
func (*GuildMsg) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{5}
}
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 {
state protoimpl.MessageState GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache ChannelMsgInfoList []*ChannelMsgInfo `protobuf:"bytes,2,rep"`
unknownFields protoimpl.UnknownFields
GuildId *uint64 `protobuf:"varint,1,opt,name=guildId" json:"guildId,omitempty"`
ChannelMsgInfoList []*ChannelMsgInfo `protobuf:"bytes,2,rep,name=channelMsgInfoList" json:"channelMsgInfoList,omitempty"`
}
func (x *GuildMsgInfo) Reset() {
*x = GuildMsgInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GuildMsgInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GuildMsgInfo) ProtoMessage() {}
func (x *GuildMsgInfo) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GuildMsgInfo.ProtoReflect.Descriptor instead.
func (*GuildMsgInfo) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{6}
}
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 {
state protoimpl.MessageState Id *MsgId `protobuf:"bytes,1,opt"`
sizeCache protoimpl.SizeCache EmojiReaction []*EmojiReaction `protobuf:"bytes,2,rep"`
unknownFields protoimpl.UnknownFields
Id *MsgId `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
EmojiReaction []*EmojiReaction `protobuf:"bytes,2,rep,name=emojiReaction" json:"emojiReaction,omitempty"`
}
func (x *MsgCnt) Reset() {
*x = MsgCnt{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MsgCnt) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MsgCnt) ProtoMessage() {}
func (x *MsgCnt) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MsgCnt.ProtoReflect.Descriptor instead.
func (*MsgCnt) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{7}
}
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 {
state protoimpl.MessageState Version proto.Option[uint64] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache Seq proto.Option[uint64] `protobuf:"varint,2,opt"`
unknownFields protoimpl.UnknownFields _ [0]func()
Version *uint64 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"`
Seq *uint64 `protobuf:"varint,2,opt,name=seq" json:"seq,omitempty"`
}
func (x *MsgId) Reset() {
*x = MsgId{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MsgId) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MsgId) ProtoMessage() {}
func (x *MsgId) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MsgId.ProtoReflect.Descriptor instead.
func (*MsgId) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{8}
}
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 {
state protoimpl.MessageState Id *MsgId `protobuf:"bytes,1,opt"`
sizeCache protoimpl.SizeCache Cnt []byte `protobuf:"bytes,2,opt"`
unknownFields protoimpl.UnknownFields
Id *MsgId `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
Cnt []byte `protobuf:"bytes,2,opt,name=cnt" json:"cnt,omitempty"`
}
func (x *MsgRespData) Reset() {
*x = MsgRespData{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MsgRespData) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MsgRespData) ProtoMessage() {}
func (x *MsgRespData) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_MsgResponsesSvr_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MsgRespData.ProtoReflect.Descriptor instead.
func (*MsgRespData) Descriptor() ([]byte, []int) {
return file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP(), []int{9}
}
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
}
var File_pb_channel_MsgResponsesSvr_proto protoreflect.FileDescriptor
var file_pb_channel_MsgResponsesSvr_proto_rawDesc = []byte{
0x0a, 0x20, 0x70, 0x62, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2f, 0x4d, 0x73, 0x67,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x53, 0x76, 0x72, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x22, 0x47, 0x0a, 0x16, 0x42, 0x61, 0x74, 0x63, 0x68, 0x47, 0x65, 0x74, 0x4d, 0x73,
0x67, 0x52, 0x73, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x2d, 0x0a, 0x0c,
0x67, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x73, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x09, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x73, 0x67, 0x52, 0x0c, 0x67,
0x75, 0x69, 0x6c, 0x64, 0x4d, 0x73, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x53, 0x0a, 0x16, 0x42,
0x61, 0x74, 0x63, 0x68, 0x47, 0x65, 0x74, 0x4d, 0x73, 0x67, 0x52, 0x73, 0x70, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x52, 0x73, 0x70, 0x12, 0x39, 0x0a, 0x10, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x73,
0x67, 0x49, 0x6e, 0x66, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x0d, 0x2e, 0x47, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10,
0x67, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x73, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x4c, 0x69, 0x73, 0x74,
0x22, 0x45, 0x0a, 0x0d, 0x53, 0x76, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73,
0x67, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12,
0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x4d, 0x73,
0x67, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x22, 0x58, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e,
0x65, 0x6c, 0x4d, 0x73, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x44,
0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x4d, 0x73, 0x67, 0x52,
0x65, 0x73, 0x70, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x44, 0x61, 0x74,
0x61, 0x22, 0x77, 0x0a, 0x0d, 0x45, 0x6d, 0x6f, 0x6a, 0x69, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x49, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09,
0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
0x09, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x6e,
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x63, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09,
0x69, 0x73, 0x43, 0x6c, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
0x09, 0x69, 0x73, 0x43, 0x6c, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x22, 0x5c, 0x0a, 0x08, 0x47, 0x75,
0x69, 0x6c, 0x64, 0x4d, 0x73, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64,
0x12, 0x36, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x4c, 0x69,
0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x53, 0x76, 0x72, 0x43, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x52, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x4d, 0x73, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x69, 0x0a, 0x0c, 0x47, 0x75, 0x69, 0x6c,
0x64, 0x4d, 0x73, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x75, 0x69, 0x6c,
0x64, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x75, 0x69, 0x6c, 0x64,
0x49, 0x64, 0x12, 0x3f, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73, 0x67,
0x49, 0x6e, 0x66, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f,
0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52,
0x12, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x4c,
0x69, 0x73, 0x74, 0x22, 0x56, 0x0a, 0x06, 0x4d, 0x73, 0x67, 0x43, 0x6e, 0x74, 0x12, 0x16, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x4d, 0x73, 0x67, 0x49,
0x64, 0x52, 0x02, 0x69, 0x64, 0x12, 0x34, 0x0a, 0x0d, 0x65, 0x6d, 0x6f, 0x6a, 0x69, 0x52, 0x65,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x45,
0x6d, 0x6f, 0x6a, 0x69, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x65, 0x6d,
0x6f, 0x6a, 0x69, 0x52, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x33, 0x0a, 0x05, 0x4d,
0x73, 0x67, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10,
0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x73, 0x65, 0x71,
0x22, 0x37, 0x0a, 0x0b, 0x4d, 0x73, 0x67, 0x52, 0x65, 0x73, 0x70, 0x44, 0x61, 0x74, 0x61, 0x12,
0x16, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x4d, 0x73,
0x67, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x6e, 0x74, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x6e, 0x74, 0x42, 0x14, 0x5a, 0x12, 0x70, 0x62, 0x2f,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x3b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
}
var (
file_pb_channel_MsgResponsesSvr_proto_rawDescOnce sync.Once
file_pb_channel_MsgResponsesSvr_proto_rawDescData = file_pb_channel_MsgResponsesSvr_proto_rawDesc
)
func file_pb_channel_MsgResponsesSvr_proto_rawDescGZIP() []byte {
file_pb_channel_MsgResponsesSvr_proto_rawDescOnce.Do(func() {
file_pb_channel_MsgResponsesSvr_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_channel_MsgResponsesSvr_proto_rawDescData)
})
return file_pb_channel_MsgResponsesSvr_proto_rawDescData
}
var file_pb_channel_MsgResponsesSvr_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_pb_channel_MsgResponsesSvr_proto_goTypes = []interface{}{
(*BatchGetMsgRspCountReq)(nil), // 0: BatchGetMsgRspCountReq
(*BatchGetMsgRspCountRsp)(nil), // 1: BatchGetMsgRspCountRsp
(*SvrChannelMsg)(nil), // 2: SvrChannelMsg
(*ChannelMsgInfo)(nil), // 3: ChannelMsgInfo
(*EmojiReaction)(nil), // 4: EmojiReaction
(*GuildMsg)(nil), // 5: GuildMsg
(*GuildMsgInfo)(nil), // 6: GuildMsgInfo
(*MsgCnt)(nil), // 7: MsgCnt
(*MsgId)(nil), // 8: MsgId
(*MsgRespData)(nil), // 9: MsgRespData
}
var file_pb_channel_MsgResponsesSvr_proto_depIdxs = []int32{
5, // 0: BatchGetMsgRspCountReq.guildMsgList:type_name -> GuildMsg
6, // 1: BatchGetMsgRspCountRsp.guildMsgInfoList:type_name -> GuildMsgInfo
8, // 2: SvrChannelMsg.id:type_name -> MsgId
9, // 3: ChannelMsgInfo.respData:type_name -> MsgRespData
2, // 4: GuildMsg.channelMsgList:type_name -> SvrChannelMsg
3, // 5: GuildMsgInfo.channelMsgInfoList:type_name -> ChannelMsgInfo
8, // 6: MsgCnt.id:type_name -> MsgId
4, // 7: MsgCnt.emojiReaction:type_name -> EmojiReaction
8, // 8: MsgRespData.id:type_name -> MsgId
9, // [9:9] is the sub-list for method output_type
9, // [9:9] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_pb_channel_MsgResponsesSvr_proto_init() }
func file_pb_channel_MsgResponsesSvr_proto_init() {
if File_pb_channel_MsgResponsesSvr_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pb_channel_MsgResponsesSvr_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BatchGetMsgRspCountReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BatchGetMsgRspCountRsp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SvrChannelMsg); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ChannelMsgInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*EmojiReaction); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GuildMsg); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GuildMsgInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MsgCnt); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MsgId); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_MsgResponsesSvr_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MsgRespData); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_channel_MsgResponsesSvr_proto_rawDesc,
NumEnums: 0,
NumMessages: 10,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_pb_channel_MsgResponsesSvr_proto_goTypes,
DependencyIndexes: file_pb_channel_MsgResponsesSvr_proto_depIdxs,
MessageInfos: file_pb_channel_MsgResponsesSvr_proto_msgTypes,
}.Build()
File_pb_channel_MsgResponsesSvr_proto = out.File
file_pb_channel_MsgResponsesSvr_proto_rawDesc = nil
file_pb_channel_MsgResponsesSvr_proto_goTypes = nil
file_pb_channel_MsgResponsesSvr_proto_depIdxs = nil
} }

View File

@ -1,7 +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;
@ -26,6 +25,7 @@ message EmojiReaction {
optional uint64 emojiType = 2; optional uint64 emojiType = 2;
optional uint64 cnt = 3; optional uint64 cnt = 3;
optional bool isClicked = 4; optional bool isClicked = 4;
optional bool isDefaultEmoji = 10001;
} }
message GuildMsg { message GuildMsg {

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;channel";
import "pb/msg/msg.proto"; import "pb/msg/msg.proto";
@ -81,9 +81,9 @@ message ChannelMsgContent {
} }
message ChannelMsgCtrlHead { message ChannelMsgCtrlHead {
repeated uint64 includeUin = 1; repeated bytes includeUin = 1;
repeated uint64 excludeUin = 2; // repeated uint64 excludeUin = 2; // bytes?
repeated uint64 featureid = 3; // repeated uint64 featureid = 3;
optional uint32 offlineFlag = 4; optional uint32 offlineFlag = 4;
optional uint32 visibility = 5; optional uint32 visibility = 5;
optional uint64 ctrlFlag = 6; optional uint64 ctrlFlag = 6;

View File

@ -1,494 +1,37 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.14.0
// source: pb/channel/msgpush.proto // source: pb/channel/msgpush.proto
package channel package channel
import ( import (
reflect "reflect" proto "github.com/RomiChan/protobuf/proto"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type FocusInfo struct { type FocusInfo struct {
state protoimpl.MessageState ChannelIdList []uint64 `protobuf:"varint,1,rep"`
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ChannelIdList []uint64 `protobuf:"varint,1,rep,name=channelIdList" json:"channelIdList,omitempty"`
}
func (x *FocusInfo) Reset() {
*x = FocusInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_msgpush_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *FocusInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*FocusInfo) ProtoMessage() {}
func (x *FocusInfo) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_msgpush_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use FocusInfo.ProtoReflect.Descriptor instead.
func (*FocusInfo) Descriptor() ([]byte, []int) {
return file_pb_channel_msgpush_proto_rawDescGZIP(), []int{0}
}
func (x *FocusInfo) GetChannelIdList() []uint64 {
if x != nil {
return x.ChannelIdList
}
return nil
} }
type MsgOnlinePush struct { type MsgOnlinePush struct {
state protoimpl.MessageState Msgs []*ChannelMsgContent `protobuf:"bytes,1,rep"`
sizeCache protoimpl.SizeCache GeneralFlag proto.Option[uint32] `protobuf:"varint,2,opt"`
unknownFields protoimpl.UnknownFields NeedResp proto.Option[uint32] `protobuf:"varint,3,opt"`
ServerBuf []byte `protobuf:"bytes,4,opt"`
Msgs []*ChannelMsgContent `protobuf:"bytes,1,rep,name=msgs" json:"msgs,omitempty"` CompressFlag proto.Option[uint32] `protobuf:"varint,5,opt"`
GeneralFlag *uint32 `protobuf:"varint,2,opt,name=generalFlag" json:"generalFlag,omitempty"` CompressMsg []byte `protobuf:"bytes,6,opt"`
NeedResp *uint32 `protobuf:"varint,3,opt,name=needResp" json:"needResp,omitempty"` FocusInfo *FocusInfo `protobuf:"bytes,7,opt"`
ServerBuf []byte `protobuf:"bytes,4,opt,name=serverBuf" json:"serverBuf,omitempty"` HugeFlag proto.Option[uint32] `protobuf:"varint,8,opt"`
CompressFlag *uint32 `protobuf:"varint,5,opt,name=compressFlag" json:"compressFlag,omitempty"`
CompressMsg []byte `protobuf:"bytes,6,opt,name=compressMsg" json:"compressMsg,omitempty"`
FocusInfo *FocusInfo `protobuf:"bytes,7,opt,name=focusInfo" json:"focusInfo,omitempty"`
HugeFlag *uint32 `protobuf:"varint,8,opt,name=hugeFlag" json:"hugeFlag,omitempty"`
}
func (x *MsgOnlinePush) Reset() {
*x = MsgOnlinePush{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_msgpush_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MsgOnlinePush) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MsgOnlinePush) ProtoMessage() {}
func (x *MsgOnlinePush) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_msgpush_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MsgOnlinePush.ProtoReflect.Descriptor instead.
func (*MsgOnlinePush) Descriptor() ([]byte, []int) {
return file_pb_channel_msgpush_proto_rawDescGZIP(), []int{1}
}
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 {
state protoimpl.MessageState ServerBuf []byte `protobuf:"bytes,1,opt"`
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ServerBuf []byte `protobuf:"bytes,1,opt,name=serverBuf" json:"serverBuf,omitempty"`
}
func (x *MsgPushResp) Reset() {
*x = MsgPushResp{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_msgpush_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *MsgPushResp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MsgPushResp) ProtoMessage() {}
func (x *MsgPushResp) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_msgpush_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MsgPushResp.ProtoReflect.Descriptor instead.
func (*MsgPushResp) Descriptor() ([]byte, []int) {
return file_pb_channel_msgpush_proto_rawDescGZIP(), []int{2}
}
func (x *MsgPushResp) GetServerBuf() []byte {
if x != nil {
return x.ServerBuf
}
return nil
} }
type PressMsg struct { type PressMsg struct {
state protoimpl.MessageState Msgs []*ChannelMsgContent `protobuf:"bytes,1,rep"`
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Msgs []*ChannelMsgContent `protobuf:"bytes,1,rep,name=msgs" json:"msgs,omitempty"`
}
func (x *PressMsg) Reset() {
*x = PressMsg{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_msgpush_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PressMsg) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PressMsg) ProtoMessage() {}
func (x *PressMsg) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_msgpush_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PressMsg.ProtoReflect.Descriptor instead.
func (*PressMsg) Descriptor() ([]byte, []int) {
return file_pb_channel_msgpush_proto_rawDescGZIP(), []int{3}
}
func (x *PressMsg) GetMsgs() []*ChannelMsgContent {
if x != nil {
return x.Msgs
}
return nil
} }
type ServerBuf struct { type ServerBuf struct {
state protoimpl.MessageState SvrIp proto.Option[uint32] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache SvrPort proto.Option[uint32] `protobuf:"varint,2,opt"`
unknownFields protoimpl.UnknownFields EchoKey []byte `protobuf:"bytes,3,opt"`
SvrIp *uint32 `protobuf:"varint,1,opt,name=svrIp" json:"svrIp,omitempty"`
SvrPort *uint32 `protobuf:"varint,2,opt,name=svrPort" json:"svrPort,omitempty"`
EchoKey []byte `protobuf:"bytes,3,opt,name=echoKey" json:"echoKey,omitempty"`
}
func (x *ServerBuf) Reset() {
*x = ServerBuf{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_msgpush_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ServerBuf) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ServerBuf) ProtoMessage() {}
func (x *ServerBuf) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_msgpush_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ServerBuf.ProtoReflect.Descriptor instead.
func (*ServerBuf) Descriptor() ([]byte, []int) {
return file_pb_channel_msgpush_proto_rawDescGZIP(), []int{4}
}
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
}
var File_pb_channel_msgpush_proto protoreflect.FileDescriptor
var file_pb_channel_msgpush_proto_rawDesc = []byte{
0x0a, 0x18, 0x70, 0x62, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2f, 0x6d, 0x73, 0x67,
0x70, 0x75, 0x73, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x1a, 0x17, 0x70, 0x62, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2f,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x31, 0x0a, 0x09,
0x46, 0x6f, 0x63, 0x75, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04,
0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x22,
0xaf, 0x02, 0x0a, 0x0d, 0x4d, 0x73, 0x67, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x75, 0x73,
0x68, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x73, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x4d, 0x73, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x6d, 0x73, 0x67,
0x73, 0x12, 0x20, 0x0a, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6c, 0x61, 0x67,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x46,
0x6c, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6e, 0x65, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12,
0x1c, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x75, 0x66, 0x18, 0x04, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x75, 0x66, 0x12, 0x22, 0x0a,
0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x18, 0x05, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x46, 0x6c, 0x61,
0x67, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x73, 0x67,
0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73,
0x4d, 0x73, 0x67, 0x12, 0x30, 0x0a, 0x09, 0x66, 0x6f, 0x63, 0x75, 0x73, 0x49, 0x6e, 0x66, 0x6f,
0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x2e, 0x46, 0x6f, 0x63, 0x75, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x66, 0x6f, 0x63, 0x75,
0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x75, 0x67, 0x65, 0x46, 0x6c, 0x61,
0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x68, 0x75, 0x67, 0x65, 0x46, 0x6c, 0x61,
0x67, 0x22, 0x2b, 0x0a, 0x0b, 0x4d, 0x73, 0x67, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70,
0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x75, 0x66, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x75, 0x66, 0x22, 0x3a,
0x0a, 0x08, 0x50, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x73, 0x67, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x73,
0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e,
0x65, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x43, 0x6f, 0x6e,
0x74, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x6d, 0x73, 0x67, 0x73, 0x22, 0x55, 0x0a, 0x09, 0x53, 0x65,
0x72, 0x76, 0x65, 0x72, 0x42, 0x75, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x76, 0x72, 0x49, 0x70,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x76, 0x72, 0x49, 0x70, 0x12, 0x18, 0x0a,
0x07, 0x73, 0x76, 0x72, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x73, 0x76, 0x72, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x63, 0x68, 0x6f, 0x4b,
0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x63, 0x68, 0x6f, 0x4b, 0x65,
0x79, 0x42, 0x14, 0x5a, 0x12, 0x70, 0x62, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x3b,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
}
var (
file_pb_channel_msgpush_proto_rawDescOnce sync.Once
file_pb_channel_msgpush_proto_rawDescData = file_pb_channel_msgpush_proto_rawDesc
)
func file_pb_channel_msgpush_proto_rawDescGZIP() []byte {
file_pb_channel_msgpush_proto_rawDescOnce.Do(func() {
file_pb_channel_msgpush_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_channel_msgpush_proto_rawDescData)
})
return file_pb_channel_msgpush_proto_rawDescData
}
var file_pb_channel_msgpush_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_pb_channel_msgpush_proto_goTypes = []interface{}{
(*FocusInfo)(nil), // 0: channel.FocusInfo
(*MsgOnlinePush)(nil), // 1: channel.MsgOnlinePush
(*MsgPushResp)(nil), // 2: channel.MsgPushResp
(*PressMsg)(nil), // 3: channel.PressMsg
(*ServerBuf)(nil), // 4: channel.ServerBuf
(*ChannelMsgContent)(nil), // 5: channel.ChannelMsgContent
}
var file_pb_channel_msgpush_proto_depIdxs = []int32{
5, // 0: channel.MsgOnlinePush.msgs:type_name -> channel.ChannelMsgContent
0, // 1: channel.MsgOnlinePush.focusInfo:type_name -> channel.FocusInfo
5, // 2: channel.PressMsg.msgs:type_name -> channel.ChannelMsgContent
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_pb_channel_msgpush_proto_init() }
func file_pb_channel_msgpush_proto_init() {
if File_pb_channel_msgpush_proto != nil {
return
}
file_pb_channel_common_proto_init()
if !protoimpl.UnsafeEnabled {
file_pb_channel_msgpush_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*FocusInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_msgpush_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MsgOnlinePush); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_msgpush_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MsgPushResp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_msgpush_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PressMsg); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_msgpush_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ServerBuf); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_channel_msgpush_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_pb_channel_msgpush_proto_goTypes,
DependencyIndexes: file_pb_channel_msgpush_proto_depIdxs,
MessageInfos: file_pb_channel_msgpush_proto_msgTypes,
}.Build()
File_pb_channel_msgpush_proto = out.File
file_pb_channel_msgpush_proto_rawDesc = nil
file_pb_channel_msgpush_proto_goTypes = nil
file_pb_channel_msgpush_proto_depIdxs = 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

@ -1,372 +1,32 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.14.0
// source: pb/channel/oidb0xf62.proto // source: pb/channel/oidb0xf62.proto
package channel package channel
import ( import (
reflect "reflect" msg "github.com/Mrs4s/MiraiGo/client/pb/msg"
sync "sync" proto "github.com/RomiChan/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type DF62ReqBody struct { type DF62ReqBody struct {
state protoimpl.MessageState Msg *ChannelMsgContent `protobuf:"bytes,1,opt"`
sizeCache protoimpl.SizeCache _ [0]func()
unknownFields protoimpl.UnknownFields
Msg *ChannelMsgContent `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}
func (x *DF62ReqBody) Reset() {
*x = DF62ReqBody{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_oidb0xf62_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DF62ReqBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DF62ReqBody) ProtoMessage() {}
func (x *DF62ReqBody) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_oidb0xf62_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DF62ReqBody.ProtoReflect.Descriptor instead.
func (*DF62ReqBody) Descriptor() ([]byte, []int) {
return file_pb_channel_oidb0xf62_proto_rawDescGZIP(), []int{0}
}
func (x *DF62ReqBody) GetMsg() *ChannelMsgContent {
if x != nil {
return x.Msg
}
return nil
} }
type DF62RspBody struct { type DF62RspBody struct {
state protoimpl.MessageState Result proto.Option[uint32] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache Errmsg []byte `protobuf:"bytes,2,opt"`
unknownFields protoimpl.UnknownFields SendTime proto.Option[uint32] `protobuf:"varint,3,opt"`
Head *ChannelMsgHead `protobuf:"bytes,4,opt"`
Result *uint32 `protobuf:"varint,1,opt,name=result" json:"result,omitempty"` ErrType proto.Option[uint32] `protobuf:"varint,5,opt"`
Errmsg []byte `protobuf:"bytes,2,opt,name=errmsg" json:"errmsg,omitempty"` TransSvrInfo *TransSvrInfo `protobuf:"bytes,6,opt"`
SendTime *uint32 `protobuf:"varint,3,opt,name=sendTime" json:"sendTime,omitempty"` FreqLimitInfo *ChannelFreqLimitInfo `protobuf:"bytes,7,opt"`
Head *ChannelMsgHead `protobuf:"bytes,4,opt,name=head" json:"head,omitempty"` Body *msg.MessageBody `protobuf:"bytes,8,opt"`
ErrType *uint32 `protobuf:"varint,5,opt,name=errType" json:"errType,omitempty"`
TransSvrInfo *TransSvrInfo `protobuf:"bytes,6,opt,name=transSvrInfo" json:"transSvrInfo,omitempty"`
FreqLimitInfo *ChannelFreqLimitInfo `protobuf:"bytes,7,opt,name=freqLimitInfo" json:"freqLimitInfo,omitempty"`
}
func (x *DF62RspBody) Reset() {
*x = DF62RspBody{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_oidb0xf62_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DF62RspBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DF62RspBody) ProtoMessage() {}
func (x *DF62RspBody) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_oidb0xf62_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DF62RspBody.ProtoReflect.Descriptor instead.
func (*DF62RspBody) Descriptor() ([]byte, []int) {
return file_pb_channel_oidb0xf62_proto_rawDescGZIP(), []int{1}
}
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
} }
type TransSvrInfo struct { type TransSvrInfo struct {
state protoimpl.MessageState SubType proto.Option[uint32] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache RetCode proto.Option[int32] `protobuf:"varint,2,opt"`
unknownFields protoimpl.UnknownFields ErrMsg []byte `protobuf:"bytes,3,opt"`
TransInfo []byte `protobuf:"bytes,4,opt"`
SubType *uint32 `protobuf:"varint,1,opt,name=subType" json:"subType,omitempty"`
RetCode *int32 `protobuf:"varint,2,opt,name=retCode" json:"retCode,omitempty"`
ErrMsg []byte `protobuf:"bytes,3,opt,name=errMsg" json:"errMsg,omitempty"`
TransInfo []byte `protobuf:"bytes,4,opt,name=transInfo" json:"transInfo,omitempty"`
}
func (x *TransSvrInfo) Reset() {
*x = TransSvrInfo{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_channel_oidb0xf62_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TransSvrInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TransSvrInfo) ProtoMessage() {}
func (x *TransSvrInfo) ProtoReflect() protoreflect.Message {
mi := &file_pb_channel_oidb0xf62_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TransSvrInfo.ProtoReflect.Descriptor instead.
func (*TransSvrInfo) Descriptor() ([]byte, []int) {
return file_pb_channel_oidb0xf62_proto_rawDescGZIP(), []int{2}
}
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
}
var File_pb_channel_oidb0xf62_proto protoreflect.FileDescriptor
var file_pb_channel_oidb0xf62_proto_rawDesc = []byte{
0x0a, 0x1a, 0x70, 0x62, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2f, 0x6f, 0x69, 0x64,
0x62, 0x30, 0x78, 0x66, 0x36, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x63, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x1a, 0x17, 0x70, 0x62, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3b,
0x0a, 0x0b, 0x44, 0x46, 0x36, 0x32, 0x52, 0x65, 0x71, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x2c, 0x0a,
0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x68, 0x61,
0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x43,
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0xa0, 0x02, 0x0a, 0x0b,
0x44, 0x46, 0x36, 0x32, 0x52, 0x73, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x72,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x73,
0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6d, 0x73, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x73,
0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73,
0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x64, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e,
0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4d, 0x73, 0x67, 0x48, 0x65, 0x61, 0x64, 0x52, 0x04,
0x68, 0x65, 0x61, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x72, 0x72, 0x54, 0x79, 0x70, 0x65, 0x18,
0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x72, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39,
0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x53, 0x76, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x06,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x54,
0x72, 0x61, 0x6e, 0x73, 0x53, 0x76, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x74, 0x72, 0x61,
0x6e, 0x73, 0x53, 0x76, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x43, 0x0a, 0x0d, 0x66, 0x72, 0x65,
0x71, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1d, 0x2e, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e,
0x65, 0x6c, 0x46, 0x72, 0x65, 0x71, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52,
0x0d, 0x66, 0x72, 0x65, 0x71, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x78,
0x0a, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x53, 0x76, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18,
0x0a, 0x07, 0x73, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x07, 0x73, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x43,
0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x72, 0x65, 0x74, 0x43, 0x6f,
0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x4d, 0x73, 0x67, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x06, 0x65, 0x72, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72,
0x61, 0x6e, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x14, 0x5a, 0x12, 0x70, 0x62, 0x2f, 0x63,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x3b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
}
var (
file_pb_channel_oidb0xf62_proto_rawDescOnce sync.Once
file_pb_channel_oidb0xf62_proto_rawDescData = file_pb_channel_oidb0xf62_proto_rawDesc
)
func file_pb_channel_oidb0xf62_proto_rawDescGZIP() []byte {
file_pb_channel_oidb0xf62_proto_rawDescOnce.Do(func() {
file_pb_channel_oidb0xf62_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_channel_oidb0xf62_proto_rawDescData)
})
return file_pb_channel_oidb0xf62_proto_rawDescData
}
var file_pb_channel_oidb0xf62_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_pb_channel_oidb0xf62_proto_goTypes = []interface{}{
(*DF62ReqBody)(nil), // 0: channel.DF62ReqBody
(*DF62RspBody)(nil), // 1: channel.DF62RspBody
(*TransSvrInfo)(nil), // 2: channel.TransSvrInfo
(*ChannelMsgContent)(nil), // 3: channel.ChannelMsgContent
(*ChannelMsgHead)(nil), // 4: channel.ChannelMsgHead
(*ChannelFreqLimitInfo)(nil), // 5: channel.ChannelFreqLimitInfo
}
var file_pb_channel_oidb0xf62_proto_depIdxs = []int32{
3, // 0: channel.DF62ReqBody.msg:type_name -> channel.ChannelMsgContent
4, // 1: channel.DF62RspBody.head:type_name -> channel.ChannelMsgHead
2, // 2: channel.DF62RspBody.transSvrInfo:type_name -> channel.TransSvrInfo
5, // 3: channel.DF62RspBody.freqLimitInfo:type_name -> channel.ChannelFreqLimitInfo
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_pb_channel_oidb0xf62_proto_init() }
func file_pb_channel_oidb0xf62_proto_init() {
if File_pb_channel_oidb0xf62_proto != nil {
return
}
file_pb_channel_common_proto_init()
if !protoimpl.UnsafeEnabled {
file_pb_channel_oidb0xf62_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DF62ReqBody); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_oidb0xf62_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DF62RspBody); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_channel_oidb0xf62_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TransSvrInfo); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_channel_oidb0xf62_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_pb_channel_oidb0xf62_proto_goTypes,
DependencyIndexes: file_pb_channel_oidb0xf62_proto_depIdxs,
MessageInfos: file_pb_channel_oidb0xf62_proto_msgTypes,
}.Build()
File_pb_channel_oidb0xf62_proto = out.File
file_pb_channel_oidb0xf62_proto_rawDesc = nil
file_pb_channel_oidb0xf62_proto_goTypes = nil
file_pb_channel_oidb0xf62_proto_depIdxs = nil
} }

View File

@ -2,8 +2,10 @@ 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";
message DF62ReqBody { message DF62ReqBody {
optional ChannelMsgContent msg = 1; optional ChannelMsgContent msg = 1;
@ -17,6 +19,7 @@ message DF62RspBody {
optional uint32 errType = 5; optional uint32 errType = 5;
optional TransSvrInfo transSvrInfo = 6; optional TransSvrInfo transSvrInfo = 6;
optional ChannelFreqLimitInfo freqLimitInfo = 7; optional ChannelFreqLimitInfo freqLimitInfo = 7;
optional msg.MessageBody body = 8;
} }
message TransSvrInfo { message TransSvrInfo {

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;
@ -142,7 +142,21 @@ message EventBody {
repeated MsgEvent events = 39; repeated MsgEvent events = 39;
optional SchedulerMsg scheduler = 40; optional SchedulerMsg scheduler = 40;
optional AppChannelMsg appChannel = 41; optional AppChannelMsg appChannel = 41;
optional FeedEvent feedEvent = 44;
optional AppChannelMsg weakMsgAppChannel = 46; optional AppChannelMsg weakMsgAppChannel = 46;
optional ReadFeedNotify readFeedNotify = 48;
}
message FeedEvent {
optional uint64 guildId = 1;
optional uint64 channelId = 2;
optional string feedId = 3;
optional string msgSummary = 4;
optional uint64 eventTime = 5;
}
message ReadFeedNotify {
optional uint64 reportTime = 2;
} }
message GroupProStatus { message GroupProStatus {
@ -286,9 +300,15 @@ message SwitchDetail {
message SwitchLiveRoom { message SwitchLiveRoom {
optional uint64 guildId = 1; optional uint64 guildId = 1;
optional uint64 channelId = 2; optional uint64 channelId = 2;
optional uint64 roomId = 3; // optional uint64 roomId = 3;
optional uint64 tinyid = 4; // optional uint64 tinyid = 4;
optional uint32 action = 5; optional SwitchLiveRoomUserInfo userInfo = 3;
optional uint32 action = 4; // JOIN = 1 QUIT = 2
}
message SwitchLiveRoomUserInfo {
optional uint64 tinyId = 1;
optional string nickname = 2;
} }
message SwitchVoiceChannel { message SwitchVoiceChannel {

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/common.proto"; import "pb/channel/common.proto";

File diff suppressed because it is too large Load Diff

View File

@ -3,13 +3,18 @@ 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
message ChannelOidb0xf5bRsp { message ChannelOidb0xf5bRsp {
optional uint64 guildId = 1; optional uint64 guildId = 1;
repeated GuildMemberInfo bots = 4; repeated GuildMemberInfo bots = 4;
repeated GuildMemberInfo members = 5; repeated GuildMemberInfo members = 5;
optional GuildAdminInfo adminInfo = 25; optional uint32 nextIndex = 10;
optional uint32 finished = 9;
optional string nextQueryParam = 24;
repeated GuildGroupMembersInfo memberWithRoles = 25;
optional uint64 nextRoleIdIndex = 26;
} }
message ChannelOidb0xf88Rsp { message ChannelOidb0xf88Rsp {
@ -32,6 +37,49 @@ message ChannelOidb0xf5dRsp {
optional ChannelListRsp rsp = 1; optional ChannelListRsp rsp = 1;
} }
message ChannelOidb0x1017Rsp {
optional P10x1017 p1 = 1;
}
message P10x1017 {
optional uint64 tinyId = 1;
repeated GuildUserRole roles = 3;
}
message ChannelOidb0x1019Rsp {
optional uint64 guildId = 1;
repeated GuildRole roles = 2;
// 3: ?
// 4:
}
/*
message ChannelOidb0x100dReq { // 修改身份组
optional uint64 guildId = 1;
repeated uint64 roleId = 2;
repeated int32 unkonwn = 3; // 3: ? 三个1
repeated ModifyGuildRole role = 4;
}*/
/*
message ChannelOidb0x1016Req { // 新建身份组
optional uint64 guildId = 1;
repeated int32 unknown = 2; // 2: ? 三个1
optional ModifyGuildRole role = 3;
repeated uint64 initialUsers = 4;
}*/
message ChannelOidb0x1016Rsp {
optional uint64 roleId = 2;
}
/*
message ChannelOidb0x101aReq { // 修改身份组
optional uint64 guildId = 1;
repeated SetGuildRole setRoles = 2;
repeated SetGuildRole removeRoles = 3;
}*/
message GuildMetaRsp { message GuildMetaRsp {
optional uint64 guildId = 3; optional uint64 guildId = 3;
optional GuildMeta meta = 4; optional GuildMeta meta = 4;
@ -43,10 +91,14 @@ message ChannelListRsp {
// 5: Category infos // 5: Category infos
} }
message GuildAdminInfo { message GuildGroupMembersInfo {
repeated GuildMemberInfo admins = 2; optional uint64 roleId = 1;
repeated GuildMemberInfo members = 2;
optional string roleName = 3;
optional uint32 color = 4;
} }
// see sub_374334
message GuildMemberInfo { message GuildMemberInfo {
optional string title = 2; optional string title = 2;
optional string nickname = 3; optional string nickname = 3;
@ -67,6 +119,38 @@ message GuildUserProfile {
// 25 current cards *uncertainty // 25 current cards *uncertainty
} }
message GuildRole {
optional uint64 roleId = 1;
optional string name = 2;
optional uint32 argbColor = 3;
optional int32 independent = 4;
optional int32 num = 5;
optional int32 owned = 6; // 是否拥有 存疑
optional int32 disabled = 7; // 权限不足或不显示
optional int32 maxNum = 8;
// 9: ?
}
message GuildUserRole {
optional uint64 roleId = 1;
optional string name = 2;
optional uint32 argbColor = 3;
optional int32 independent = 4;
}
/*
message SetGuildRole {
optional uint64 roleId = 1;
optional uint64 targetId = 2;
}*/
/*
message ModifyGuildRole {
optional string roleName = 1;
optional uint32 color = 2;
optional int32 independent = 3; // 身份组单独显示
}*/
message GuildMeta { message GuildMeta {
optional uint64 guildCode = 2; optional uint64 guildCode = 2;
optional int64 createTime = 4; optional int64 createTime = 4;

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,803 +1,77 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// versions: // source: pb/cmd0x352/cmd0x352.proto
// protoc-gen-go v1.26.0
// protoc v3.17.1
// source: cmd0x352.proto
package cmd0x352 package cmd0x352
import ( import (
reflect "reflect" proto "github.com/RomiChan/protobuf/proto"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type ReqBody struct { type ReqBody struct {
state protoimpl.MessageState Subcmd proto.Option[uint32] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache TryupImgReq []*D352TryUpImgReq `protobuf:"bytes,2,rep"`
unknownFields protoimpl.UnknownFields
Subcmd *uint32 `protobuf:"varint,1,opt,name=subcmd" json:"subcmd,omitempty"`
TryupImgReq []*D352TryUpImgReq `protobuf:"bytes,2,rep,name=tryupImgReq" json:"tryupImgReq,omitempty"`
// repeated GetImgUrlReq getimgUrlReq = 3; // repeated GetImgUrlReq getimgUrlReq = 3;
// repeated DelImgReq delImgReq = 4; // repeated DelImgReq delImgReq = 4;
NetType *uint32 `protobuf:"varint,10,opt,name=netType" json:"netType,omitempty"` NetType proto.Option[uint32] `protobuf:"varint,10,opt"`
}
func (x *ReqBody) Reset() {
*x = ReqBody{}
if protoimpl.UnsafeEnabled {
mi := &file_cmd0x352_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReqBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReqBody) ProtoMessage() {}
func (x *ReqBody) ProtoReflect() protoreflect.Message {
mi := &file_cmd0x352_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReqBody.ProtoReflect.Descriptor instead.
func (*ReqBody) Descriptor() ([]byte, []int) {
return file_cmd0x352_proto_rawDescGZIP(), []int{0}
}
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 {
state protoimpl.MessageState Subcmd proto.Option[uint32] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache TryupImgRsp []*TryUpImgRsp `protobuf:"bytes,2,rep"`
unknownFields protoimpl.UnknownFields
Subcmd *uint32 `protobuf:"varint,1,opt,name=subcmd" json:"subcmd,omitempty"`
TryupImgRsp []*TryUpImgRsp `protobuf:"bytes,2,rep,name=tryupImgRsp" json:"tryupImgRsp,omitempty"`
// repeated GetImgUrlRsp getimgUrlRsp = 3; // repeated GetImgUrlRsp getimgUrlRsp = 3;
NewBigchan *bool `protobuf:"varint,4,opt,name=newBigchan" json:"newBigchan,omitempty"` NewBigchan proto.Option[bool] `protobuf:"varint,4,opt"`
// repeated DelImgRsp delImgRsp = 5; // repeated DelImgRsp delImgRsp = 5;
FailMsg []byte `protobuf:"bytes,10,opt,name=failMsg" json:"failMsg,omitempty"` FailMsg []byte `protobuf:"bytes,10,opt"`
}
func (x *RspBody) Reset() {
*x = RspBody{}
if protoimpl.UnsafeEnabled {
mi := &file_cmd0x352_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RspBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RspBody) ProtoMessage() {}
func (x *RspBody) ProtoReflect() protoreflect.Message {
mi := &file_cmd0x352_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RspBody.ProtoReflect.Descriptor instead.
func (*RspBody) Descriptor() ([]byte, []int) {
return file_cmd0x352_proto_rawDescGZIP(), []int{1}
}
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 {
state protoimpl.MessageState SrcUin proto.Option[uint64] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache DstUin proto.Option[uint64] `protobuf:"varint,2,opt"`
unknownFields protoimpl.UnknownFields FileId proto.Option[uint64] `protobuf:"varint,3,opt"`
FileMd5 []byte `protobuf:"bytes,4,opt"`
SrcUin *uint64 `protobuf:"varint,1,opt,name=srcUin" json:"srcUin,omitempty"` FileSize proto.Option[uint64] `protobuf:"varint,5,opt"`
DstUin *uint64 `protobuf:"varint,2,opt,name=dstUin" json:"dstUin,omitempty"` FileName []byte `protobuf:"bytes,6,opt"`
FileId *uint64 `protobuf:"varint,3,opt,name=fileId" json:"fileId,omitempty"` SrcTerm proto.Option[uint32] `protobuf:"varint,7,opt"`
FileMd5 []byte `protobuf:"bytes,4,opt,name=fileMd5" json:"fileMd5,omitempty"` PlatformType proto.Option[uint32] `protobuf:"varint,8,opt"`
FileSize *uint64 `protobuf:"varint,5,opt,name=fileSize" json:"fileSize,omitempty"` InnerIp proto.Option[uint32] `protobuf:"varint,9,opt"`
FileName []byte `protobuf:"bytes,6,opt,name=fileName" json:"fileName,omitempty"` AddressBook proto.Option[bool] `protobuf:"varint,10,opt"`
SrcTerm *uint32 `protobuf:"varint,7,opt,name=srcTerm" json:"srcTerm,omitempty"` Retry proto.Option[uint32] `protobuf:"varint,11,opt"`
PlatformType *uint32 `protobuf:"varint,8,opt,name=platformType" json:"platformType,omitempty"` BuType proto.Option[uint32] `protobuf:"varint,12,opt"`
InnerIp *uint32 `protobuf:"varint,9,opt,name=innerIp" json:"innerIp,omitempty"` PicOriginal proto.Option[bool] `protobuf:"varint,13,opt"`
AddressBook *bool `protobuf:"varint,10,opt,name=addressBook" json:"addressBook,omitempty"` PicWidth proto.Option[uint32] `protobuf:"varint,14,opt"`
Retry *uint32 `protobuf:"varint,11,opt,name=retry" json:"retry,omitempty"` PicHeight proto.Option[uint32] `protobuf:"varint,15,opt"`
BuType *uint32 `protobuf:"varint,12,opt,name=buType" json:"buType,omitempty"` PicType proto.Option[uint32] `protobuf:"varint,16,opt"`
PicOriginal *bool `protobuf:"varint,13,opt,name=picOriginal" json:"picOriginal,omitempty"` BuildVer []byte `protobuf:"bytes,17,opt"`
PicWidth *uint32 `protobuf:"varint,14,opt,name=picWidth" json:"picWidth,omitempty"` FileIndex []byte `protobuf:"bytes,18,opt"`
PicHeight *uint32 `protobuf:"varint,15,opt,name=picHeight" json:"picHeight,omitempty"` StoreDays proto.Option[uint32] `protobuf:"varint,19,opt"`
PicType *uint32 `protobuf:"varint,16,opt,name=picType" json:"picType,omitempty"` TryupStepflag proto.Option[uint32] `protobuf:"varint,20,opt"`
BuildVer []byte `protobuf:"bytes,17,opt,name=buildVer" json:"buildVer,omitempty"` RejectTryfast proto.Option[bool] `protobuf:"varint,21,opt"`
FileIndex []byte `protobuf:"bytes,18,opt,name=fileIndex" json:"fileIndex,omitempty"` SrvUpload proto.Option[uint32] `protobuf:"varint,22,opt"`
StoreDays *uint32 `protobuf:"varint,19,opt,name=storeDays" json:"storeDays,omitempty"` TransferUrl []byte `protobuf:"bytes,23,opt"`
TryupStepflag *uint32 `protobuf:"varint,20,opt,name=tryupStepflag" json:"tryupStepflag,omitempty"`
RejectTryfast *bool `protobuf:"varint,21,opt,name=rejectTryfast" json:"rejectTryfast,omitempty"`
SrvUpload *uint32 `protobuf:"varint,22,opt,name=srvUpload" json:"srvUpload,omitempty"`
TransferUrl []byte `protobuf:"bytes,23,opt,name=transferUrl" json:"transferUrl,omitempty"`
}
func (x *D352TryUpImgReq) Reset() {
*x = D352TryUpImgReq{}
if protoimpl.UnsafeEnabled {
mi := &file_cmd0x352_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *D352TryUpImgReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*D352TryUpImgReq) ProtoMessage() {}
func (x *D352TryUpImgReq) ProtoReflect() protoreflect.Message {
mi := &file_cmd0x352_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use D352TryUpImgReq.ProtoReflect.Descriptor instead.
func (*D352TryUpImgReq) Descriptor() ([]byte, []int) {
return file_cmd0x352_proto_rawDescGZIP(), []int{2}
}
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 {
state protoimpl.MessageState FileId proto.Option[uint64] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache ClientIp proto.Option[uint32] `protobuf:"varint,2,opt"`
unknownFields protoimpl.UnknownFields Result proto.Option[uint32] `protobuf:"varint,3,opt"`
FailMsg []byte `protobuf:"bytes,4,opt"`
FileId *uint64 `protobuf:"varint,1,opt,name=fileId" json:"fileId,omitempty"` FileExit proto.Option[bool] `protobuf:"varint,5,opt"`
ClientIp *uint32 `protobuf:"varint,2,opt,name=clientIp" json:"clientIp,omitempty"`
Result *uint32 `protobuf:"varint,3,opt,name=result" json:"result,omitempty"`
FailMsg []byte `protobuf:"bytes,4,opt,name=failMsg" json:"failMsg,omitempty"`
FileExit *bool `protobuf:"varint,5,opt,name=fileExit" json:"fileExit,omitempty"`
// optional ImgInfo imgInfo = 6; // optional ImgInfo imgInfo = 6;
UpIp []uint32 `protobuf:"varint,7,rep,name=upIp" json:"upIp,omitempty"` UpIp []uint32 `protobuf:"varint,7,rep"`
UpPort []uint32 `protobuf:"varint,8,rep,name=upPort" json:"upPort,omitempty"` UpPort []uint32 `protobuf:"varint,8,rep"`
UpUkey []byte `protobuf:"bytes,9,opt,name=upUkey" json:"upUkey,omitempty"` UpUkey []byte `protobuf:"bytes,9,opt"`
UpResid []byte `protobuf:"bytes,10,opt,name=upResid" json:"upResid,omitempty"` UpResid []byte `protobuf:"bytes,10,opt"`
UpUuid []byte `protobuf:"bytes,11,opt,name=upUuid" json:"upUuid,omitempty"` UpUuid []byte `protobuf:"bytes,11,opt"`
UpOffset *uint64 `protobuf:"varint,12,opt,name=upOffset" json:"upOffset,omitempty"` UpOffset proto.Option[uint64] `protobuf:"varint,12,opt"`
BlockSize *uint64 `protobuf:"varint,13,opt,name=blockSize" json:"blockSize,omitempty"` BlockSize proto.Option[uint64] `protobuf:"varint,13,opt"`
EncryptDstip []byte `protobuf:"bytes,14,opt,name=encryptDstip" json:"encryptDstip,omitempty"` EncryptDstip []byte `protobuf:"bytes,14,opt"`
Roamdays *uint32 `protobuf:"varint,15,opt,name=roamdays" json:"roamdays,omitempty"` Roamdays proto.Option[uint32] `protobuf:"varint,15,opt"`
// repeated IPv6Info upIp6 = 26; // repeated IPv6Info upIp6 = 26;
ClientIp6 []byte `protobuf:"bytes,27,opt,name=clientIp6" json:"clientIp6,omitempty"` ClientIp6 []byte `protobuf:"bytes,27,opt"`
ThumbDownPara []byte `protobuf:"bytes,60,opt,name=thumbDownPara" json:"thumbDownPara,omitempty"` ThumbDownPara []byte `protobuf:"bytes,60,opt"`
OriginalDownPara []byte `protobuf:"bytes,61,opt,name=originalDownPara" json:"originalDownPara,omitempty"` OriginalDownPara []byte `protobuf:"bytes,61,opt"`
DownDomain []byte `protobuf:"bytes,62,opt,name=downDomain" json:"downDomain,omitempty"` DownDomain []byte `protobuf:"bytes,62,opt"`
BigDownPara []byte `protobuf:"bytes,64,opt,name=bigDownPara" json:"bigDownPara,omitempty"` BigDownPara []byte `protobuf:"bytes,64,opt"`
BigThumbDownPara []byte `protobuf:"bytes,65,opt,name=bigThumbDownPara" json:"bigThumbDownPara,omitempty"` BigThumbDownPara []byte `protobuf:"bytes,65,opt"`
HttpsUrlFlag *uint32 `protobuf:"varint,66,opt,name=httpsUrlFlag" json:"httpsUrlFlag,omitempty"` // optional TryUpInfo4Busi info4Busi = 1001; HttpsUrlFlag proto.Option[uint32] `protobuf:"varint,66,opt"` // optional TryUpInfo4Busi info4Busi = 1001;
}
func (x *TryUpImgRsp) Reset() {
*x = TryUpImgRsp{}
if protoimpl.UnsafeEnabled {
mi := &file_cmd0x352_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TryUpImgRsp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TryUpImgRsp) ProtoMessage() {}
func (x *TryUpImgRsp) ProtoReflect() protoreflect.Message {
mi := &file_cmd0x352_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TryUpImgRsp.ProtoReflect.Descriptor instead.
func (*TryUpImgRsp) Descriptor() ([]byte, []int) {
return file_cmd0x352_proto_rawDescGZIP(), []int{3}
}
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
}
var File_cmd0x352_proto protoreflect.FileDescriptor
var file_cmd0x352_proto_rawDesc = []byte{
0x0a, 0x0e, 0x63, 0x6d, 0x64, 0x30, 0x78, 0x33, 0x35, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x22, 0x6f, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x73,
0x75, 0x62, 0x63, 0x6d, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x75, 0x62,
0x63, 0x6d, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x74, 0x72, 0x79, 0x75, 0x70, 0x49, 0x6d, 0x67, 0x52,
0x65, 0x71, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x44, 0x33, 0x35, 0x32, 0x54,
0x72, 0x79, 0x55, 0x70, 0x49, 0x6d, 0x67, 0x52, 0x65, 0x71, 0x52, 0x0b, 0x74, 0x72, 0x79, 0x75,
0x70, 0x49, 0x6d, 0x67, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x54, 0x79,
0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x54, 0x79, 0x70,
0x65, 0x22, 0x8b, 0x01, 0x0a, 0x07, 0x52, 0x73, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x16, 0x0a,
0x06, 0x73, 0x75, 0x62, 0x63, 0x6d, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x73,
0x75, 0x62, 0x63, 0x6d, 0x64, 0x12, 0x2e, 0x0a, 0x0b, 0x74, 0x72, 0x79, 0x75, 0x70, 0x49, 0x6d,
0x67, 0x52, 0x73, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x54, 0x72, 0x79,
0x55, 0x70, 0x49, 0x6d, 0x67, 0x52, 0x73, 0x70, 0x52, 0x0b, 0x74, 0x72, 0x79, 0x75, 0x70, 0x49,
0x6d, 0x67, 0x52, 0x73, 0x70, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x65, 0x77, 0x42, 0x69, 0x67, 0x63,
0x68, 0x61, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x42, 0x69,
0x67, 0x63, 0x68, 0x61, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x61, 0x69, 0x6c, 0x4d, 0x73, 0x67,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x66, 0x61, 0x69, 0x6c, 0x4d, 0x73, 0x67, 0x22,
0xad, 0x05, 0x0a, 0x0f, 0x44, 0x33, 0x35, 0x32, 0x54, 0x72, 0x79, 0x55, 0x70, 0x49, 0x6d, 0x67,
0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x72, 0x63, 0x55, 0x69, 0x6e, 0x18, 0x01, 0x20,
0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x72, 0x63, 0x55, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x64,
0x73, 0x74, 0x55, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x64, 0x73, 0x74,
0x55, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x18, 0x03, 0x20,
0x01, 0x28, 0x04, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x66,
0x69, 0x6c, 0x65, 0x4d, 0x64, 0x35, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x66, 0x69,
0x6c, 0x65, 0x4d, 0x64, 0x35, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a,
0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x73, 0x72, 0x63, 0x54, 0x65, 0x72, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
0x73, 0x72, 0x63, 0x54, 0x65, 0x72, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x6c, 0x61, 0x74, 0x66,
0x6f, 0x72, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70,
0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x69,
0x6e, 0x6e, 0x65, 0x72, 0x49, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x69, 0x6e,
0x6e, 0x65, 0x72, 0x49, 0x70, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x42, 0x6f, 0x6f, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x42, 0x6f, 0x6f, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x74, 0x72, 0x79,
0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x72, 0x65, 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a,
0x06, 0x62, 0x75, 0x54, 0x79, 0x70, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x62,
0x75, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x69, 0x63, 0x4f, 0x72, 0x69, 0x67,
0x69, 0x6e, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x63, 0x4f,
0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x69, 0x63, 0x57, 0x69,
0x64, 0x74, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x69, 0x63, 0x57, 0x69,
0x64, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x69, 0x63, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74,
0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x69, 0x63, 0x48, 0x65, 0x69, 0x67, 0x68,
0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x18, 0x10, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x07, 0x70, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x62,
0x75, 0x69, 0x6c, 0x64, 0x56, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x62,
0x75, 0x69, 0x6c, 0x64, 0x56, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x49,
0x6e, 0x64, 0x65, 0x78, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65,
0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x61,
0x79, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44,
0x61, 0x79, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x79, 0x75, 0x70, 0x53, 0x74, 0x65, 0x70,
0x66, 0x6c, 0x61, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x72, 0x79, 0x75,
0x70, 0x53, 0x74, 0x65, 0x70, 0x66, 0x6c, 0x61, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x6a,
0x65, 0x63, 0x74, 0x54, 0x72, 0x79, 0x66, 0x61, 0x73, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x72, 0x79, 0x66, 0x61, 0x73, 0x74, 0x12,
0x1c, 0x0a, 0x09, 0x73, 0x72, 0x76, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x16, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x09, 0x73, 0x72, 0x76, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x20, 0x0a,
0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x18, 0x17, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x22,
0x81, 0x05, 0x0a, 0x0b, 0x54, 0x72, 0x79, 0x55, 0x70, 0x49, 0x6d, 0x67, 0x52, 0x73, 0x70, 0x12,
0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
0x06, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x49, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x49, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x66,
0x61, 0x69, 0x6c, 0x4d, 0x73, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x66, 0x61,
0x69, 0x6c, 0x4d, 0x73, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x69,
0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x69,
0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x70, 0x49, 0x70, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52,
0x04, 0x75, 0x70, 0x49, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x18,
0x08, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x75, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x16, 0x0a,
0x06, 0x75, 0x70, 0x55, 0x6b, 0x65, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x75,
0x70, 0x55, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x70, 0x52, 0x65, 0x73, 0x69, 0x64,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x75, 0x70, 0x52, 0x65, 0x73, 0x69, 0x64, 0x12,
0x16, 0x0a, 0x06, 0x75, 0x70, 0x55, 0x75, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x06, 0x75, 0x70, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x70, 0x4f, 0x66, 0x66,
0x73, 0x65, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x75, 0x70, 0x4f, 0x66, 0x66,
0x73, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a, 0x65,
0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x53, 0x69, 0x7a,
0x65, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x44, 0x73, 0x74, 0x69,
0x70, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74,
0x44, 0x73, 0x74, 0x69, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x61, 0x6d, 0x64, 0x61, 0x79,
0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x6f, 0x61, 0x6d, 0x64, 0x61, 0x79,
0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x36, 0x18, 0x1b,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x36, 0x12,
0x24, 0x0a, 0x0d, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x44, 0x6f, 0x77, 0x6e, 0x50, 0x61, 0x72, 0x61,
0x18, 0x3c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x44, 0x6f, 0x77,
0x6e, 0x50, 0x61, 0x72, 0x61, 0x12, 0x2a, 0x0a, 0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
0x6c, 0x44, 0x6f, 0x77, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x77, 0x6e, 0x50, 0x61, 0x72,
0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x6f, 0x77, 0x6e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
0x3e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x64, 0x6f, 0x77, 0x6e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x69, 0x67, 0x44, 0x6f, 0x77, 0x6e, 0x50, 0x61, 0x72, 0x61,
0x18, 0x40, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x69, 0x67, 0x44, 0x6f, 0x77, 0x6e, 0x50,
0x61, 0x72, 0x61, 0x12, 0x2a, 0x0a, 0x10, 0x62, 0x69, 0x67, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x44,
0x6f, 0x77, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x18, 0x41, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x62,
0x69, 0x67, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x44, 0x6f, 0x77, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x12,
0x22, 0x0a, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x55, 0x72, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x18,
0x42, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x55, 0x72, 0x6c, 0x46,
0x6c, 0x61, 0x67, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x63, 0x6d, 0x64, 0x30, 0x78, 0x33,
0x35, 0x32,
}
var (
file_cmd0x352_proto_rawDescOnce sync.Once
file_cmd0x352_proto_rawDescData = file_cmd0x352_proto_rawDesc
)
func file_cmd0x352_proto_rawDescGZIP() []byte {
file_cmd0x352_proto_rawDescOnce.Do(func() {
file_cmd0x352_proto_rawDescData = protoimpl.X.CompressGZIP(file_cmd0x352_proto_rawDescData)
})
return file_cmd0x352_proto_rawDescData
}
var file_cmd0x352_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_cmd0x352_proto_goTypes = []interface{}{
(*ReqBody)(nil), // 0: ReqBody
(*RspBody)(nil), // 1: RspBody
(*D352TryUpImgReq)(nil), // 2: D352TryUpImgReq
(*TryUpImgRsp)(nil), // 3: TryUpImgRsp
}
var file_cmd0x352_proto_depIdxs = []int32{
2, // 0: ReqBody.tryupImgReq:type_name -> D352TryUpImgReq
3, // 1: RspBody.tryupImgRsp:type_name -> TryUpImgRsp
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_cmd0x352_proto_init() }
func file_cmd0x352_proto_init() {
if File_cmd0x352_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_cmd0x352_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReqBody); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_cmd0x352_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RspBody); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_cmd0x352_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*D352TryUpImgReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_cmd0x352_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TryUpImgRsp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_cmd0x352_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_cmd0x352_proto_goTypes,
DependencyIndexes: file_cmd0x352_proto_depIdxs,
MessageInfos: file_cmd0x352_proto_msgTypes,
}.Build()
File_cmd0x352_proto = out.File
file_cmd0x352_proto_rawDesc = nil
file_cmd0x352_proto_goTypes = nil
file_cmd0x352_proto_depIdxs = nil
} }

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;

File diff suppressed because it is too large Load Diff

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;

File diff suppressed because it is too large Load Diff

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,589 +1,48 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-golite. DO NOT EDIT.
// versions: // source: pb/cmd0x6ff/subcmd0x501.proto
// protoc-gen-go v1.26.0
// protoc v3.17.1
// source: subcmd0x501.proto
package cmd0x6ff package cmd0x6ff
import ( import (
reflect "reflect" proto "github.com/RomiChan/protobuf/proto"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
type C501ReqBody struct { type C501ReqBody struct {
state protoimpl.MessageState ReqBody *SubCmd0X501ReqBody `protobuf:"bytes,1281,opt"`
sizeCache protoimpl.SizeCache _ [0]func()
unknownFields protoimpl.UnknownFields
ReqBody *SubCmd0X501ReqBody `protobuf:"bytes,1281,opt,name=ReqBody" json:"ReqBody,omitempty"`
}
func (x *C501ReqBody) Reset() {
*x = C501ReqBody{}
if protoimpl.UnsafeEnabled {
mi := &file_subcmd0x501_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *C501ReqBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*C501ReqBody) ProtoMessage() {}
func (x *C501ReqBody) ProtoReflect() protoreflect.Message {
mi := &file_subcmd0x501_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use C501ReqBody.ProtoReflect.Descriptor instead.
func (*C501ReqBody) Descriptor() ([]byte, []int) {
return file_subcmd0x501_proto_rawDescGZIP(), []int{0}
}
func (x *C501ReqBody) GetReqBody() *SubCmd0X501ReqBody {
if x != nil {
return x.ReqBody
}
return nil
} }
type C501RspBody struct { type C501RspBody struct {
state protoimpl.MessageState RspBody *SubCmd0X501RspBody `protobuf:"bytes,1281,opt"`
sizeCache protoimpl.SizeCache _ [0]func()
unknownFields protoimpl.UnknownFields
RspBody *SubCmd0X501RspBody `protobuf:"bytes,1281,opt,name=RspBody" json:"RspBody,omitempty"`
}
func (x *C501RspBody) Reset() {
*x = C501RspBody{}
if protoimpl.UnsafeEnabled {
mi := &file_subcmd0x501_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *C501RspBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*C501RspBody) ProtoMessage() {}
func (x *C501RspBody) ProtoReflect() protoreflect.Message {
mi := &file_subcmd0x501_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use C501RspBody.ProtoReflect.Descriptor instead.
func (*C501RspBody) Descriptor() ([]byte, []int) {
return file_subcmd0x501_proto_rawDescGZIP(), []int{1}
}
func (x *C501RspBody) GetRspBody() *SubCmd0X501RspBody {
if x != nil {
return x.RspBody
}
return nil
} }
type SubCmd0X501ReqBody struct { type SubCmd0X501ReqBody struct {
state protoimpl.MessageState Uin proto.Option[uint64] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache IdcId proto.Option[uint32] `protobuf:"varint,2,opt"`
unknownFields protoimpl.UnknownFields Appid proto.Option[uint32] `protobuf:"varint,3,opt"`
LoginSigType proto.Option[uint32] `protobuf:"varint,4,opt"`
Uin *uint64 `protobuf:"varint,1,opt,name=uin" json:"uin,omitempty"` LoginSigTicket []byte `protobuf:"bytes,5,opt"`
IdcId *uint32 `protobuf:"varint,2,opt,name=idcId" json:"idcId,omitempty"` RequestFlag proto.Option[uint32] `protobuf:"varint,6,opt"`
Appid *uint32 `protobuf:"varint,3,opt,name=appid" json:"appid,omitempty"` ServiceTypes []uint32 `protobuf:"varint,7,rep"`
LoginSigType *uint32 `protobuf:"varint,4,opt,name=loginSigType" json:"loginSigType,omitempty"` Bid proto.Option[uint32] `protobuf:"varint,8,opt"`
LoginSigTicket []byte `protobuf:"bytes,5,opt,name=loginSigTicket" json:"loginSigTicket,omitempty"`
RequestFlag *uint32 `protobuf:"varint,6,opt,name=requestFlag" json:"requestFlag,omitempty"`
ServiceTypes []uint32 `protobuf:"varint,7,rep,name=serviceTypes" json:"serviceTypes,omitempty"`
Bid *uint32 `protobuf:"varint,8,opt,name=bid" json:"bid,omitempty"`
}
func (x *SubCmd0X501ReqBody) Reset() {
*x = SubCmd0X501ReqBody{}
if protoimpl.UnsafeEnabled {
mi := &file_subcmd0x501_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SubCmd0X501ReqBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SubCmd0X501ReqBody) ProtoMessage() {}
func (x *SubCmd0X501ReqBody) ProtoReflect() protoreflect.Message {
mi := &file_subcmd0x501_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SubCmd0X501ReqBody.ProtoReflect.Descriptor instead.
func (*SubCmd0X501ReqBody) Descriptor() ([]byte, []int) {
return file_subcmd0x501_proto_rawDescGZIP(), []int{2}
}
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 {
state protoimpl.MessageState SigSession []byte `protobuf:"bytes,1,opt"`
sizeCache protoimpl.SizeCache SessionKey []byte `protobuf:"bytes,2,opt"`
unknownFields protoimpl.UnknownFields Addrs []*SrvAddrs `protobuf:"bytes,3,rep"`
SigSession []byte `protobuf:"bytes,1,opt,name=sigSession" json:"sigSession,omitempty"`
SessionKey []byte `protobuf:"bytes,2,opt,name=sessionKey" json:"sessionKey,omitempty"`
Addrs []*SrvAddrs `protobuf:"bytes,3,rep,name=addrs" json:"addrs,omitempty"`
}
func (x *SubCmd0X501RspBody) Reset() {
*x = SubCmd0X501RspBody{}
if protoimpl.UnsafeEnabled {
mi := &file_subcmd0x501_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SubCmd0X501RspBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SubCmd0X501RspBody) ProtoMessage() {}
func (x *SubCmd0X501RspBody) ProtoReflect() protoreflect.Message {
mi := &file_subcmd0x501_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SubCmd0X501RspBody.ProtoReflect.Descriptor instead.
func (*SubCmd0X501RspBody) Descriptor() ([]byte, []int) {
return file_subcmd0x501_proto_rawDescGZIP(), []int{3}
}
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 {
state protoimpl.MessageState ServiceType proto.Option[uint32] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache Addrs []*IpAddr `protobuf:"bytes,2,rep"`
unknownFields protoimpl.UnknownFields
ServiceType *uint32 `protobuf:"varint,1,opt,name=serviceType" json:"serviceType,omitempty"`
Addrs []*IpAddr `protobuf:"bytes,2,rep,name=addrs" json:"addrs,omitempty"`
}
func (x *SrvAddrs) Reset() {
*x = SrvAddrs{}
if protoimpl.UnsafeEnabled {
mi := &file_subcmd0x501_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SrvAddrs) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SrvAddrs) ProtoMessage() {}
func (x *SrvAddrs) ProtoReflect() protoreflect.Message {
mi := &file_subcmd0x501_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SrvAddrs.ProtoReflect.Descriptor instead.
func (*SrvAddrs) Descriptor() ([]byte, []int) {
return file_subcmd0x501_proto_rawDescGZIP(), []int{4}
}
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 {
state protoimpl.MessageState Type proto.Option[uint32] `protobuf:"varint,1,opt"`
sizeCache protoimpl.SizeCache Ip proto.Option[uint32] `protobuf:"fixed32,2,opt"`
unknownFields protoimpl.UnknownFields Port proto.Option[uint32] `protobuf:"varint,3,opt"`
Area proto.Option[uint32] `protobuf:"varint,4,opt"`
Type *uint32 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` _ [0]func()
Ip *uint32 `protobuf:"fixed32,2,opt,name=ip" json:"ip,omitempty"`
Port *uint32 `protobuf:"varint,3,opt,name=port" json:"port,omitempty"`
Area *uint32 `protobuf:"varint,4,opt,name=area" json:"area,omitempty"`
}
func (x *IpAddr) Reset() {
*x = IpAddr{}
if protoimpl.UnsafeEnabled {
mi := &file_subcmd0x501_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *IpAddr) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*IpAddr) ProtoMessage() {}
func (x *IpAddr) ProtoReflect() protoreflect.Message {
mi := &file_subcmd0x501_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use IpAddr.ProtoReflect.Descriptor instead.
func (*IpAddr) Descriptor() ([]byte, []int) {
return file_subcmd0x501_proto_rawDescGZIP(), []int{5}
}
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
}
var File_subcmd0x501_proto protoreflect.FileDescriptor
var file_subcmd0x501_proto_rawDesc = []byte{
0x0a, 0x11, 0x73, 0x75, 0x62, 0x63, 0x6d, 0x64, 0x30, 0x78, 0x35, 0x30, 0x31, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x22, 0x3d, 0x0a, 0x0b, 0x43, 0x35, 0x30, 0x31, 0x52, 0x65, 0x71, 0x42, 0x6f,
0x64, 0x79, 0x12, 0x2e, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x42, 0x6f, 0x64, 0x79, 0x18, 0x81, 0x0a,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x53, 0x75, 0x62, 0x43, 0x6d, 0x64, 0x30, 0x78, 0x35,
0x30, 0x31, 0x52, 0x65, 0x71, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x07, 0x52, 0x65, 0x71, 0x42, 0x6f,
0x64, 0x79, 0x22, 0x3d, 0x0a, 0x0b, 0x43, 0x35, 0x30, 0x31, 0x52, 0x73, 0x70, 0x42, 0x6f, 0x64,
0x79, 0x12, 0x2e, 0x0a, 0x07, 0x52, 0x73, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x18, 0x81, 0x0a, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x53, 0x75, 0x62, 0x43, 0x6d, 0x64, 0x30, 0x78, 0x35, 0x30,
0x31, 0x52, 0x73, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x07, 0x52, 0x73, 0x70, 0x42, 0x6f, 0x64,
0x79, 0x22, 0xf6, 0x01, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x43, 0x6d, 0x64, 0x30, 0x78, 0x35, 0x30,
0x31, 0x52, 0x65, 0x71, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x6e, 0x18,
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x75, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x64,
0x63, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x64, 0x63, 0x49, 0x64,
0x12, 0x14, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x05, 0x61, 0x70, 0x70, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x53,
0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6c, 0x6f,
0x67, 0x69, 0x6e, 0x53, 0x69, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x6c, 0x6f,
0x67, 0x69, 0x6e, 0x53, 0x69, 0x67, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0e, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x53, 0x69, 0x67, 0x54, 0x69, 0x63, 0x6b,
0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x46, 0x6c, 0x61,
0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x46, 0x6c, 0x61, 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54,
0x79, 0x70, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x69, 0x64, 0x18,
0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x62, 0x69, 0x64, 0x22, 0x75, 0x0a, 0x12, 0x53, 0x75,
0x62, 0x43, 0x6d, 0x64, 0x30, 0x78, 0x35, 0x30, 0x31, 0x52, 0x73, 0x70, 0x42, 0x6f, 0x64, 0x79,
0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79,
0x12, 0x1f, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x09, 0x2e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x73, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72,
0x73, 0x22, 0x4b, 0x0a, 0x08, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x20, 0x0a,
0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12,
0x1d, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x07,
0x2e, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x22, 0x54,
0x0a, 0x06, 0x49, 0x70, 0x41, 0x64, 0x64, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02,
0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x07, 0x52, 0x02, 0x69, 0x70, 0x12, 0x12, 0x0a, 0x04,
0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74,
0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x65, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04,
0x61, 0x72, 0x65, 0x61, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x63, 0x6d, 0x64, 0x30, 0x78,
0x36, 0x66, 0x66,
}
var (
file_subcmd0x501_proto_rawDescOnce sync.Once
file_subcmd0x501_proto_rawDescData = file_subcmd0x501_proto_rawDesc
)
func file_subcmd0x501_proto_rawDescGZIP() []byte {
file_subcmd0x501_proto_rawDescOnce.Do(func() {
file_subcmd0x501_proto_rawDescData = protoimpl.X.CompressGZIP(file_subcmd0x501_proto_rawDescData)
})
return file_subcmd0x501_proto_rawDescData
}
var file_subcmd0x501_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_subcmd0x501_proto_goTypes = []interface{}{
(*C501ReqBody)(nil), // 0: C501ReqBody
(*C501RspBody)(nil), // 1: C501RspBody
(*SubCmd0X501ReqBody)(nil), // 2: SubCmd0x501ReqBody
(*SubCmd0X501RspBody)(nil), // 3: SubCmd0x501RspBody
(*SrvAddrs)(nil), // 4: SrvAddrs
(*IpAddr)(nil), // 5: IpAddr
}
var file_subcmd0x501_proto_depIdxs = []int32{
2, // 0: C501ReqBody.ReqBody:type_name -> SubCmd0x501ReqBody
3, // 1: C501RspBody.RspBody:type_name -> SubCmd0x501RspBody
4, // 2: SubCmd0x501RspBody.addrs:type_name -> SrvAddrs
5, // 3: SrvAddrs.addrs:type_name -> IpAddr
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_subcmd0x501_proto_init() }
func file_subcmd0x501_proto_init() {
if File_subcmd0x501_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_subcmd0x501_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*C501ReqBody); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_subcmd0x501_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*C501RspBody); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_subcmd0x501_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SubCmd0X501ReqBody); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_subcmd0x501_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SubCmd0X501RspBody); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_subcmd0x501_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SrvAddrs); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_subcmd0x501_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*IpAddr); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_subcmd0x501_proto_rawDesc,
NumEnums: 0,
NumMessages: 6,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_subcmd0x501_proto_goTypes,
DependencyIndexes: file_subcmd0x501_proto_depIdxs,
MessageInfos: file_subcmd0x501_proto_msgTypes,
}.Build()
File_subcmd0x501_proto = out.File
file_subcmd0x501_proto_rawDesc = nil
file_subcmd0x501_proto_goTypes = nil
file_subcmd0x501_proto_depIdxs = nil
} }

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;

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