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

246 Commits

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

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

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

* 修复:security#1

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

* Update tlv_decoders.go

* fix: 我是憨憨

* fix: 我是憨憨

---------

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

* Update group_info.go

* Update client.go

* Update builders.go

---------

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

* -- add
 - sync upstream change

* -- ci
 - sync upstream change

* Delete sync.yml

---------

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

* ci: update to go1.19

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

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

* group sign

* rename pb file 0xeb7

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

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

* refactor: avoid importing new dependent package

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

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

* struct: ProfileDetailUpdate

* ProfileDetailUpdate

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

* fix: pretty
2022-02-20 18:57:08 +08:00
bd6f8947ef client: minor update 2022-02-18 18:13:55 +08:00
0578942d86 Revert "fix reply seq"
This reverts commit 6692c4acb8.
2022-02-18 14:57:47 +08:00
0e68a1e7b7 client: merge fragmented private message 2022-02-14 16:27:17 +08:00
d53bf8503e client/pb: re-generate proto files 2022-02-14 00:51:36 +08:00
8a518ee96c dep: drop github.com/klauspost/compress 2022-02-13 17:08:11 +08:00
6692c4acb8 fix reply seq 2022-02-12 16:16:37 +08:00
28637c41be internal/proto: move binary.DynamicProtoMessage to proto.DynamicMessage 2022-02-10 21:56:40 +08:00
5d071b034c fix event flow panic 2022-02-09 17:25:29 +08:00
8258176fba Add MarketFaceElement case to message.ToString() (#241) 2022-02-07 19:51:33 +08:00
a8788e826f fix ToGroupUin, ToGroupCode 2022-01-22 01:29:55 +08:00
231 changed files with 9977 additions and 40424 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:
push:
branches: [ master ]
branches: [ master, typeparam ]
pull_request:
branches: [ master ]
branches: [ master, typeparam ]
jobs:
@ -16,7 +16,7 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.13
go-version: '1.20'
- name: Check out code into the Go module directory
uses: actions/checkout@v2

View File

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

52
Makefile Normal file
View File

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

View File

@ -1,22 +1,34 @@
# MiraiGo
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] 语音
@ -30,7 +42,7 @@ qq-android协议的golang实现 移植于mirai
- [x] 合并转发
- [x] 群文件(上传与接收信息)
#### 事件
**事件**
- [x] 好友消息
- [x] 群消息
- [x] 临时会话消息
@ -47,8 +59,9 @@ qq-android协议的golang实现 移植于mirai
- [x] 客户端离线
- [x] 群提示 (戳一戳/运气王等)
#### 主动操作
> 为防止滥用,将不支持主动邀请新成员进群
**主动操作**
_为防止滥用不支持主动邀请新成员进群_
- [x] 发送群消息
- [x] 发送好友消息
@ -72,11 +85,14 @@ qq-android协议的golang实现 移植于mirai
- [x] 戳一戳群友
- [x] 获取陌生人信息
#### 敏感操作
> 由于[QQ钱包支付用户服务协议](https://www.tenpay.com/v2/html5/basic/public/agreement/protocol_mqq_pay.shtml), 将不支持一切有关QQ钱包的协议
</details>
### 不支持的协议
**基于 [QQ钱包支付用户服务协议](https://www.tenpay.com/v2/html5/basic/public/agreement/protocol_mqq_pay.shtml) 不支持一切有关QQ钱包的协议**
>4.13 您不得利用本服务实施下列任一的行为:
>\
> 9 **侵害QQ钱包支付服务系統**
- [ ] ~~QQ钱包协议(收款/付款等)~~

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -4,27 +4,28 @@ import (
"crypto/md5"
"fmt"
"math/rand"
"net"
"net/netip"
"sort"
"strconv"
"sync"
"sync/atomic"
"time"
"github.com/pkg/errors"
"go.uber.org/atomic"
"github.com/RomiChan/syncx"
"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/message"
"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 {
Uin int64
PasswordMd5 [16]byte
@ -33,7 +34,8 @@ type QQClient struct {
once sync.Once
// option
AllowSlider bool
AllowSlider bool
UseFragmentMessage bool
// account info
Online atomic.Bool
@ -49,21 +51,20 @@ type QQClient struct {
// protocol public field
SequenceId atomic.Int32
SessionId []byte
RandomKey []byte
TCP *network.TCPListener // todo: combine other protocol state into one struct
TCP *network.TCPClient // todo: combine other protocol state into one struct
ConnectTime time.Time
transport *network.Transport
oicq *oicq.Codec
logger Logger
// internal state
handlers HandlerMap
waiters sync.Map
servers []*net.TCPAddr
handlers syncx.Map[uint16, *handlerInfo]
waiters syncx.Map[string, func(any, error)]
initServerOnce sync.Once
servers []netip.AddrPort
currServerIndex int
retryTimes int
version *auth.AppVersion
deviceInfo *auth.Device
alive bool
// session info
@ -74,22 +75,52 @@ type QQClient struct {
// timeDiff int64
// address
otherSrvAddrs []string
fileStorageInfo *jce.FileStoragePushFSSvcList
// otherSrvAddrs []string
// 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
msgSvcCache *utils.Cache
msgSvcCache *utils.Cache[unit]
lastC2CMsgTime int64
transCache *utils.Cache
transCache *utils.Cache[unit]
groupSysMsgCache *GroupSystemMessages
groupMsgBuilders sync.Map
onlinePushCache *utils.Cache
msgBuilders syncx.Map[int32, *messageBuilder]
onlinePushCache *utils.Cache[unit]
heartbeatEnabled bool
requestPacketRequestID atomic.Int32
groupSeq atomic.Int32
friendSeq atomic.Int32
highwayApplyUpSeq atomic.Int32
eventHandlers *eventHandlers
groupListLock sync.Mutex
}
@ -104,7 +135,7 @@ type QiDianAccountInfo struct {
}
type handlerInfo struct {
fun func(i interface{}, err error)
fun func(i any, err error)
dynamic bool
params network.RequestParams
}
@ -116,7 +147,7 @@ func (h *handlerInfo) getParams() network.RequestParams {
return h.params
}
var decoders = map[string]func(*QQClient, *network.IncomingPacketInfo, []byte) (interface{}, error){
var decoders = map[string]func(*QQClient, *network.Packet) (any, error){
"wtlogin.login": decodeLoginResponse,
"wtlogin.exchange_emp": decodeExchangeEmpResponse,
"wtlogin.trans_emp": decodeTransEmpResponse,
@ -143,10 +174,6 @@ var decoders = map[string]func(*QQClient, *network.IncomingPacketInfo, []byte) (
"SummaryCard.ReqSummaryCard": decodeSummaryCardResponse,
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
// NewClient create new qq client
func NewClient(uin int64, password string) *QQClient {
return NewClientMd5(uin, md5.Sum([]byte(password)))
@ -161,30 +188,21 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
Uin: uin,
PasswordMd5: passwordMd5,
AllowSlider: true,
TCP: &network.TCPListener{},
TCP: &network.TCPClient{},
sig: &auth.SigInfo{
OutPacketSessionID: []byte{0x02, 0xB0, 0x5B, 0x8B},
},
eventHandlers: &eventHandlers{},
msgSvcCache: utils.NewCache(time.Second * 15),
transCache: utils.NewCache(time.Second * 15),
onlinePushCache: utils.NewCache(time.Second * 15),
servers: []*net.TCPAddr{},
msgSvcCache: utils.NewCache[unit](time.Second * 15),
transCache: utils.NewCache[unit](time.Second * 15),
onlinePushCache: utils.NewCache[unit](time.Second * 15),
alive: true,
highwaySession: new(highway.Session),
version: new(auth.AppVersion),
deviceInfo: new(auth.Device),
}
cli.transport = &network.Transport{
Sig: cli.sig,
Version: cli.version,
Device: cli.deviceInfo,
}
cli.transport = &network.Transport{Sig: cli.sig}
cli.oicq = oicq.NewCodec(cli.Uin)
{ // init atomic values
cli.SequenceId.Store(0x3635)
cli.SequenceId.Store(int32(rand.Intn(100000)))
cli.requestPacketRequestID.Store(1921334513)
cli.groupSeq.Store(int32(rand.Intn(20000)))
cli.friendSeq.Store(22911)
@ -192,63 +210,23 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
}
cli.highwaySession.Uin = strconv.FormatInt(cli.Uin, 10)
cli.GuildService = &GuildService{c: cli}
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.UnexpectedDisconnect(cli.unexpectedDisconnect)
rand.Read(cli.RandomKey)
return cli
}
func (c *QQClient) version() *auth.AppVersion {
return c.transport.Version
}
func (c *QQClient) Device() *DeviceInfo {
return c.transport.Device
}
func (c *QQClient) UseDevice(info *auth.Device) {
*c.version = *info.Protocol.Version()
*c.deviceInfo = *info
c.highwaySession.AppID = int32(c.version.AppId)
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))
}
@ -300,9 +278,9 @@ func (c *QQClient) TokenLogin(token []byte) error {
c.oicq.WtSessionTicketKey = r.ReadBytesShort()
c.sig.OutPacketSessionID = r.ReadBytesShort()
// SystemDeviceInfo.TgtgtKey = r.ReadBytesShort()
c.deviceInfo.TgtgtKey = r.ReadBytesShort()
c.Device().TgtgtKey = r.ReadBytesShort()
}
_, err = c.sendAndWait(c.buildRequestChangeSigPacket(c.version.MainSigMap))
_, err = c.sendAndWait(c.buildRequestChangeSigPacket(true))
if err != nil {
return err
}
@ -393,7 +371,7 @@ func (c *QQClient) SubmitSMS(code string) (*LoginResponse, error) {
func (c *QQClient) RequestSMS() bool {
rsp, err := c.sendAndWait(c.buildSMSRequestPacket())
if err != nil {
c.Error("request sms error: %v", err)
c.error("request sms error: %v", err)
return false
}
return rsp.(LoginResponse).Error == SMSNeededError
@ -401,18 +379,18 @@ func (c *QQClient) RequestSMS() bool {
func (c *QQClient) init(tokenLogin bool) error {
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 {
return errors.Wrap(err, "register error")
}
if tokenLogin {
notify := make(chan struct{})
d := c.waitPacket("StatSvc.ReqMSFOffline", func(i interface{}, err error) {
notify := make(chan struct{}, 2)
d := c.waitPacket("StatSvc.ReqMSFOffline", func(i any, err error) {
notify <- struct{}{}
})
d2 := c.waitPacket("MessageSvc.PushForceOffline", func(i interface{}, err error) {
d2 := c.waitPacket("MessageSvc.PushForceOffline", func(i any, err error) {
notify <- struct{}{}
})
select {
@ -430,7 +408,7 @@ func (c *QQClient) init(tokenLogin bool) error {
go c.doHeartbeat()
}
_ = c.RefreshStatus()
if c.version.Protocol == auth.QiDian {
if c.version().Protocol == auth.QiDian {
_, _ = c.sendAndWait(c.buildLoginExtraPacket()) // 小登录
_, _ = c.sendAndWait(c.buildConnKeyRequestPacket()) // big data key 如果等待 config push 的话时间来不及
}
@ -451,7 +429,7 @@ func (c *QQClient) GenToken() []byte {
w.WriteBytesShort(c.sig.EncryptedA1)
w.WriteBytesShort(c.oicq.WtSessionTicketKey)
w.WriteBytesShort(c.sig.OutPacketSessionID)
w.WriteBytesShort(c.deviceInfo.TgtgtKey)
w.WriteBytesShort(c.Device().TgtgtKey)
})
}
@ -500,7 +478,7 @@ func (c *QQClient) ReloadFriendList() error {
// 当使用普通QQ时: 请求好友列表
// 当使用企点QQ时: 请求外部联系人列表
func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
if c.version.Protocol == auth.QiDian {
if c.version().Protocol == auth.QiDian {
rsp, err := c.getQiDianAddressDetailList()
if err != nil {
return nil, err
@ -549,6 +527,7 @@ func (c *QQClient) GetGroupList() ([]*GroupInfo, error) {
if err != nil {
return nil, err
}
interner := intern.NewStringInterner()
r := rsp.([]*GroupInfo)
wg := sync.WaitGroup{}
batch := 50
@ -561,11 +540,12 @@ func (c *QQClient) GetGroupList() ([]*GroupInfo, error) {
for j := i; j < k; j++ {
go func(g *GroupInfo, wg *sync.WaitGroup) {
defer wg.Done()
m, err := c.GetGroupMembers(g)
m, err := c.getGroupMembers(g, interner)
if err != nil {
return
}
g.Members = m
g.Name = interner.Intern(g.Name)
}(r[j], &wg)
}
wg.Wait()
@ -574,6 +554,11 @@ func (c *QQClient) GetGroupList() ([]*GroupInfo, 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 list []*GroupMemberInfo
for {
@ -582,7 +567,7 @@ func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error)
return nil, err
}
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)
nextUin = rsp.NextUin
@ -591,6 +576,9 @@ func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]*GroupMemberInfo, error)
if m.Uin == group.OwnerUin {
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...)
if nextUin == 0 {
@ -611,6 +599,12 @@ func (c *QQClient) GetMemberInfo(groupCode, memberUin int64) (*GroupMemberInfo,
}
func (c *QQClient) FindFriend(uin int64) *FriendInfo {
if uin == c.Uin {
return &FriendInfo{
Uin: uin,
Nickname: c.Nickname,
}
}
for _, t := range c.FriendList {
f := t
if f.Uin == uin {
@ -647,7 +641,7 @@ func (c *QQClient) FindGroup(code int64) *GroupInfo {
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 {
block = false
reason = ""
@ -676,7 +670,7 @@ func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) {
func (c *QQClient) getSKey() string {
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())
}
return string(c.sig.SKey)
@ -720,10 +714,6 @@ func (c *QQClient) updateGroupName(groupCode int64, newName string) {
_, _ = 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) {
_, _ = c.sendAndWait(c.buildGroupMuteAllPacket(groupCode, mute))
}
@ -736,8 +726,8 @@ func (c *QQClient) quitGroup(groupCode int64) {
_, _ = c.sendAndWait(c.buildQuitGroupPacket(groupCode))
}
func (c *QQClient) kickGroupMember(groupCode, memberUin int64, msg string, block bool) {
_, _ = c.sendAndWait(c.buildGroupKickPacket(groupCode, memberUin, msg, block))
func (c *QQClient) KickGroupMembers(groupCode int64, msg string, block bool, memberUins ...int64) {
_, _ = c.sendAndWait(c.buildGroupKickPacket(groupCode, msg, block, memberUins...))
}
func (g *GroupInfo) removeMember(uin int64) {
@ -752,7 +742,16 @@ func (g *GroupInfo) removeMember(uin int64) {
})
}
func (c *QQClient) SetCustomServer(servers []*net.TCPAddr) {
func (c *QQClient) setGroupAnonymous(groupCode int64, enable bool) {
_, _ = c.sendAndWait(c.buildSetGroupAnonymous(groupCode, enable))
}
// UpdateProfile 修改个人资料
func (c *QQClient) UpdateProfile(profile ProfileDetailUpdate) {
_, _ = c.sendAndWait(c.buildUpdateProfileDetailPacket(profile))
}
func (c *QQClient) SetCustomServer(servers []netip.AddrPort) {
c.servers = append(servers, c.servers...)
}
@ -765,7 +764,12 @@ func (c *QQClient) registerClient() error {
}
func (c *QQClient) nextSeq() uint16 {
return uint16(c.SequenceId.Add(1) & 0x7FFF)
seq := c.SequenceId.Add(1)
if seq > 1000000 {
seq = int32(rand.Intn(100000)) + 60000
c.SequenceId.Store(seq)
}
return uint16(seq)
}
func (c *QQClient) nextPacketSeq() int32 {

View File

@ -2,9 +2,8 @@ package client
import (
"crypto/md5"
"encoding/hex"
"fmt"
"net"
"net/netip"
"strconv"
"strings"
"sync"
@ -14,7 +13,9 @@ import (
"github.com/Mrs4s/MiraiGo/binary"
"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/cmd0x352"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff"
@ -32,29 +33,36 @@ var (
)
// wtlogin.login
func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
reader := binary.NewReader(payload)
func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
reader := binary.NewReader(pkt.Payload)
reader.ReadUInt16() // sub command
t := reader.ReadByte()
reader.ReadUInt16()
m := reader.ReadTlvMap(2)
m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(reader.ReadAvailable())
if err != nil {
return nil, err
}
if m.Exists(0x402) {
c.sig.Dpwd = []byte(utils.RandomString(16))
c.sig.T402 = m[0x402]
h := md5.Sum(append(append(c.deviceInfo.Guid, c.sig.Dpwd...), c.sig.T402...))
h := md5.Sum(append(append(c.Device().Guid, c.sig.Dpwd...), c.sig.T402...))
c.sig.G = h[:]
}
if m.Exists(0x546) {
c.sig.T547 = auth.CalcPow(m[0x546])
}
// c.logger.Info("login response %v", t)
if t == 0 { // login success
// if t150, ok := m[0x150]; ok {
// c.t150 = t150
// c.t150 = t150
// }
// if t161, ok := m[0x161]; ok {
// c.decodeT161(t161)
// }
if t161, ok := m[0x161]; ok {
c.decodeT161(t161)
}
if m.Exists(0x403) {
c.sig.RandSeed = m[0x403]
}
c.decodeT119(m[0x119], c.deviceInfo.TgtgtKey)
c.decodeT119(m[0x119], c.Device().TgtgtKey)
return LoginResponse{
Success: true,
}, nil
@ -64,6 +72,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
if m.Exists(0x192) {
return LoginResponse{
Success: false,
Code: t,
VerifyUrl: string(m[0x192]),
Error: SliderNeededError,
}, nil
@ -75,6 +84,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
sign := imgData.ReadBytes(int(signLen))
return LoginResponse{
Success: false,
Code: t,
Error: NeedCaptcha,
CaptchaImage: imgData.ReadAvailable(),
CaptchaSign: sign,
@ -82,6 +92,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
} else {
return LoginResponse{
Success: false,
Code: t,
Error: UnknownLoginError,
}, nil
}
@ -90,6 +101,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
if t == 40 {
return LoginResponse{
Success: false,
Code: t,
ErrorMessage: "账号被冻结",
Error: UnknownLoginError,
}, nil
@ -102,11 +114,13 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
c.sig.RandSeed = m[0x403]
phone := func() string {
r := binary.NewReader(m[0x178])
return r.ReadStringLimit(int(r.ReadInt32()))
r.ReadStringShort()
return r.ReadStringShort()
}()
if t204, ok := m[0x204]; ok { // 同时支持扫码验证 ?
return LoginResponse{
Success: false,
Code: t,
Error: SMSOrVerifyNeededError,
VerifyUrl: string(t204),
SMSPhone: phone,
@ -115,6 +129,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
}
return LoginResponse{
Success: false,
Code: t,
Error: SMSNeededError,
SMSPhone: phone,
ErrorMessage: string(m[0x17e]),
@ -125,6 +140,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
c.sig.T104 = m[0x104]
return LoginResponse{
Success: false,
Code: t,
Error: SMSNeededError,
}, nil
}
@ -132,6 +148,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
if t204, ok := m[0x204]; ok { // 扫码验证
return LoginResponse{
Success: false,
Code: t,
Error: UnsafeDeviceError,
VerifyUrl: string(t204),
ErrorMessage: "",
@ -141,6 +158,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
if t == 162 {
return LoginResponse{
Code: t,
Error: TooManySMSRequestError,
}, nil
}
@ -157,6 +175,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
t149r.ReadStringShort() // title
return LoginResponse{
Success: false,
Code: t,
Error: OtherLoginError,
ErrorMessage: t149r.ReadStringShort(),
}, nil
@ -168,28 +187,29 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b
t146r.ReadStringShort() // title
return LoginResponse{
Success: false,
Code: t,
Error: OtherLoginError,
ErrorMessage: t146r.ReadStringShort(),
}, nil
}
c.Debug("unknown login response: %v", t)
c.debug("unknown login response: %v", t)
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) // ?
}
// StatSvc.register
func decodeClientRegisterResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeClientRegisterResponse(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
svcRsp := &jce.SvcRespRegister{}
svcRsp.ReadFrom(jce.NewJceReader(data.Map["SvcRespRegister"]["QQService.SvcRespRegister"][1:]))
if svcRsp.Result != "" || svcRsp.ReplyCode != 0 {
if svcRsp.Result != "" {
c.Error("reg error: %v", svcRsp.Result)
c.error("reg error: %v", svcRsp.Result)
}
return nil, errors.New("reg failed")
}
@ -197,12 +217,15 @@ func decodeClientRegisterResponse(c *QQClient, _ *network.IncomingPacketInfo, pa
}
// wtlogin.exchange_emp
func decodeExchangeEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
reader := binary.NewReader(payload)
func decodeExchangeEmpResponse(c *QQClient, pkt *network.Packet) (any, error) {
reader := binary.NewReader(pkt.Payload)
cmd := reader.ReadUInt16()
t := reader.ReadByte()
reader.ReadUInt16()
m := reader.ReadTlvMap(2)
m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(reader.ReadAvailable())
if err != nil {
return nil, err
}
if t != 0 {
return nil, errors.Errorf("exchange_emp failed: %v", t)
}
@ -217,11 +240,11 @@ func decodeExchangeEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, paylo
}
// wtlogin.trans_emp
func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
if len(payload) < 48 {
func decodeTransEmpResponse(c *QQClient, pkt *network.Packet) (any, error) {
if len(pkt.Payload) < 48 {
return nil, errors.New("missing payload length")
}
reader := binary.NewReader(payload)
reader := binary.NewReader(pkt.Payload)
reader.ReadBytes(5) // trans req head
reader.ReadByte()
reader.ReadUInt16()
@ -242,7 +265,10 @@ func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload
}
sig := body.ReadBytesShort()
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) {
return &QRCodeLoginResponse{
State: QRCodeImageFetch,
@ -285,11 +311,14 @@ func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload
c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10)
body.ReadInt32() // sig create time
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) {
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{
tmpPwd: m[0x18],
tmpNoPicSig: m[0x19],
@ -300,9 +329,9 @@ func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload
}
// ConfigPushSvc.PushReq
func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["PushReq"]["ConfigPush.PushReq"][1:])
@ -314,16 +343,16 @@ func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []b
ssoPkt := jce.NewJceReader(jceBuf)
servers := ssoPkt.ReadSsoServerInfos(1)
if len(servers) > 0 {
var adds []*net.TCPAddr
var adds []netip.AddrPort
for _, s := range servers {
if strings.Contains(s.Server, "com") {
continue
}
c.Debug("got new server addr: %v location: %v", s.Server, s.Location)
adds = append(adds, &net.TCPAddr{
IP: net.ParseIP(s.Server),
Port: int(s.Port),
})
c.debug("got new server addr: %v location: %v", s.Server, s.Location)
addr, err := netip.ParseAddr(s.Server)
if err == nil {
adds = append(adds, netip.AddrPortFrom(addr, uint16(s.Port)))
}
}
f := true
for _, e := range c.eventHandlers.serverUpdatedHandlers {
@ -342,48 +371,51 @@ func decodePushReqPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []b
fmtPkt := jce.NewJceReader(jceBuf)
list := &jce.FileStoragePushFSSvcList{}
list.ReadFrom(fmtPkt)
c.Debug("got file storage svc push.")
c.fileStorageInfo = list
c.debug("got file storage svc push.")
// c.fileStorageInfo = list
rsp := cmd0x6ff.C501RspBody{}
if err := proto.Unmarshal(list.BigDataChannel.PbBuf, &rsp); err == nil && rsp.RspBody != nil {
c.highwaySession.SigSession = rsp.RspBody.SigSession
c.highwaySession.SessionKey = rsp.RspBody.SessionKey
for _, srv := range rsp.RspBody.Addrs {
if srv.GetServiceType() == 10 {
if srv.ServiceType.Unwrap() == 10 {
for _, addr := range srv.Addrs {
c.highwaySession.AppendAddr(addr.GetIp(), addr.GetPort())
c.highwaySession.AppendAddr(addr.Ip.Unwrap(), addr.Port.Unwrap())
}
}
if srv.GetServiceType() == 21 {
for _, addr := range srv.Addrs {
c.otherSrvAddrs = append(c.otherSrvAddrs, fmt.Sprintf("%v:%v", binary.UInt32ToIPV4Address(addr.GetIp()), addr.GetPort()))
/*
if srv.ServiceType.Unwrap() == 21 {
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)
_, pkt := c.buildConfPushRespPacket(t, seq, jceBuf)
return nil, c.sendPacket(pkt)
_, resp := c.buildConfPushRespPacket(t, seq, jceBuf)
return nil, c.sendPacket(resp)
}
// MessageSvc.PbGetMsg
func decodeMessageSvcPacket(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeMessageSvcPacket(c *QQClient, pkt *network.Packet) (any, error) {
rsp := msg.GetMessageResponse{}
err := proto.Unmarshal(payload, &rsp)
err := proto.Unmarshal(pkt.Payload, &rsp)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
c.c2cMessageSyncProcessor(&rsp, info)
c.c2cMessageSyncProcessor(&rsp, pkt.Params)
return nil, nil
}
// MessageSvc.PushNotify
func decodeSvcNotify(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeSvcNotify(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload[4:]))
request.ReadFrom(jce.NewJceReader(pkt.Payload[4:]))
data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
if len(data.Map) == 0 {
@ -408,9 +440,9 @@ func decodeSvcNotify(c *QQClient, _ *network.IncomingPacketInfo, payload []byte)
}
// SummaryCard.ReqSummaryCard
func decodeSummaryCardResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeSummaryCardResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
rsp := func() *jce.JceReader {
@ -420,16 +452,34 @@ func decodeSummaryCardResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo
return jce.NewJceReader(data.Map["RespSummaryCard"]["SummaryCard_Old.RespSummaryCard"][1:])
}()
info := &SummaryCardInfo{
Sex: rsp.ReadByte(1),
Age: rsp.ReadByte(2),
Nickname: rsp.ReadString(3),
Level: rsp.ReadInt32(5),
City: rsp.ReadString(7),
Sign: rsp.ReadString(8),
Mobile: rsp.ReadString(11),
Uin: rsp.ReadInt64(23),
LoginDays: rsp.ReadInt64(36),
Sex: rsp.ReadByte(1),
Age: rsp.ReadByte(2),
Nickname: rsp.ReadString(3),
Level: rsp.ReadInt32(5),
City: rsp.ReadString(7),
Sign: rsp.ReadString(8),
Mobile: rsp.ReadString(11),
Uin: rsp.ReadInt64(23),
}
vipInfo := rsp.ReadMapIntVipInfo(29) // 1 -> vip, 3 -> svip
if v1, v3 := vipInfo[1], vipInfo[3]; v1 != nil || v3 != nil {
if v1.Open != 0 {
info.VipLevel = fmt.Sprintf("vip%d", v1.Level)
}
if v3.Open != 0 {
info.VipLevel = fmt.Sprintf("svip%d", v3.Level)
}
}
richSign := rsp.ReadBytes(32)
records, _ := tlv.NewDecoder(1, 1).Decode(richSign)
for _, r := range records {
if r.Tag == 3 {
info.Sign = string(r.Value)
}
}
info.LoginDays = rsp.ReadInt64(36)
services := rsp.ReadByteArrArr(46)
readService := func(buf []byte) (*profilecard.BusiComm, []byte) {
r := binary.NewReader(buf)
@ -444,11 +494,11 @@ func decodeSummaryCardResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo
}
for _, buf := range services {
comm, payload := readService(buf)
if comm.GetService() == 16 {
if comm.Service.Unwrap() == 16 {
rsp := profilecard.GateVaProfileGateRsp{}
_ = proto.Unmarshal(payload, &rsp)
if rsp.QidInfo != nil {
info.Qid = rsp.QidInfo.GetQid()
info.Qid = rsp.QidInfo.Qid.Unwrap()
}
}
}
@ -456,9 +506,9 @@ func decodeSummaryCardResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo
}
// friendlist.getFriendGroupList
func decodeFriendGroupListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeFriendGroupListResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion3{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["FLRESP"][1:])
@ -481,9 +531,9 @@ func decodeFriendGroupListResponse(_ *QQClient, _ *network.IncomingPacketInfo, p
}
// friendlist.delFriend
func decodeFriendDeleteResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeFriendDeleteResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion3{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["DFRESP"][1:])
@ -494,9 +544,9 @@ func decodeFriendDeleteResponse(_ *QQClient, _ *network.IncomingPacketInfo, payl
}
// friendlist.GetTroopListReqV2
func decodeGroupListResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeGroupListResponse(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion3{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["GetTroopListRespV2"][1:])
@ -508,7 +558,6 @@ func decodeGroupListResponse(c *QQClient, _ *network.IncomingPacketInfo, payload
Uin: g.GroupUin,
Code: g.GroupCode,
Name: g.GroupName,
Memo: g.GroupMemo,
OwnerUin: g.GroupOwnerUin,
MemberCount: uint16(g.MemberNum),
MaxMemberCount: uint16(g.MaxGroupMemberNum),
@ -526,9 +575,9 @@ func decodeGroupListResponse(c *QQClient, _ *network.IncomingPacketInfo, payload
}
// friendlist.GetTroopMemberListReq
func decodeGroupMemberListResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeGroupMemberListResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion3{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["GTMLRESP"][1:])
@ -536,23 +585,21 @@ func decodeGroupMemberListResponse(_ *QQClient, _ *network.IncomingPacketInfo, p
next := r.ReadInt64(4)
l := make([]*GroupMemberInfo, 0, len(members))
for _, m := range members {
permission := Member
if m.Flag&1 != 0 {
permission = Administrator
}
l = append(l, &GroupMemberInfo{
Uin: m.MemberUin,
Nickname: m.Nick,
Gender: m.Gender,
CardName: m.Name,
Level: uint16(m.MemberLevel),
JoinTime: m.JoinTime,
LastSpeakTime: m.LastSpeakTime,
SpecialTitle: m.SpecialTitle,
SpecialTitleExpireTime: m.SpecialTitleExpireTime,
ShutUpTimestamp: m.ShutUpTimestap,
Permission: func() MemberPermission {
if m.Flag == 1 {
return Administrator
}
return Member
}(),
Uin: m.MemberUin,
Nickname: m.Nick,
Gender: m.Gender,
CardName: m.Name,
Level: uint16(m.MemberLevel),
JoinTime: m.JoinTime,
LastSpeakTime: m.LastSpeakTime,
SpecialTitle: m.SpecialTitle,
ShutUpTimestamp: m.ShutUpTimestap,
Permission: permission,
})
}
return &groupMemberListResponse{
@ -562,105 +609,103 @@ func decodeGroupMemberListResponse(_ *QQClient, _ *network.IncomingPacketInfo, p
}
// group_member_card.get_group_member_card_info
func decodeGroupMemberInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeGroupMemberInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
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")
}
if rsp.MemInfo == nil || (rsp.MemInfo.Nick == nil && rsp.MemInfo.Age == 0) {
return nil, errors.WithStack(ErrMemberNotFound)
}
group := c.FindGroup(rsp.GroupCode)
permission := Member
if rsp.MemInfo.Uin == group.OwnerUin {
permission = Owner
}
if rsp.MemInfo.Role == 2 {
permission = Administrator
}
return &GroupMemberInfo{
Group: group,
Uin: rsp.MemInfo.Uin,
Gender: byte(rsp.MemInfo.Sex),
Nickname: string(rsp.MemInfo.Nick),
CardName: string(rsp.MemInfo.Card),
Level: uint16(rsp.MemInfo.Level),
JoinTime: rsp.MemInfo.Join,
LastSpeakTime: rsp.MemInfo.LastSpeak,
SpecialTitle: string(rsp.MemInfo.SpecialTitle),
SpecialTitleExpireTime: int64(rsp.MemInfo.SpecialTitleExpireTime),
Permission: func() MemberPermission {
if rsp.MemInfo.Uin == group.OwnerUin {
return Owner
}
if rsp.MemInfo.Role == 2 {
return Administrator
}
return Member
}(),
Group: group,
Uin: rsp.MemInfo.Uin,
Gender: byte(rsp.MemInfo.Sex),
Nickname: string(rsp.MemInfo.Nick),
CardName: string(rsp.MemInfo.Card),
Level: uint16(rsp.MemInfo.Level),
JoinTime: rsp.MemInfo.Join,
LastSpeakTime: rsp.MemInfo.LastSpeak,
SpecialTitle: string(rsp.MemInfo.SpecialTitle),
Permission: permission,
}, nil
}
// LongConn.OffPicUp
func decodeOffPicUpResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeOffPicUpResponse(_ *QQClient, pkt *network.Packet) (any, error) {
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")
}
if rsp.GetFailMsg() != nil {
if rsp.FailMsg != nil {
return &imageUploadResponse{
ResultCode: -1,
Message: string(rsp.FailMsg),
}, nil
}
if rsp.GetSubcmd() != 1 || len(rsp.GetTryupImgRsp()) == 0 {
if rsp.Subcmd.Unwrap() != 1 || len(rsp.TryupImgRsp) == 0 {
return &imageUploadResponse{
ResultCode: -2,
}, nil
}
imgRsp := rsp.GetTryupImgRsp()[0]
if imgRsp.GetResult() != 0 {
imgRsp := rsp.TryupImgRsp[0]
if imgRsp.Result.Unwrap() != 0 {
return &imageUploadResponse{
ResultCode: int32(*imgRsp.Result),
Message: string(imgRsp.GetFailMsg()),
ResultCode: int32(imgRsp.Result.Unwrap()),
Message: string(imgRsp.FailMsg),
}, nil
}
if imgRsp.GetFileExit() {
if imgRsp.FileExit.Unwrap() {
return &imageUploadResponse{
IsExists: true,
ResourceId: string(imgRsp.GetUpResid()),
ResourceId: string(imgRsp.UpResid),
}, nil
}
return &imageUploadResponse{
ResourceId: string(imgRsp.GetUpResid()),
UploadKey: imgRsp.GetUpUkey(),
UploadIp: imgRsp.GetUpIp(),
UploadPort: imgRsp.GetUpPort(),
ResourceId: string(imgRsp.UpResid),
UploadKey: imgRsp.UpUkey,
UploadIp: imgRsp.UpIp,
UploadPort: imgRsp.UpPort,
}, nil
}
// OnlinePush.PbPushTransMsg
func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeOnlinePushTransPacket(c *QQClient, pkt *network.Packet) (any, error) {
info := msg.TransMsgInfo{}
err := proto.Unmarshal(payload, &info)
err := proto.Unmarshal(pkt.Payload, &info)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
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 {
return nil, nil
}
c.transCache.Add(idStr, "", time.Second*15)
if info.GetMsgType() == 34 {
c.transCache.Add(idStr, unit{}, time.Second*15)
if info.MsgType.Unwrap() == 34 {
data.ReadInt32()
data.ReadByte()
target := int64(uint32(data.ReadInt32()))
typ := int32(data.ReadByte())
operator := int64(uint32(data.ReadInt32()))
if g := c.FindGroupByUin(info.GetFromUin()); g != nil {
if g := c.FindGroupByUin(info.FromUin.Unwrap()); g != nil {
groupLeaveLock.Lock()
defer groupLeaveLock.Unlock()
switch typ {
case 0x02:
if target == c.Uin {
c.dispatchLeaveGroupEvent(&GroupLeaveEvent{Group: g})
c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{Group: g})
} else if m := g.FindMember(target); m != nil {
g.removeMember(target)
c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{
c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{
Group: g,
Member: m,
})
@ -670,13 +715,13 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay
return nil, err
}
if target == c.Uin {
c.dispatchLeaveGroupEvent(&GroupLeaveEvent{
c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{
Group: g,
Operator: g.FindMember(operator),
})
} else if m := g.FindMember(target); m != nil {
g.removeMember(target)
c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{
c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{
Group: g,
Member: m,
Operator: g.FindMember(operator),
@ -685,7 +730,7 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay
case 0x82:
if m := g.FindMember(target); m != nil {
g.removeMember(target)
c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{
c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{
Group: g,
Member: m,
})
@ -693,16 +738,25 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay
case 0x83:
if m := g.FindMember(target); m != nil {
g.removeMember(target)
c.dispatchMemberLeaveEvent(&MemberLeaveGroupEvent{
c.GroupMemberLeaveEvent.dispatch(c, &MemberLeaveGroupEvent{
Group: g,
Member: m,
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)
var4 := int32(data.ReadByte())
var5 := int64(0)
@ -710,19 +764,17 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay
if var4 != 0 && var4 != 1 {
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 {
newPermission := func() MemberPermission {
if data.ReadByte() == 1 {
return Administrator
}
return Member
}()
newPermission := Member
if data.ReadByte() == 1 {
newPermission = Administrator
}
mem := g.FindMember(target)
if mem.Permission != newPermission {
old := mem.Permission
mem.Permission = newPermission
c.dispatchPermissionChanged(&MemberPermissionChangedEvent{
c.GroupMemberPermissionChangedEvent.dispatch(c, &MemberPermissionChangedEvent{
Group: g,
Member: mem,
OldPermission: old,
@ -736,9 +788,9 @@ func decodeOnlinePushTransPacket(c *QQClient, _ *network.IncomingPacketInfo, pay
}
// ProfileService.Pb.ReqSystemMsgNew.Friend
func decodeSystemMsgFriendPacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeSystemMsgFriendPacket(c *QQClient, pkt *network.Packet) (any, error) {
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")
}
if len(rsp.Friendmsgs) == 0 {
@ -746,7 +798,7 @@ func decodeSystemMsgFriendPacket(c *QQClient, _ *network.IncomingPacketInfo, pay
}
st := rsp.Friendmsgs[0]
if st.Msg != nil {
c.dispatchNewFriendRequest(&NewFriendRequest{
c.NewFriendRequestEvent.dispatch(c, &NewFriendRequest{
RequestId: st.MsgSeq,
Message: st.Msg.MsgAdditional,
RequesterUin: st.ReqUin,
@ -758,36 +810,33 @@ func decodeSystemMsgFriendPacket(c *QQClient, _ *network.IncomingPacketInfo, pay
}
// MessageSvc.PushForceOffline
func decodeForceOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeForceOfflinePacket(c *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
r := jce.NewJceReader(data.Map["req_PushForceOffline"]["PushNotifyPack.RequestPushForceOffline"][1:])
tips := r.ReadString(2)
c.Disconnect()
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: tips})
go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: tips})
return nil, nil
}
// StatSvc.ReqMSFOffline
func decodeMSFOfflinePacket(c *QQClient, _ *network.IncomingPacketInfo, _ []byte) (interface{}, error) {
func decodeMSFOfflinePacket(c *QQClient, _ *network.Packet) (any, error) {
// c.lastLostMsg = "服务器端强制下线."
c.Disconnect()
// 这个decoder不能消耗太多时间, event另起线程处理
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "服务端强制下线."})
go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "服务端强制下线."})
return nil, nil
}
// OidbSvc.0xd79
func decodeWordSegmentation(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := &oidb.D79RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
func decodeWordSegmentation(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D79RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
if rsp.Content != nil {
return rsp.Content.SliceContent, nil
@ -795,15 +844,17 @@ func decodeWordSegmentation(_ *QQClient, _ *network.IncomingPacketInfo, payload
return nil, errors.New("no word received")
}
func decodeSidExpiredPacket(c *QQClient, i *network.IncomingPacketInfo, _ []byte) (interface{}, error) {
_, err := c.sendAndWait(c.buildRequestChangeSigPacket(3554528))
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))
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
}
@ -815,8 +866,8 @@ func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if pkg.GetRetCode() != 0 {
return nil, errors.New(pkg.GetErrMsg())
if pkg.RetCode.Unwrap() != 0 {
return nil, errors.New(pkg.ErrMsg.Unwrap())
}
if err := proto.Unmarshal(pkg.BusiBuff, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
@ -825,6 +876,6 @@ func decodeAppInfoResponse(_ *QQClient, _ *incomingPacketInfo, payload []byte) (
}
*/
func ignoreDecoder(_ *QQClient, _ *network.IncomingPacketInfo, _ []byte) (interface{}, error) {
func ignoreDecoder(_ *QQClient, _ *network.Packet) (any, error) {
return nil, nil
}

View File

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

View File

@ -2,18 +2,62 @@ package client
import (
"fmt"
"reflect"
"runtime/debug"
"sync"
"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 {
privateMessageHandlers []func(*QQClient, *message.PrivateMessage)
tempMessageHandlers []func(*QQClient, *TempMessageEvent)
groupMessageHandlers []func(*QQClient, *message.GroupMessage)
selfPrivateMessageHandlers []func(*QQClient, *message.PrivateMessage)
selfGroupMessageHandlers []func(*QQClient, *message.GroupMessage)
// todo: move to event handle
guildChannelMessageHandlers []func(*QQClient, *message.GuildChannelMessage)
guildMessageReactionsUpdatedHandlers []func(*QQClient, *GuildMessageReactionsUpdatedEvent)
guildMessageRecalledHandlers []func(*QQClient, *GuildMessageRecalledEvent)
@ -21,58 +65,14 @@ type eventHandlers struct {
guildChannelCreatedHandlers []func(*QQClient, *GuildChannelOperationEvent)
guildChannelDestroyedHandlers []func(*QQClient, *GuildChannelOperationEvent)
memberJoinedGuildHandlers []func(*QQClient, *MemberJoinGuildEvent)
groupMuteEventHandlers []func(*QQClient, *GroupMuteEvent)
groupRecalledHandlers []func(*QQClient, *GroupMessageRecalledEvent)
friendRecalledHandlers []func(*QQClient, *FriendMessageRecalledEvent)
joinGroupHandlers []func(*QQClient, *GroupInfo)
leaveGroupHandlers []func(*QQClient, *GroupLeaveEvent)
memberJoinedHandlers []func(*QQClient, *MemberJoinGroupEvent)
memberLeavedHandlers []func(*QQClient, *MemberLeaveGroupEvent)
memberCardUpdatedHandlers []func(*QQClient, *MemberCardUpdatedEvent)
groupNameUpdatedHandlers []func(*QQClient, *GroupNameUpdatedEvent)
permissionChangedHandlers []func(*QQClient, *MemberPermissionChangedEvent)
groupInvitedHandlers []func(*QQClient, *GroupInvitedRequest)
joinRequestHandlers []func(*QQClient, *UserJoinGroupRequest)
friendRequestHandlers []func(*QQClient, *NewFriendRequest)
newFriendHandlers []func(*QQClient, *NewFriendEvent)
disconnectHandlers []func(*QQClient, *ClientDisconnectedEvent)
logHandlers []func(*QQClient, *LogEvent)
serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool
groupNotifyHandlers []func(*QQClient, INotifyEvent)
friendNotifyHandlers []func(*QQClient, INotifyEvent)
memberTitleUpdatedHandlers []func(*QQClient, *MemberSpecialTitleUpdatedEvent)
offlineFileHandlers []func(*QQClient, *OfflineFileEvent)
otherClientStatusChangedHandlers []func(*QQClient, *OtherClientStatusChangedEvent)
groupDigestHandlers []func(*QQClient, *GroupDigestEvent)
groupMessageReceiptHandlers sync.Map
serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool
subscribedEventHandlers []any
groupMessageReceiptHandlers sync.Map
}
func (c *QQClient) OnPrivateMessage(f func(*QQClient, *message.PrivateMessage)) {
c.eventHandlers.privateMessageHandlers = append(c.eventHandlers.privateMessageHandlers, f)
}
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 (c *QQClient) SubscribeEventHandler(handler any) {
c.eventHandlers.subscribedEventHandlers = append(c.eventHandlers.subscribedEventHandlers, handler)
}
func (s *GuildService) OnGuildChannelMessage(f func(*QQClient, *message.GuildChannelMessage)) {
@ -103,99 +103,10 @@ func (s *GuildService) OnMemberJoinedGuild(f func(*QQClient, *MemberJoinGuildEve
s.c.eventHandlers.memberJoinedGuildHandlers = append(s.c.eventHandlers.memberJoinedGuildHandlers, f)
}
func (c *QQClient) OnGroupMuted(f func(*QQClient, *GroupMuteEvent)) {
c.eventHandlers.groupMuteEventHandlers = append(c.eventHandlers.groupMuteEventHandlers, f)
}
func (c *QQClient) OnJoinGroup(f func(*QQClient, *GroupInfo)) {
c.eventHandlers.joinGroupHandlers = append(c.eventHandlers.joinGroupHandlers, f)
}
func (c *QQClient) OnLeaveGroup(f func(*QQClient, *GroupLeaveEvent)) {
c.eventHandlers.leaveGroupHandlers = append(c.eventHandlers.leaveGroupHandlers, f)
}
func (c *QQClient) OnGroupMemberJoined(f func(*QQClient, *MemberJoinGroupEvent)) {
c.eventHandlers.memberJoinedHandlers = append(c.eventHandlers.memberJoinedHandlers, f)
}
func (c *QQClient) OnGroupMemberLeaved(f func(*QQClient, *MemberLeaveGroupEvent)) {
c.eventHandlers.memberLeavedHandlers = append(c.eventHandlers.memberLeavedHandlers, f)
}
func (c *QQClient) OnGroupMemberCardUpdated(f func(*QQClient, *MemberCardUpdatedEvent)) {
c.eventHandlers.memberCardUpdatedHandlers = append(c.eventHandlers.memberCardUpdatedHandlers, f)
}
func (c *QQClient) OnGroupNameUpdated(f func(*QQClient, *GroupNameUpdatedEvent)) {
c.eventHandlers.groupNameUpdatedHandlers = append(c.eventHandlers.groupNameUpdatedHandlers, f)
}
func (c *QQClient) OnGroupMemberPermissionChanged(f func(*QQClient, *MemberPermissionChangedEvent)) {
c.eventHandlers.permissionChangedHandlers = append(c.eventHandlers.permissionChangedHandlers, f)
}
func (c *QQClient) OnGroupMessageRecalled(f func(*QQClient, *GroupMessageRecalledEvent)) {
c.eventHandlers.groupRecalledHandlers = append(c.eventHandlers.groupRecalledHandlers, f)
}
func (c *QQClient) OnFriendMessageRecalled(f func(*QQClient, *FriendMessageRecalledEvent)) {
c.eventHandlers.friendRecalledHandlers = append(c.eventHandlers.friendRecalledHandlers, f)
}
func (c *QQClient) OnGroupInvited(f func(*QQClient, *GroupInvitedRequest)) {
c.eventHandlers.groupInvitedHandlers = append(c.eventHandlers.groupInvitedHandlers, f)
}
func (c *QQClient) OnUserWantJoinGroup(f func(*QQClient, *UserJoinGroupRequest)) {
c.eventHandlers.joinRequestHandlers = append(c.eventHandlers.joinRequestHandlers, f)
}
func (c *QQClient) OnNewFriendRequest(f func(*QQClient, *NewFriendRequest)) {
c.eventHandlers.friendRequestHandlers = append(c.eventHandlers.friendRequestHandlers, f)
}
func (c *QQClient) OnNewFriendAdded(f func(*QQClient, *NewFriendEvent)) {
c.eventHandlers.newFriendHandlers = append(c.eventHandlers.newFriendHandlers, f)
}
func (c *QQClient) OnDisconnected(f func(*QQClient, *ClientDisconnectedEvent)) {
c.eventHandlers.disconnectHandlers = append(c.eventHandlers.disconnectHandlers, f)
}
func (c *QQClient) OnServerUpdated(f func(*QQClient, *ServerUpdatedEvent) bool) {
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 {
return func(msg *message.PrivateMessage) bool {
return msg.Sender.Uin == uin
@ -210,61 +121,6 @@ func (c *QQClient) onGroupMessageReceipt(id string, f ...func(*QQClient, *groupM
c.eventHandlers.groupMessageReceiptHandlers.LoadOrStore(id, f[0])
}
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) {
if msg == nil {
return
@ -342,255 +198,13 @@ func (c *QQClient) dispatchMemberJoinedGuildEvent(e *MemberJoinGuildEvent) {
}
}
func (c *QQClient) dispatchGroupMuteEvent(e *GroupMuteEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupMuteEventHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupMessageRecalledEvent(e *GroupMessageRecalledEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupRecalledHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchFriendMessageRecalledEvent(e *FriendMessageRecalledEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.friendRecalledHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchJoinGroupEvent(group *GroupInfo) {
if group == nil {
return
}
for _, f := range c.eventHandlers.joinGroupHandlers {
cover(func() {
f(c, group)
})
}
}
func (c *QQClient) dispatchLeaveGroupEvent(e *GroupLeaveEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.leaveGroupHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchNewMemberEvent(e *MemberJoinGroupEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.memberJoinedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchMemberLeaveEvent(e *MemberLeaveGroupEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.memberLeavedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchMemberCardUpdatedEvent(e *MemberCardUpdatedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.memberCardUpdatedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupNameUpdatedEvent(e *GroupNameUpdatedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.groupNameUpdatedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchPermissionChanged(e *MemberPermissionChangedEvent) {
if e == nil {
return
}
for _, f := range c.eventHandlers.permissionChangedHandlers {
cover(func() {
f(c, e)
})
}
}
func (c *QQClient) dispatchGroupMessageReceiptEvent(e *groupMessageReceiptEvent) {
c.eventHandlers.groupMessageReceiptHandlers.Range(func(_, f interface{}) bool {
c.eventHandlers.groupMessageReceiptHandlers.Range(func(_, f any) bool {
go f.(func(*QQClient, *groupMessageReceiptEvent))(c, e)
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()) {
defer func() {
if pan := recover(); pan != nil {

View File

@ -31,8 +31,8 @@ func (c *QQClient) buildFaceroamRequestPacket() (uint16, []byte) {
payload, _ := proto.Marshal(&faceroam.FaceroamReqBody{
Comm: &faceroam.PlatInfo{
Implat: proto.Int64(109),
Osver: proto.String(string(c.deviceInfo.Version.Release)),
Mqqver: &c.version.SortVersionName,
Osver: proto.String(string(c.Device().Version.Release)),
Mqqver: proto.Some(c.version().SortVersionName),
},
Uin: proto.Uint64(uint64(c.Uin)),
SubCmd: proto.Uint32(1),
@ -41,9 +41,9 @@ func (c *QQClient) buildFaceroamRequestPacket() (uint16, []byte) {
return c.uniPacket("Faceroam.OpReq", payload)
}
func decodeFaceroamResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeFaceroamResponse(c *QQClient, pkt *network.Packet) (any, error) {
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")
}
if rsp.RspUserInfo == nil {
@ -53,7 +53,7 @@ func decodeFaceroamResponse(c *QQClient, _ *network.IncomingPacketInfo, payload
for i := len(rsp.RspUserInfo.Filename) - 1; i >= 0; i-- {
res[len(rsp.RspUserInfo.Filename)-1-i] = &CustomFace{
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

View File

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

View File

@ -1,22 +1,17 @@
package client
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"math/rand"
"os"
"runtime/debug"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/internal/highway"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/pb/exciting"
"github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/utils"
"github.com/Mrs4s/MiraiGo/message"
)
type (
@ -55,13 +50,13 @@ type (
}
)
var fsWaiter = utils.NewUploadWaiter()
func init() {
decoders["OidbSvc.0x6d8_1"] = decodeOIDB6d81Response
decoders["OidbSvc.0x6d6_0"] = decodeOIDB6d60Response
decoders["OidbSvc.0x6d6_2"] = decodeOIDB6d62Response
decoders["OidbSvc.0x6d6_3"] = decodeOIDB6d63Response
decoders["OidbSvc.0x6d6_4"] = decodeOIDB6d64Response
decoders["OidbSvc.0x6d6_5"] = decodeOIDB6d65Response
decoders["OidbSvc.0x6d7_0"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d7_1"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d7_2"] = decodeOIDB6d7Response
@ -71,7 +66,7 @@ func init() {
func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err error) {
defer func() {
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")
}
}()
@ -84,8 +79,8 @@ func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err
return nil, e
}
fs = &GroupFileSystem{
FileCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.GetAllFileCount(),
LimitCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.GetLimitCount(),
FileCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.AllFileCount.Unwrap(),
LimitCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.LimitCount.Unwrap(),
GroupCode: groupCode,
client: c,
}
@ -93,8 +88,8 @@ func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err
if err != nil {
return nil, err
}
fs.TotalSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.GetTotalSpace()
fs.UsedSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.GetUsedSpace()
fs.TotalSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.TotalSpace.Unwrap()
fs.UsedSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.UsedSpace.Unwrap()
return fs, nil
}
@ -104,7 +99,7 @@ func (c *QQClient) GetGroupFileUrl(groupCode int64, fileId string, busId int32)
return ""
}
url := i.(string)
url += "?fname=" + hex.EncodeToString([]byte(fileId))
url += fmt.Sprintf("?fname=%x", fileId)
return url
}
@ -129,115 +124,54 @@ func (fs *GroupFileSystem) GetFilesByFolder(folderID string) ([]*GroupFile, []*G
if item.FileInfo != nil {
files = append(files, &GroupFile{
GroupCode: fs.GroupCode,
FileId: item.FileInfo.GetFileId(),
FileName: item.FileInfo.GetFileName(),
BusId: int32(item.FileInfo.GetBusId()),
FileSize: int64(item.FileInfo.GetFileSize()),
UploadTime: int64(item.FileInfo.GetUploadTime()),
DeadTime: int64(item.FileInfo.GetDeadTime()),
ModifyTime: int64(item.FileInfo.GetModifyTime()),
DownloadTimes: int64(item.FileInfo.GetDownloadTimes()),
Uploader: int64(item.FileInfo.GetUploaderUin()),
UploaderName: item.FileInfo.GetUploaderName(),
FileId: item.FileInfo.FileId.Unwrap(),
FileName: item.FileInfo.FileName.Unwrap(),
BusId: int32(item.FileInfo.BusId.Unwrap()),
FileSize: int64(item.FileInfo.FileSize.Unwrap()),
UploadTime: int64(item.FileInfo.UploadTime.Unwrap()),
DeadTime: int64(item.FileInfo.DeadTime.Unwrap()),
ModifyTime: int64(item.FileInfo.ModifyTime.Unwrap()),
DownloadTimes: int64(item.FileInfo.DownloadTimes.Unwrap()),
Uploader: int64(item.FileInfo.UploaderUin.Unwrap()),
UploaderName: item.FileInfo.UploaderName.Unwrap(),
})
}
if item.FolderInfo != nil {
folders = append(folders, &GroupFolder{
GroupCode: fs.GroupCode,
FolderId: item.FolderInfo.GetFolderId(),
FolderName: item.FolderInfo.GetFolderName(),
CreateTime: int64(item.FolderInfo.GetCreateTime()),
Creator: int64(item.FolderInfo.GetCreateUin()),
CreatorName: item.FolderInfo.GetCreatorName(),
TotalFileCount: item.FolderInfo.GetTotalFileCount(),
FolderId: item.FolderInfo.FolderId.Unwrap(),
FolderName: item.FolderInfo.FolderName.Unwrap(),
CreateTime: int64(item.FolderInfo.CreateTime.Unwrap()),
Creator: int64(item.FolderInfo.CreateUin.Unwrap()),
CreatorName: item.FolderInfo.CreatorName.Unwrap(),
TotalFileCount: item.FolderInfo.TotalFileCount.Unwrap(),
})
}
}
if rsp.FileListInfoRsp.GetIsEnd() {
if rsp.FileListInfoRsp.IsEnd.Unwrap() {
break
}
startIndex = rsp.FileListInfoRsp.GetNextIndex()
startIndex = rsp.FileListInfoRsp.NextIndex.Unwrap()
}
return files, folders, nil
}
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)
if err != nil {
return errors.Wrap(err, "open file error")
}
defer func() { _ = file.Close() }()
md5Hash, size := utils.ComputeMd5AndLength(file)
_, _ = file.Seek(0, io.SeekStart)
sha1H := sha1.New()
_, _ = io.Copy(sha1H, file)
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")
f := &LocalFile{
FileName: name,
Body: file,
RemoteFolder: folderId,
}
rsp := i.(*oidb.UploadFileRspBody)
if rsp.GetBoolFileExist() {
_, pkt := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.GetFileId(), rsp.GetBusId(), rand.Int31())
return fs.client.sendPacket(pkt)
target := message.Source{
SourceType: message.SourceGroup,
PrimaryID: fs.GroupCode,
}
if len(rsp.UploadIpLanV4) == 0 {
return errors.New("server requires unsupported ftn upload")
}
ext, _ := proto.Marshal(&exciting.GroupFileUploadExt{
Unknown1: proto.Int32(100),
Unknown2: proto.Int32(1),
Entry: &exciting.GroupFileUploadEntry{
BusiBuff: &exciting.ExcitingBusiInfo{
BusId: rsp.BusId,
SenderUin: &fs.client.Uin,
ReceiverUin: &fs.GroupCode,
GroupCode: &fs.GroupCode,
},
FileEntry: &exciting.ExcitingFileEntry{
FileSize: &size,
Md5: md5Hash,
Sha1: sha1Hash,
FileId: []byte(rsp.GetFileId()),
UploadKey: rsp.CheckKey,
},
ClientInfo: &exciting.ExcitingClientInfo{
ClientType: proto.Int32(2),
AppId: proto.String(fmt.Sprint(fs.client.version.AppId)),
TerminalType: proto.Int32(2),
ClientVer: proto.String("9e9c09dc"),
Unknown: proto.Int32(4),
},
FileNameInfo: &exciting.ExcitingFileNameInfo{FileName: &name},
Host: &exciting.ExcitingHostConfig{Hosts: []*exciting.ExcitingHostInfo{
{
Url: &exciting.ExcitingUrlInfo{
Unknown: proto.Int32(1),
Host: &rsp.UploadIpLanV4[0],
},
Port: rsp.UploadPort,
},
}},
},
Unknown3: proto.Int32(0),
})
client := fs.client
input := highway.ExcitingInput{
CommandID: 71,
Body: file,
Ticket: fs.client.highwaySession.SigSession,
Ext: ext,
}
if _, err = fs.client.highwaySession.UploadExciting(input); err != nil {
return errors.Wrap(err, "upload failed")
}
_, pkt := client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.GetFileId(), rsp.GetBusId(), rand.Int31())
return client.sendPacket(pkt)
return fs.client.UploadFile(target, f)
}
func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string {
@ -275,27 +209,41 @@ func (fs *GroupFileSystem) DeleteFile(parentFolderID, fileId string, busId int32
return i.(string)
}
func (c *QQClient) buildGroupFileUploadReqPacket(parentFolderID, fileName string, groupCode, fileSize int64, md5, sha1 []byte) (uint16, []byte) {
b, _ := proto.Marshal(&oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{
GroupCode: &groupCode,
// RenameFile 重命名群文件,需要管理权限或者是自己发的文件.
// 返回错误, 空为重命名成功
func (fs *GroupFileSystem) RenameFile(parentFolderID, fileId string, busId int32, newFileName string) string {
i, err := fs.client.sendAndWait(fs.client.buildGroupFileRenameReqPacket(fs.GroupCode, parentFolderID, fileId, busId, newFileName))
if err != nil {
return err.Error()
}
return i.(string)
}
// MoveFile 移动群文件,需要管理权限或者是自己发的文件.
// 返回错误, 空为移动成功
func (fs *GroupFileSystem) MoveFile(parentFolderID, fileId string, busId int32, DestFolderId string) string {
i, err := fs.client.sendAndWait(fs.client.buildGroupFileMoveReqPacket(fs.GroupCode, parentFolderID, fileId, busId, DestFolderId))
if err != nil {
return err.Error()
}
return i.(string)
}
func (c *QQClient) buildGroupFileUploadReqPacket(groupCode int64, file *LocalFile) (uint16, []byte) {
body := &oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{
GroupCode: proto.Some(groupCode),
AppId: proto.Int32(3),
BusId: proto.Int32(102),
Entrance: proto.Int32(5),
ParentFolderId: &parentFolderID,
FileName: &fileName,
LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + fileName),
Int64FileSize: &fileSize,
Sha: sha1,
Md5: md5,
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),
}})
req := &oidb.OIDBSSOPkg{
Command: 1750,
ServiceType: 0,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
}}
payload := c.packOIDBPackageProto(1750, 0, body)
return c.uniPacket("OidbSvc.0x6d6_0", payload)
}
@ -304,7 +252,7 @@ func (c *QQClient) buildGroupFileFeedsRequest(groupCode int64, fileID string, bu
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
FeedsInfoList: []*oidb.GroupFileFeedsInfo{{
FileId: &fileID,
FileId: proto.Some(fileID),
FeedFlag: proto.Uint32(1),
BusId: proto.Uint32(uint32(busId)),
MsgRandom: proto.Uint32(uint32(msgRand)),
@ -318,41 +266,29 @@ func (c *QQClient) buildGroupFileListRequestPacket(groupCode int64, folderID str
body := &oidb.D6D8ReqBody{FileListInfoReq: &oidb.GetFileListReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
FolderId: &folderID,
FolderId: proto.Some(folderID),
FileCount: proto.Uint32(20),
AllFileCount: proto.Uint32(0),
ReqFrom: proto.Uint32(3),
SortBy: proto.Uint32(1),
FilterCode: proto.Uint32(0),
Uin: proto.Uint64(0),
StartIndex: &startIndex,
StartIndex: proto.Some(startIndex),
Context: EmptyBytes,
}}
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 1752,
ServiceType: 1,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
payload := c.packOIDBPackageProto(1752, 1, body)
return c.uniPacket("OidbSvc.0x6d8_1", payload)
}
func (c *QQClient) buildGroupFileCountRequestPacket(groupCode int64) (uint16, []byte) {
body := &oidb.D6D8ReqBody{GroupFileCountReq: &oidb.GetFileCountReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
BusId: proto.Uint32(0),
}}
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 1752,
ServiceType: 2,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
body := &oidb.D6D8ReqBody{
GroupFileCountReq: &oidb.GetFileCountReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
BusId: proto.Uint32(0),
},
}
payload, _ := proto.Marshal(req)
payload := c.packOIDBPackageProto(1752, 2, body)
return c.uniPacket("OidbSvc.0x6d8_1", payload)
}
@ -361,14 +297,7 @@ func (c *QQClient) buildGroupFileSpaceRequestPacket(groupCode int64) (uint16, []
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
}}
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 1752,
ServiceType: 3,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
payload := c.packOIDBPackageProto(1752, 3, body)
return c.uniPacket("OidbSvc.0x6d8_1", payload)
}
@ -376,8 +305,8 @@ func (c *QQClient) buildGroupFileCreateFolderPacket(groupCode int64, parentFolde
payload := c.packOIDBPackageProto(1751, 0, &oidb.D6D7ReqBody{CreateFolderReq: &oidb.CreateFolderReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
ParentFolderId: &parentFolder,
FolderName: &name,
ParentFolderId: proto.Some(parentFolder),
FolderName: proto.Some(name),
}})
return c.uniPacket("OidbSvc.0x6d7_0", payload)
}
@ -405,115 +334,149 @@ func (c *QQClient) buildGroupFileDeleteFolderPacket(groupCode int64, folderId st
func (c *QQClient) buildGroupFileDownloadReqPacket(groupCode int64, fileId string, busId int32) (uint16, []byte) {
body := &oidb.D6D6ReqBody{
DownloadFileReq: &oidb.DownloadFileReqBody{
GroupCode: &groupCode,
GroupCode: proto.Some(groupCode),
AppId: proto.Int32(3),
BusId: &busId,
FileId: &fileId,
BusId: proto.Some(busId),
FileId: proto.Some(fileId),
},
}
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 1750,
ServiceType: 2,
Bodybuffer: b,
}
payload, _ := proto.Marshal(req)
payload := c.packOIDBPackageProto(1750, 2, body)
return c.uniPacket("OidbSvc.0x6d6_2", payload)
}
func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId, fileId string, busId int32) (uint16, []byte) {
body := &oidb.D6D6ReqBody{DeleteFileReq: &oidb.DeleteFileReqBody{
GroupCode: &groupCode,
GroupCode: proto.Some(groupCode),
AppId: proto.Int32(3),
BusId: &busId,
ParentFolderId: &parentFolderId,
FileId: &fileId,
BusId: proto.Some(busId),
ParentFolderId: proto.Some(parentFolderId),
FileId: proto.Some(fileId),
}}
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 1750,
ServiceType: 3,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
payload := c.packOIDBPackageProto(1750, 3, body)
return c.uniPacket("OidbSvc.0x6d6_3", payload)
}
func decodeOIDB6d81Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
func (c *QQClient) buildGroupFileRenameReqPacket(groupCode int64, parentFolderId string, fileId string, busId int32, newFileName string) (uint16, []byte) {
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{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
return &rsp, nil
}
// OidbSvc.0x6d6_2
func decodeOIDB6d62Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
// OidbSvc.0x6d6_2 GroupFileDownloadRespPacket
func decodeOIDB6d62Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
if rsp.DownloadFileRsp.DownloadUrl == nil {
return nil, errors.New(rsp.DownloadFileRsp.GetClientWording())
return nil, errors.New(rsp.DownloadFileRsp.ClientWording.Unwrap())
}
ip := rsp.DownloadFileRsp.GetDownloadIp()
ip := rsp.DownloadFileRsp.DownloadIp.Unwrap()
url := hex.EncodeToString(rsp.DownloadFileRsp.DownloadUrl)
return fmt.Sprintf("http://%s/ftn_handler/%s/", ip, url), nil
}
func decodeOIDB6d63Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
// GroupFileDeleteRespPacket
func decodeOIDB6d63Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
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.DeleteFileRsp == nil {
return "", nil
}
return rsp.DeleteFileRsp.GetClientWording(), nil
return rsp.DeleteFileRsp.ClientWording.Unwrap(), nil
}
func decodeOIDB6d60Response(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
// GroupFileUploadRespPacket
func decodeOIDB6d60Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
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")
u := rsp.UploadFileRsp
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, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
// GroupFileCreateFolderPacket, GroupFileDeleteFolderPacket, GroupFileRenameFolderPacket
func decodeOIDB6d7Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D7RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
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 createRsp := rsp.CreateFolderRsp; createRsp != nil {
if retCode := createRsp.RetCode.Unwrap(); retCode != 0 {
return nil, errors.Errorf("create folder error: %v", retCode)
}
}
if rsp.CreateFolderRsp != nil && rsp.CreateFolderRsp.GetRetCode() != 0 {
return nil, errors.Errorf("create folder error: %v", rsp.CreateFolderRsp.GetRetCode())
if renameRsp := rsp.RenameFolderRsp; renameRsp != nil {
if retCode := renameRsp.RetCode.Unwrap(); retCode != 0 {
return nil, errors.Errorf("rename folder error: %v", retCode)
}
}
if rsp.RenameFolderRsp != nil && rsp.RenameFolderRsp.GetRetCode() != 0 {
return nil, errors.Errorf("rename folder error: %v", rsp.CreateFolderRsp.GetRetCode())
}
if rsp.DeleteFolderRsp != nil && rsp.DeleteFolderRsp.GetRetCode() != 0 {
return nil, errors.Errorf("delete folder error: %v", rsp.CreateFolderRsp.GetRetCode())
if deleteRsp := rsp.DeleteFolderRsp; deleteRsp != nil {
if retCode := deleteRsp.RetCode.Unwrap(); retCode != 0 {
return nil, errors.Errorf("delete folder error: %v", retCode)
}
}
return nil, nil
}
// GroupFileRenameRespPacket
func decodeOIDB6d64Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
return rsp.RenameFileRsp.ClientWording.Unwrap(), nil
}
// GroupFileMoveRespPacket
func decodeOIDB6d65Response(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D6D6RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
return rsp.MoveFileRsp.ClientWording.Unwrap(), nil
}

View File

@ -27,7 +27,6 @@ type (
Uin int64
Code int64
Name string
Memo string
OwnerUin int64
GroupCreateTime uint32
GroupLevel uint32
@ -43,18 +42,17 @@ type (
}
GroupMemberInfo struct {
Group *GroupInfo
Uin int64
Gender byte
Nickname string
CardName string
Level uint16
JoinTime int64
LastSpeakTime int64
SpecialTitle string
SpecialTitleExpireTime int64
ShutUpTimestamp int64
Permission MemberPermission
Group *GroupInfo
Uin int64
Nickname string
CardName string
JoinTime int64
LastSpeakTime int64
SpecialTitle string
ShutUpTimestamp int64
Permission MemberPermission
Level uint16
Gender byte
}
// GroupSearchInfo 通过搜索得到的群信息
@ -81,7 +79,7 @@ func (c *QQClient) GetGroupInfo(groupCode int64) (*GroupInfo, error) {
// OidbSvc.0x88d_0
func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte) {
body := &oidb.D88DReqBody{
AppId: proto.Uint32(c.version.AppId),
AppId: proto.Uint32(c.version().AppId),
ReqGroupInfo: []*oidb.ReqGroupInfo{
{
GroupCode: proto.Uint64(uint64(groupCode)),
@ -116,12 +114,7 @@ func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte)
},
PcClientVersion: proto.Uint32(0),
}
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 2189,
Bodybuffer: b,
}
payload, _ := proto.Marshal(req)
payload := c.packOIDBPackageProto(2189, 0, body)
return c.uniPacket("OidbSvc.0x88d_0", payload)
}
@ -147,7 +140,7 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
search, _ := proto.Marshal(&profilecard.AccountSearch{
Start: proto.Int32(0),
End: proto.Uint32(4),
Keyword: &keyword,
Keyword: proto.Some(keyword),
Highlight: []string{keyword},
UserLocation: &profilecard.Location{
Latitude: proto.Float64(0),
@ -188,9 +181,9 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
}
// SummaryCard.ReqSearch
func decodeGroupSearchResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeGroupSearchResponse(_ *QQClient, pkt *network.Packet) (any, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(payload))
request.ReadFrom(jce.NewJceReader(pkt.Payload))
data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
if len(data.Map["RespHead"]["SummaryCard.RespHead"]) > 20 {
@ -213,11 +206,11 @@ func decodeGroupSearchResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo
return nil, errors.Wrap(err, "get search result failed")
}
var ret []GroupSearchInfo
for _, g := range searchRsp.GetList() {
for _, g := range searchRsp.List {
ret = append(ret, GroupSearchInfo{
Code: int64(g.GetCode()),
Name: g.GetName(),
Memo: g.GetBrief(),
Code: int64(g.Code.Unwrap()),
Name: g.Name.Unwrap(),
Memo: g.Brief.Unwrap(),
})
}
return ret, nil
@ -226,14 +219,11 @@ func decodeGroupSearchResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo
}
// OidbSvc.0x88d_0
func decodeGroupInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
func decodeGroupInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D88DRspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
if len(rsp.RspGroupInfo) == 0 {
return nil, errors.New(string(rsp.StrErrorInfo))
@ -243,17 +233,16 @@ func decodeGroupInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload
return nil, errors.New("group info not found")
}
return &GroupInfo{
Uin: int64(*info.GroupInfo.GroupUin),
Code: int64(*info.GroupCode),
Uin: int64(info.GroupInfo.GroupUin.Unwrap()),
Code: int64(info.GroupCode.Unwrap()),
Name: string(info.GroupInfo.GroupName),
Memo: string(info.GroupInfo.GroupMemo),
GroupCreateTime: *info.GroupInfo.GroupCreateTime,
GroupLevel: *info.GroupInfo.GroupLevel,
OwnerUin: int64(*info.GroupInfo.GroupOwner),
MemberCount: uint16(*info.GroupInfo.GroupMemberNum),
MaxMemberCount: uint16(*info.GroupInfo.GroupMemberMaxNum),
GroupCreateTime: info.GroupInfo.GroupCreateTime.Unwrap(),
GroupLevel: info.GroupInfo.GroupLevel.Unwrap(),
OwnerUin: int64(info.GroupInfo.GroupOwner.Unwrap()),
MemberCount: uint16(info.GroupInfo.GroupMemberNum.Unwrap()),
MaxMemberCount: uint16(info.GroupInfo.GroupMemberMaxNum.Unwrap()),
Members: []*GroupMemberInfo{},
LastMsgSeq: int64(info.GroupInfo.GetGroupCurMsgSeq()),
LastMsgSeq: int64(info.GroupInfo.GroupCurMsgSeq.Unwrap()),
client: c,
}, nil
}
@ -261,7 +250,7 @@ func decodeGroupInfoResponse(c *QQClient, _ *network.IncomingPacketInfo, payload
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, _ := 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)
@ -279,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) {
if g.AdministratorOrOwner() {
_ = g.client.uploadGroupHeadPortrait(g.Uin, img)
@ -336,7 +318,7 @@ func (g *GroupInfo) AdministratorOrOwner() bool {
}
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)
})
if r == nil {
@ -368,7 +350,7 @@ func (g *GroupInfo) Update(f func(*GroupInfo)) {
f(g)
}
func (g *GroupInfo) Read(f func(*GroupInfo) interface{}) interface{} {
func (g *GroupInfo) Read(f func(*GroupInfo) any) any {
g.lock.RLock()
defer g.lock.RUnlock()
return f(g)
@ -407,7 +389,7 @@ func (m *GroupMemberInfo) EditSpecialTitle(title string) {
func (m *GroupMemberInfo) Kick(msg string, block bool) error {
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
} else {
return errors.New("not manageable")
@ -426,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 {
if m.Uin == m.Group.client.Uin {
return true

View File

@ -4,7 +4,6 @@ import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"math"
"math/rand"
"strconv"
@ -13,8 +12,8 @@ import (
"github.com/pkg/errors"
"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/longmsg"
"github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/client/pb/multimsg"
@ -34,40 +33,18 @@ func init() {
}
// SendGroupMessage 发送群消息
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage, f ...bool) *message.GroupMessage {
useFram := false
if len(f) > 0 {
useFram = f[0]
}
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage) *message.GroupMessage {
imgCount := 0
for _, e := range m.Elements {
switch e.Type() {
case message.Image:
imgCount++
case message.Reply:
useFram = false
}
}
msgLen := message.EstimateLength(m.Elements)
if msgLen > message.MaxMessageSize || imgCount > 50 {
return nil
}
if !useFram && (msgLen > 100 || imgCount > 2) {
lmsg, err := c.uploadGroupLongMessage(groupCode,
message.NewForwardMessage().AddNode(&message.ForwardNode{
SenderId: c.Uin,
SenderName: c.Nickname,
Time: int32(time.Now().Unix()),
Message: m.Elements,
}))
if err != nil {
c.Error("%v", err)
return nil
}
ret := c.sendGroupMessage(groupCode, false, &message.SendingMessage{Elements: []message.IMessageElement{lmsg}})
ret.Elements = m.Elements
return ret
}
return c.sendGroupMessage(groupCode, false, m)
}
@ -119,7 +96,7 @@ func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.Se
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())
fragmented := m.ToFragmented()
for i, elems := range fragmented {
@ -161,58 +138,6 @@ func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.Se
}
}
func (c *QQClient) uploadGroupLongMessage(groupCode int64, m *message.ForwardMessage) (*message.ServiceElement, error) {
ts := time.Now().UnixNano()
seq := c.nextGroupSeq()
data, hash := m.CalculateValidationData(seq, rand.Int31(), groupCode)
rsp, body, err := c.multiMsgApplyUp(groupCode, data, hash, 1)
if err != nil {
return nil, errors.Errorf("upload long message error: %v", err)
}
for i, ip := range rsp.Uint32UpIp {
addr := highway.Addr{IP: uint32(ip), Port: int(rsp.Uint32UpPort[i])}
input := highway.Input{
CommandID: 27,
Key: rsp.MsgSig,
Body: bytes.NewReader(body),
}
err := c.highwaySession.Upload(addr, input)
if err != nil {
c.Error("highway upload long message error: %v", err)
continue
}
return genLongTemplate(rsp.MsgResid, m.Brief(), ts), nil
}
return nil, errors.New("upload long message error: highway server list is empty or not available server.")
}
func (c *QQClient) UploadGroupForwardMessage(groupCode int64, m *message.ForwardMessage) *message.ForwardElement {
if m.Length() > 200 {
return nil
}
ts := time.Now().UnixNano()
seq := c.nextGroupSeq()
data, hash, items := m.CalculateValidationDataForward(seq, rand.Int31(), groupCode)
rsp, body, err := c.multiMsgApplyUp(groupCode, data, hash, 2)
if err != nil {
return nil
}
for i, ip := range rsp.Uint32UpIp {
addr := highway.Addr{IP: uint32(ip), Port: int(rsp.Uint32UpPort[i])}
input := highway.Input{
CommandID: 27,
Key: rsp.MsgSig,
Body: bytes.NewReader(body),
}
err := c.highwaySession.Upload(addr, input)
if err != nil {
continue
}
return genForwardTemplate(rsp.MsgResid, m.Preview(), "群聊的聊天记录", "[聊天记录]", "聊天记录", fmt.Sprintf("查看 %d 条转发消息", m.Length()), ts, items)
}
return nil
}
func (c *QQClient) multiMsgApplyUp(groupCode int64, data []byte, hash []byte, buType int32) (*multimsg.MultiMsgApplyUpRsp, []byte, error) {
i, err := c.sendAndWait(c.buildMultiApplyUpPacket(data, hash, buType, utils.ToGroupUin(groupCode)))
if err != nil {
@ -246,8 +171,8 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
}
}
req := &msg.SendMessageRequest{
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: &groupCode}},
ContentHead: &msg.ContentHead{PkgNum: &pkgNum, PkgIndex: &pkgIndex, DivSeq: &pkgDiv},
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: proto.Some(groupCode)}},
ContentHead: &msg.ContentHead{PkgNum: proto.Some(pkgNum), PkgIndex: proto.Some(pkgIndex), DivSeq: proto.Some(pkgDiv)},
MsgBody: &msg.MessageBody{
RichText: &msg.RichText{
Elems: message.ToProtoElems(m, true),
@ -260,7 +185,7 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
},
},
MsgSeq: proto.Int32(c.nextGroupSeq()),
MsgRand: &r,
MsgRand: proto.Some(r),
SyncCookie: EmptyBytes,
MsgVia: proto.Int32(1),
MsgCtrl: func() *msg.MsgCtrl {
@ -297,94 +222,90 @@ func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byt
}
// OnlinePush.PbPushGroupMsg
func decodeGroupMessagePacket(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeGroupMessagePacket(c *QQClient, packet *network.Packet) (any, error) {
pkt := msg.PushMessagePacket{}
err := proto.Unmarshal(payload, &pkt)
err := proto.Unmarshal(packet.Payload, &pkt)
if err != nil {
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{
Rand: pkt.Message.Body.RichText.Attr.GetRandom(),
Seq: pkt.Message.Head.GetMsgSeq(),
Rand: pkt.Message.Body.RichText.Attr.Random.Unwrap(),
Seq: pkt.Message.Head.MsgSeq.Unwrap(),
Msg: c.parseGroupMessage(pkt.Message),
})
}
if pkt.Message.Content != nil && pkt.Message.Content.GetPkgNum() > 1 {
var builder *groupMessageBuilder
i, ok := c.groupMsgBuilders.Load(pkt.Message.Content.GetDivSeq())
if !ok {
builder = &groupMessageBuilder{}
c.groupMsgBuilders.Store(pkt.Message.Content.GetDivSeq(), builder)
} else {
builder = i.(*groupMessageBuilder)
}
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()))
if pkt.Message.Content != nil && pkt.Message.Content.PkgNum.Unwrap() > 1 {
seq := pkt.Message.Content.DivSeq.Unwrap()
builder := c.messageBuilder(pkt.Message.Content.DivSeq.Unwrap())
builder.append(pkt.Message)
if builder.len() >= pkt.Message.Content.PkgNum.Unwrap() {
c.msgBuilders.Delete(seq)
if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build()))
} else {
c.dispatchGroupMessage(c.parseGroupMessage(builder.build()))
c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build()))
}
}
return nil, nil
}
if pkt.Message.Head.GetFromUin() == c.Uin {
c.dispatchGroupMessageSelf(c.parseGroupMessage(pkt.Message))
if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message))
} else {
c.dispatchGroupMessage(c.parseGroupMessage(pkt.Message))
c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message))
}
return nil, nil
}
func decodeMsgSendResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeMsgSendResponse(c *QQClient, pkt *network.Packet) (any, error) {
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")
}
switch rsp.GetResult() {
switch rsp.Result.Unwrap() {
case 0: // OK.
case 46:
c.error("sendPacket msg error: 需要使用安全设备验证")
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:
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
}
func decodeGetGroupMsgResponse(c *QQClient, info *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeGetGroupMsgResponse(c *QQClient, pkt *network.Packet) (any, error) {
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")
}
if rsp.GetResult() != 0 {
c.Error("get msg error: %v %v", rsp.GetResult(), rsp.GetErrmsg())
return nil, errors.Errorf("get msg error: %v msg: %v", rsp.GetResult(), rsp.GetErrmsg())
if rsp.Result.Unwrap() != 0 {
c.error("get msg error: %v %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
return nil, errors.Errorf("get msg error: %v msg: %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
}
var ret []*message.GroupMessage
for _, m := range rsp.Msg {
if m.Head.FromUin == nil {
if m.Head.FromUin.IsNone() {
continue
}
if m.Content != nil && m.Content.GetPkgNum() > 1 && !info.Params.Bool("raw") {
if m.Content.GetPkgIndex() == 0 {
c.Debug("build fragmented message from history")
i := m.Head.GetMsgSeq() - m.Content.GetPkgNum()
builder := &groupMessageBuilder{}
if m.Content != nil && m.Content.PkgNum.Unwrap() > 1 && !pkt.Params.Bool("raw") {
if m.Content.PkgIndex.Unwrap() == 0 {
c.debug("build fragmented message from history")
i := m.Head.MsgSeq.Unwrap() - m.Content.PkgNum.Unwrap()
builder := &messageBuilder{}
for {
end := int32(math.Min(float64(i+19), float64(m.Head.GetMsgSeq()+m.Content.GetPkgNum())))
seq, pkt := c.buildGetGroupMsgRequest(m.Head.GroupInfo.GetGroupCode(), int64(i), int64(end))
end := int32(math.Min(float64(i+19), float64(m.Head.MsgSeq.Unwrap()+m.Content.PkgNum.Unwrap())))
seq, pkt := c.buildGetGroupMsgRequest(m.Head.GroupInfo.GroupCode.Unwrap(), int64(i), int64(end))
data, err := c.sendAndWait(seq, pkt, network.RequestParams{"raw": true})
if err != nil {
return nil, errors.Wrap(err, "build fragmented message error")
}
for _, fm := range data.([]*message.GroupMessage) {
if fm.OriginalObject.Content != nil && fm.OriginalObject.Content.GetDivSeq() == m.Content.GetDivSeq() {
builder.MessageSlices = append(builder.MessageSlices, fm.OriginalObject)
if fm.OriginalObject.Content != nil && fm.OriginalObject.Content.DivSeq.Unwrap() == m.Content.DivSeq.Unwrap() {
builder.append(fm.OriginalObject)
}
}
if end >= m.Head.GetMsgSeq()+m.Content.GetPkgNum() {
if end >= m.Head.MsgSeq.Unwrap()+m.Content.PkgNum.Unwrap() {
break
}
i = end
@ -402,29 +323,26 @@ func decodeGetGroupMsgResponse(c *QQClient, info *network.IncomingPacketInfo, pa
return ret, nil
}
func decodeAtAllRemainResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
func decodeAtAllRemainResponse(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.D8A7RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
return &AtAllRemainInfo{
CanAtAll: rsp.GetCanAtAll(),
RemainAtAllCountForGroup: rsp.GetRemainAtAllCountForGroup(),
RemainAtAllCountForUin: rsp.GetRemainAtAllCountForUin(),
CanAtAll: rsp.CanAtAll.Unwrap(),
RemainAtAllCountForGroup: rsp.RemainAtAllCountForGroup.Unwrap(),
RemainAtAllCountForUin: rsp.RemainAtAllCountForUin.Unwrap(),
}, nil
}
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 {
c.Debug("sync group %v.", m.Head.GroupInfo.GetGroupCode())
info, err := c.GetGroupInfo(m.Head.GroupInfo.GetGroupCode())
c.debug("sync group %v.", m.Head.GroupInfo.GroupCode.Unwrap())
info, err := c.GetGroupInfo(m.Head.GroupInfo.GroupCode.Unwrap())
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
}
group = info
@ -433,7 +351,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
if len(group.Members) == 0 {
mem, err := c.GetGroupMembers(group)
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
}
group.Members = mem
@ -456,20 +374,20 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
IsFriend: false,
}
} else {
mem := group.FindMember(m.Head.GetFromUin())
mem := group.FindMember(m.Head.FromUin.Unwrap())
if mem == nil {
group.Update(func(_ *GroupInfo) {
if mem = group.FindMemberWithoutLock(m.Head.GetFromUin()); mem != nil {
if mem = group.FindMemberWithoutLock(m.Head.FromUin.Unwrap()); mem != nil {
return
}
info, _ := c.GetMemberInfo(group.Code, m.Head.GetFromUin())
info, _ := c.GetMemberInfo(group.Code, m.Head.FromUin.Unwrap())
if info == nil {
return
}
mem = info
group.Members = append(group.Members, mem)
group.sort()
go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{
go c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
Group: group,
Member: info,
})
@ -487,11 +405,11 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
}
var g *message.GroupMessage
g = &message.GroupMessage{
Id: m.Head.GetMsgSeq(),
Id: m.Head.MsgSeq.Unwrap(),
GroupCode: group.Code,
GroupName: string(m.Head.GroupInfo.GroupName),
Sender: sender,
Time: m.Head.GetMsgTime(),
Time: m.Head.MsgTime.Unwrap(),
Elements: message.ParseMessageElems(m.Body.RichText.Elems),
OriginalObject: m,
}
@ -499,14 +417,14 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
// pre parse
for _, elem := range m.Body.RichText.Elems {
// is rich long msg
if elem.GeneralFlags != nil && elem.GeneralFlags.GetLongTextResid() != "" && len(g.Elements) == 1 {
if f := c.GetForwardMessage(elem.GeneralFlags.GetLongTextResid()); f != nil && len(f.Nodes) == 1 {
if elem.GeneralFlags != nil && elem.GeneralFlags.LongTextResid.Unwrap() != "" && len(g.Elements) == 1 {
if f := c.GetForwardMessage(elem.GeneralFlags.LongTextResid.Unwrap()); f != nil && len(f.Nodes) == 1 {
g = &message.GroupMessage{
Id: m.Head.GetMsgSeq(),
Id: m.Head.MsgSeq.Unwrap(),
GroupCode: group.Code,
GroupName: string(m.Head.GroupInfo.GroupName),
Sender: sender,
Time: m.Head.GetMsgTime(),
Time: m.Head.MsgTime.Unwrap(),
Elements: f.Nodes[0].Message,
OriginalObject: m,
}
@ -517,8 +435,8 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
}
}
if !sender.IsAnonymous() {
mem := group.FindMember(m.Head.GetFromUin())
groupCard := m.Head.GroupInfo.GetGroupCard()
mem := group.FindMember(m.Head.FromUin.Unwrap())
groupCard := m.Head.GroupInfo.GroupCard.Unwrap()
if extInfo != nil && len(extInfo.GroupCard) > 0 && extInfo.GroupCard[0] == 0x0A {
buf := oidb.D8FCCommCardNameBuf{}
if err := proto.Unmarshal(extInfo.GroupCard, &buf); err == nil && len(buf.RichCardName) > 0 {
@ -537,7 +455,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
mem.CardName = groupCard
}
if old != mem.CardName {
go c.dispatchMemberCardUpdatedEvent(&MemberCardUpdatedEvent{
c.MemberCardUpdatedEvent.dispatch(c, &MemberCardUpdatedEvent{
Group: group,
OldCard: old,
Member: mem,
@ -546,17 +464,49 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
}
}
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{
&message.VoiceElement{
Name: m.Body.RichText.Ptt.GetFileName(),
Name: m.Body.RichText.Ptt.FileName.Unwrap(),
Md5: m.Body.RichText.Ptt.FileMd5,
Size: m.Body.RichText.Ptt.GetFileSize(),
Url: "http://grouptalk.c2c.qq.com" + string(m.Body.RichText.Ptt.DownPara),
Size: m.Body.RichText.Ptt.FileSize.Unwrap(),
Url: url,
},
}
}
if m.Body.RichText.Attr != nil {
g.InternalId = m.Body.RichText.Attr.GetRandom()
g.InternalId = m.Body.RichText.Attr.Random.Unwrap()
}
return g
}
@ -568,8 +518,8 @@ func (c *QQClient) SetEssenceMessage(groupCode int64, msgID, msgInternalId int32
return errors.Wrap(err, "set essence msg network")
}
rsp := r.(*oidb.EACRspBody)
if rsp.GetErrorCode() != 0 {
return errors.New(rsp.GetWording())
if rsp.ErrorCode.Unwrap() != 0 {
return errors.New(rsp.Wording.Unwrap())
}
return nil
}
@ -581,8 +531,8 @@ func (c *QQClient) DeleteEssenceMessage(groupCode int64, msgID, msgInternalId in
return errors.Wrap(err, "set essence msg networ")
}
rsp := r.(*oidb.EACRspBody)
if rsp.GetErrorCode() != 0 {
return errors.New(rsp.GetWording())
if rsp.ErrorCode.Unwrap() != 0 {
return errors.New(rsp.Wording.Unwrap())
}
return nil
}
@ -598,14 +548,11 @@ func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand
}
// OidbSvc.0xeac_1/2
func decodeEssenceMsgResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
func decodeEssenceMsgResponse(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := &oidb.EACRspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
return rsp, nil
}

View File

@ -15,7 +15,6 @@ import (
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb/channel"
"github.com/Mrs4s/MiraiGo/utils"
)
@ -176,16 +175,16 @@ func (g *GuildInfo) removeChannel(id uint64) {
}
func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error) {
flags := binary.DynamicProtoMessage{}
flags := proto.DynamicMessage{}
for i := 3; i <= 29; i++ {
flags[uint64(i)] = uint32(1)
flags[uint64(i)] = 1
}
flags[99] = uint32(1)
flags[100] = uint32(1)
payload := s.c.packOIDBPackageDynamically(3976, 1, binary.DynamicProtoMessage{
flags[99] = 1
flags[100] = 1
payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
1: flags,
3: tinyId,
4: uint32(0),
4: 0,
})
rsp, err := s.c.sendAndWaitDynamic(s.c.uniPacket("OidbSvcTrpcTcp.0xfc9_1", payload))
if err != nil {
@ -198,9 +197,9 @@ func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error)
// todo: 解析个性档案
return &GuildUserProfile{
TinyId: tinyId,
Nickname: body.Profile.GetNickname(),
AvatarUrl: body.Profile.GetAvatarUrl(),
JoinTime: body.Profile.GetJoinTime(),
Nickname: body.Profile.Nickname.Unwrap(),
AvatarUrl: body.Profile.AvatarUrl.Unwrap(),
JoinTime: body.Profile.JoinTime.Unwrap(),
}, nil
}
@ -209,16 +208,23 @@ func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error)
// 后续请求请根据上次请求的返回值进行设置
func (s *GuildService) FetchGuildMemberListWithRole(guildId, channelId uint64, startIndex uint32, roleIdIndex uint64, param string) (*FetchGuildMemberListWithRoleResult, error) {
seq := s.c.nextSeq()
u1 := uint32(1)
m := binary.DynamicProtoMessage{
m := proto.DynamicMessage{
1: guildId, // guild id
2: uint32(3),
3: uint32(0),
4: binary.DynamicProtoMessage{ // unknown param, looks like flags
1: u1, 2: u1, 3: u1, 4: u1, 5: u1, 6: u1, 7: u1, 8: u1, 20: u1,
2: 3,
3: 0,
4: proto.DynamicMessage{ // unknown param, looks like flags
1: 1,
2: 1,
3: 1,
4: 1,
5: 1,
6: 1,
7: 1,
8: 1,
20: 1,
},
6: startIndex,
8: uint32(50), // count
8: 50, // count
12: channelId,
}
if param != "" {
@ -238,44 +244,44 @@ func (s *GuildService) FetchGuildMemberListWithRole(guildId, channelId uint64, s
for _, memberWithRole := range body.MemberWithRoles {
for _, mem := range memberWithRole.Members {
ret = append(ret, &GuildMemberInfo{
TinyId: mem.GetTinyId(),
Title: mem.GetTitle(),
Nickname: mem.GetNickname(),
LastSpeakTime: mem.GetLastSpeakTime(),
Role: memberWithRole.GetRoleId(),
RoleName: memberWithRole.GetRoleName(),
TinyId: mem.TinyId.Unwrap(),
Title: mem.Title.Unwrap(),
Nickname: mem.Nickname.Unwrap(),
LastSpeakTime: mem.LastSpeakTime.Unwrap(),
Role: memberWithRole.RoleId.Unwrap(),
RoleName: memberWithRole.RoleName.Unwrap(),
})
}
}
for _, mem := range body.Members {
ret = append(ret, &GuildMemberInfo{
TinyId: mem.GetTinyId(),
Title: mem.GetTitle(),
Nickname: mem.GetNickname(),
LastSpeakTime: mem.GetLastSpeakTime(),
TinyId: mem.TinyId.Unwrap(),
Title: mem.Title.Unwrap(),
Nickname: mem.Nickname.Unwrap(),
LastSpeakTime: mem.LastSpeakTime.Unwrap(),
Role: 1,
RoleName: "普通成员",
})
}
return &FetchGuildMemberListWithRoleResult{
Members: ret,
NextIndex: body.GetNextIndex(),
NextRoleId: body.GetNextRoleIdIndex(),
NextQueryParam: body.GetNextQueryParam(),
Finished: body.NextIndex == nil,
NextIndex: body.NextIndex.Unwrap(),
NextRoleId: body.NextRoleIdIndex.Unwrap(),
NextQueryParam: body.NextQueryParam.Unwrap(),
Finished: body.NextIndex.IsNone(),
}, nil
}
// FetchGuildMemberProfileInfo 获取单个频道成员资料
func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*GuildUserProfile, error) {
seq := s.c.nextSeq()
flags := binary.DynamicProtoMessage{}
flags := proto.DynamicMessage{}
for i := 3; i <= 29; i++ {
flags[uint64(i)] = uint32(1)
flags[uint64(i)] = 1
}
flags[99] = uint32(1)
flags[100] = uint32(1)
payload := s.c.packOIDBPackageDynamically(3976, 1, binary.DynamicProtoMessage{
flags[99] = 1
flags[100] = 1
payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
1: flags,
3: tinyId,
4: guildId,
@ -296,16 +302,16 @@ func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*Gui
// todo: 解析个性档案
return &GuildUserProfile{
TinyId: tinyId,
Nickname: body.Profile.GetNickname(),
AvatarUrl: body.Profile.GetAvatarUrl(),
JoinTime: body.Profile.GetJoinTime(),
Nickname: body.Profile.Nickname.Unwrap(),
AvatarUrl: body.Profile.AvatarUrl.Unwrap(),
JoinTime: body.Profile.JoinTime.Unwrap(),
Roles: roles,
}, nil
}
func (s *GuildService) GetGuildRoles(guildId uint64) ([]*GuildRole, error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1019_1",
s.c.packOIDBPackageDynamically(4121, 1, binary.DynamicProtoMessage{1: guildId}))
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")
@ -314,32 +320,31 @@ func (s *GuildService) GetGuildRoles(guildId uint64) ([]*GuildRole, error) {
if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
roles := make([]*GuildRole, 0, len(body.GetRoles()))
for _, role := range body.GetRoles() {
roles := make([]*GuildRole, 0, len(body.Roles))
for _, role := range body.Roles {
roles = append(roles, &GuildRole{
RoleId: role.GetRoleId(),
RoleName: role.GetName(),
ArgbColor: role.GetArgbColor(),
Independent: role.GetIndependent() == 1,
Num: role.GetNum(),
Owned: role.GetOwned() == 1,
Disabled: role.GetDisabled() == 1,
MaxNum: role.GetMaxNum(),
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) {
u1 := uint32(1)
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1016_1", s.c.packOIDBPackageDynamically(4118, 1, binary.DynamicProtoMessage{
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1016_1", s.c.packOIDBPackageDynamically(4118, 1, proto.DynamicMessage{
1: guildId,
2: binary.DynamicProtoMessage{ // todo: 未知参数
1: u1,
2: u1,
3: u1,
2: proto.DynamicMessage{ // todo: 未知参数
1: 1,
2: 1,
3: 1,
},
3: binary.DynamicProtoMessage{
3: proto.DynamicMessage{
1: name,
2: color,
3: independent,
@ -354,11 +359,11 @@ func (s *GuildService) CreateGuildRole(guildId uint64, name string, color uint32
if err = unpackOIDBPackage(rsp, body); err != nil {
return 0, errors.Wrap(err, "decode packet error")
}
return body.GetRoleId(), nil
return body.RoleId.Unwrap(), nil
}
func (s *GuildService) DeleteGuildRole(guildId uint64, roleId uint64) error {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100e_1", s.c.packOIDBPackageDynamically(4110, 1, binary.DynamicProtoMessage{
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100e_1", s.c.packOIDBPackageDynamically(4110, 1, proto.DynamicMessage{
1: guildId,
2: roleId,
}))
@ -370,7 +375,7 @@ func (s *GuildService) DeleteGuildRole(guildId uint64, roleId uint64) error {
}
func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint64, user []uint64) error { // remove => p2 = false
setOrRemove := binary.DynamicProtoMessage{
setOrRemove := proto.DynamicMessage{
1: roleId,
}
if set {
@ -378,7 +383,7 @@ func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint6
} else {
setOrRemove[3] = user
}
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x101a_1", s.c.packOIDBPackageDynamically(4122, 1, binary.DynamicProtoMessage{
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x101a_1", s.c.packOIDBPackageDynamically(4122, 1, proto.DynamicMessage{
1: guildId,
2: setOrRemove,
}))
@ -390,16 +395,15 @@ func (s *GuildService) SetUserRoleInGuild(guildId uint64, set bool, roleId uint6
}
func (s *GuildService) ModifyRoleInGuild(guildId uint64, roleId uint64, name string, color uint32, indepedent bool) error {
u1 := uint32(1)
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100d_1", s.c.packOIDBPackageDynamically(4109, 1, binary.DynamicProtoMessage{
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100d_1", s.c.packOIDBPackageDynamically(4109, 1, proto.DynamicMessage{
1: guildId,
2: roleId,
3: binary.DynamicProtoMessage{
1: u1,
2: u1,
3: u1,
3: proto.DynamicMessage{
1: 1,
2: 1,
3: 1,
},
4: binary.DynamicProtoMessage{
4: proto.DynamicMessage{
1: name,
2: color,
3: indepedent,
@ -413,18 +417,17 @@ func (s *GuildService) ModifyRoleInGuild(guildId uint64, roleId uint64, name str
}
func (s *GuildService) FetchGuestGuild(guildId uint64) (*GuildMeta, error) {
u1 := uint32(1)
payload := s.c.packOIDBPackageDynamically(3927, 9, binary.DynamicProtoMessage{
1: binary.DynamicProtoMessage{
1: binary.DynamicProtoMessage{
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,
payload := s.c.packOIDBPackageDynamically(3927, 9, proto.DynamicMessage{
1: proto.DynamicMessage{
1: proto.DynamicMessage{
2: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 11: 1, 12: 1, 13: 1, 14: 1, 45: 1,
18: 1, 19: 1, 20: 1, 22: 1, 23: 1, 5002: 1, 5003: 1, 5004: 1, 5005: 1, 10007: 1,
},
2: binary.DynamicProtoMessage{
3: u1, 4: u1, 6: u1, 11: u1, 14: u1, 15: u1, 16: u1, 17: u1,
2: proto.DynamicMessage{
3: 1, 4: 1, 6: 1, 11: 1, 14: 1, 15: 1, 16: 1, 17: 1,
},
},
2: binary.DynamicProtoMessage{
2: proto.DynamicMessage{
1: guildId,
},
})
@ -438,24 +441,24 @@ func (s *GuildService) FetchGuestGuild(guildId uint64) (*GuildMeta, error) {
return nil, errors.Wrap(err, "decode packet error")
}
return &GuildMeta{
GuildName: body.Rsp.Meta.GetName(),
GuildProfile: body.Rsp.Meta.GetProfile(),
MaxMemberCount: body.Rsp.Meta.GetMaxMemberCount(),
MemberCount: body.Rsp.Meta.GetMemberCount(),
CreateTime: body.Rsp.Meta.GetCreateTime(),
MaxRobotCount: body.Rsp.Meta.GetRobotMaxNum(),
MaxAdminCount: body.Rsp.Meta.GetAdminMaxNum(),
OwnerId: body.Rsp.Meta.GetOwnerId(),
GuildName: body.Rsp.Meta.Name.Unwrap(),
GuildProfile: body.Rsp.Meta.Profile.Unwrap(),
MaxMemberCount: body.Rsp.Meta.MaxMemberCount.Unwrap(),
MemberCount: body.Rsp.Meta.MemberCount.Unwrap(),
CreateTime: body.Rsp.Meta.CreateTime.Unwrap(),
MaxRobotCount: body.Rsp.Meta.RobotMaxNum.Unwrap(),
MaxAdminCount: body.Rsp.Meta.AdminMaxNum.Unwrap(),
OwnerId: body.Rsp.Meta.OwnerId.Unwrap(),
}, nil
}
func (s *GuildService) FetchChannelList(guildId uint64) (r []*ChannelInfo, e error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf5d_1",
s.c.packOIDBPackageDynamically(3933, 1,
binary.DynamicProtoMessage{
proto.DynamicMessage{
1: guildId,
3: binary.DynamicProtoMessage{
1: uint32(1),
3: proto.DynamicMessage{
1: 1,
},
}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
@ -473,7 +476,7 @@ func (s *GuildService) FetchChannelList(guildId uint64) (r []*ChannelInfo, e err
}
func (s *GuildService) FetchChannelInfo(guildId, channelId uint64) (*ChannelInfo, error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf55_1", s.c.packOIDBPackageDynamically(3925, 1, binary.DynamicProtoMessage{1: guildId, 2: channelId}))
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf55_1", s.c.packOIDBPackageDynamically(3925, 1, proto.DynamicMessage{1: guildId, 2: channelId}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
@ -501,8 +504,8 @@ func (s *GuildService) GetTopicChannelFeeds(guildId, channelId uint64) ([]*topic
Count: proto.Uint32(12),
From: proto.Uint32(0),
ChannelSign: &channel.StChannelSign{
GuildId: &guildId,
ChannelId: &channelId,
GuildId: proto.Some(guildId),
ChannelId: proto.Some(channelId),
},
FeedAttchInfo: proto.String(""), // isLoadMore
})
@ -610,21 +613,20 @@ func (s *GuildService) PostTopicChannelFeed(guildId, channelId uint64, feed *top
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 != nil {
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) {
u1 := uint32(1)
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1017_1", s.c.packOIDBPackageDynamically(4119, 1, binary.DynamicProtoMessage{
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1017_1", s.c.packOIDBPackageDynamically(4119, 1, proto.DynamicMessage{
1: guildId,
2: tinyId,
4: binary.DynamicProtoMessage{
1: u1,
2: u1,
3: u1,
4: proto.DynamicMessage{
1: 1,
2: 1,
3: 1,
},
}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
@ -635,16 +637,16 @@ func (s *GuildService) fetchMemberRoles(guildId uint64, tinyId uint64) ([]*Guild
if err = unpackOIDBPackage(rsp, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
p1 := body.GetP1()
p1 := body.P1
if p1 == nil {
return nil, errors.New("packet OidbSvcTrpcTcp.0x1017_1: decode p1 error")
}
roles := make([]*GuildRole, 0, len(p1.GetRoles()))
for _, role := range p1.GetRoles() {
roles := make([]*GuildRole, 0, len(p1.Roles))
for _, role := range p1.Roles {
roles = append(roles, &GuildRole{
RoleId: role.GetRoleId(),
RoleName: role.GetName(),
ArgbColor: role.GetArgbColor(),
RoleId: role.RoleId.Unwrap(),
RoleName: role.Name.Unwrap(),
ArgbColor: role.ArgbColor.Unwrap(),
})
}
return roles, nil
@ -657,8 +659,8 @@ func (s *GuildService) fetchChannelListState(guildId uint64, channels []*Channel
for _, info := range channels {
ids = append(ids, info.ChannelId)
}
payload := s.c.packOIDBPackageDynamically(4104, 1, binary.DynamicProtoMessage{
1: binary.DynamicProtoMessage{
payload := s.c.packOIDBPackageDynamically(4104, 1, binary.DynamicMessage{
1: binary.DynamicMessage{
1: guildId,
2: ids,
},
@ -677,32 +679,32 @@ func (s *GuildService) fetchChannelListState(guildId uint64, channels []*Channel
func convertChannelInfo(info *channel.GuildChannelInfo) *ChannelInfo {
meta := &ChannelMeta{
CreatorUin: info.GetCreatorUin(),
CreatorTinyId: info.GetCreatorTinyId(),
CreateTime: info.GetCreateTime(),
GuildId: info.GetGuildId(),
VisibleType: info.GetVisibleType(),
CurrentSlowMode: info.GetCurrentSlowModeKey(),
TalkPermission: info.GetTalkPermission(),
CreatorUin: info.CreatorUin.Unwrap(),
CreatorTinyId: info.CreatorTinyId.Unwrap(),
CreateTime: info.CreateTime.Unwrap(),
GuildId: info.GuildId.Unwrap(),
VisibleType: info.VisibleType.Unwrap(),
CurrentSlowMode: info.CurrentSlowModeKey.Unwrap(),
TalkPermission: info.TalkPermission.Unwrap(),
}
if info.TopMsg != nil {
meta.TopMessageSeq = info.TopMsg.GetTopMsgSeq()
meta.TopMessageTime = info.TopMsg.GetTopMsgTime()
meta.TopMessageOperatorId = info.TopMsg.GetTopMsgOperatorTinyId()
meta.TopMessageSeq = info.TopMsg.TopMsgSeq.Unwrap()
meta.TopMessageTime = info.TopMsg.TopMsgTime.Unwrap()
meta.TopMessageOperatorId = info.TopMsg.TopMsgOperatorTinyId.Unwrap()
}
for _, slow := range info.SlowModeInfos {
meta.SlowModes = append(meta.SlowModes, &ChannelSlowModeInfo{
SlowModeKey: slow.GetSlowModeKey(),
SpeakFrequency: slow.GetSpeakFrequency(),
SlowModeCircle: slow.GetSlowModeCircle(),
SlowModeText: slow.GetSlowModeText(),
SlowModeKey: slow.SlowModeKey.Unwrap(),
SpeakFrequency: slow.SpeakFrequency.Unwrap(),
SlowModeCircle: slow.SlowModeCircle.Unwrap(),
SlowModeText: slow.SlowModeText.Unwrap(),
})
}
return &ChannelInfo{
ChannelId: info.GetChannelId(),
ChannelName: info.GetChannelName(),
NotifyType: uint32(info.GetFinalNotifyType()),
ChannelType: ChannelType(info.GetChannelType()),
ChannelId: info.ChannelId.Unwrap(),
ChannelName: info.ChannelName.Unwrap(),
NotifyType: uint32(info.FinalNotifyType.Unwrap()),
ChannelType: ChannelType(info.ChannelType.Unwrap()),
Meta: meta,
fetchTime: time.Now().Unix(),
}
@ -711,20 +713,20 @@ func convertChannelInfo(info *channel.GuildChannelInfo) *ChannelInfo {
func (c *QQClient) syncChannelFirstView() {
rsp, err := c.sendAndWaitDynamic(c.buildSyncChannelFirstViewPacket())
if err != nil {
c.Error("sync channel error: %v", err)
c.error("sync channel error: %v", err)
return
}
firstViewRsp := new(channel.FirstViewRsp)
if err = proto.Unmarshal(rsp, firstViewRsp); err != nil {
return
}
c.GuildService.TinyId = firstViewRsp.GetSelfTinyid()
c.GuildService.GuildCount = firstViewRsp.GetGuildCount()
c.GuildService.TinyId = firstViewRsp.SelfTinyid.Unwrap()
c.GuildService.GuildCount = firstViewRsp.GuildCount.Unwrap()
if self, err := c.GuildService.GetUserProfile(c.GuildService.TinyId); err == nil {
c.GuildService.Nickname = self.Nickname
c.GuildService.AvatarUrl = self.AvatarUrl
} else {
c.Error("get self guild profile error: %v", err)
c.error("get self guild profile error: %v", err)
}
}
@ -738,35 +740,35 @@ func (c *QQClient) buildSyncChannelFirstViewPacket() (uint16, []byte) {
return c.uniPacket("trpc.group_pro.synclogic.SyncLogic.SyncFirstView", payload)
}
func decodeGuildPushFirstView(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeGuildPushFirstView(c *QQClient, pkt *network.Packet) (any, error) {
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")
}
if len(firstViewMsg.GuildNodes) > 0 {
c.GuildService.Guilds = []*GuildInfo{}
for _, guild := range firstViewMsg.GuildNodes {
info := &GuildInfo{
GuildId: guild.GetGuildId(),
GuildCode: guild.GetGuildCode(),
GuildId: guild.GuildId.Unwrap(),
GuildCode: guild.GuildCode.Unwrap(),
GuildName: utils.B2S(guild.GuildName),
CoverUrl: fmt.Sprintf("https://groupprocover-76483.picgzc.qpic.cn/%v", guild.GetGuildId()),
AvatarUrl: fmt.Sprintf("https://groupprohead-76292.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.GuildId.Unwrap()),
}
channels, err := c.GuildService.FetchChannelList(info.GuildId)
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 {
meta := new(channel.ChannelMsgMeta)
_ = proto.Unmarshal(node.Meta, meta)
info.Channels = append(info.Channels, &ChannelInfo{
ChannelId: node.GetChannelId(),
ChannelId: node.ChannelId.Unwrap(),
ChannelName: utils.B2S(node.ChannelName),
Time: node.GetTime(),
EventTime: node.GetEventTime(),
NotifyType: node.GetNotifyType(),
ChannelType: ChannelType(node.GetChannelType()),
AtAllSeq: meta.GetAtAllSeq(),
Time: node.Time.Unwrap(),
EventTime: node.EventTime.Unwrap(),
NotifyType: node.NotifyType.Unwrap(),
ChannelType: ChannelType(node.ChannelType.Unwrap()),
AtAllSeq: meta.AtAllSeq.Unwrap(),
})
}
} else {
@ -776,7 +778,7 @@ func decodeGuildPushFirstView(c *QQClient, _ *network.IncomingPacketInfo, payloa
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
}

View File

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

View File

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

View File

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

@ -5,13 +5,12 @@ import (
"encoding/json"
"fmt"
"html"
"io"
"mime/multipart"
"net/http"
"net/textproto"
"net/url"
"regexp"
"strconv"
"strings"
"github.com/pkg/errors"
@ -21,51 +20,6 @@ import (
"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 -------- */
type (
@ -106,15 +60,20 @@ const (
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) {
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 {
return nil, err
}
b = b[bytes.Index(b, []byte(`window.__INITIAL_STATE__=`))+25:]
b = b[:bytes.Index(b, []byte("</script>"))]
matched := honorRe.FindSubmatch(b)
if len(matched) == 0 {
return nil, errors.New("无匹配结果")
}
ret := GroupHonorInfo{}
err = json.Unmarshal(b, &ret)
err = json.NewDecoder(bytes.NewReader(matched[1])).Decode(&ret)
if err != nil {
return nil, err
}
@ -165,6 +124,37 @@ func (c *QQClient) GetTts(text string) ([]byte, error) {
/* -------- 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 {
ErrorCode int `json:"ec"`
ErrorMessage string `json:"em"`
@ -177,37 +167,87 @@ type noticeImage struct {
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) {
buf := new(bytes.Buffer)
w := multipart.NewWriter(buf)
err := w.WriteField("bkn", strconv.Itoa(c.getCSRFToken()))
if err != nil {
return nil, errors.Wrap(err, "write multipart<bkn> failed")
}
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")
}
_ = w.WriteField("bkn", strconv.Itoa(c.getCSRFToken()))
_ = w.WriteField("source", "troopNotice")
_ = w.WriteField("m", "0")
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", `form-data; name="pic_up"; filename="temp_uploadFile.png"`)
h.Set("Content-Type", "image/png")
fw, err := w.CreatePart(h)
if err != nil {
return nil, errors.Wrap(err, "create multipart field<pic_up> failed")
}
_, 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)
fw, _ := w.CreatePart(h)
_, _ = fw.Write(img)
_ = w.Close()
req, err := http.NewRequest(http.MethodPost, "https://web.qun.qq.com/cgi-bin/announce/upload_img", buf)
if err != nil {
return nil, errors.Wrap(err, "new request error")
}
@ -218,12 +258,8 @@ func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) {
return nil, errors.Wrap(err, "post error")
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "read body error")
}
res := noticePicUpResponse{}
err = json.Unmarshal(body, &res)
var res noticePicUpResponse
err = json.NewDecoder(resp.Body).Decode(&res)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal json")
}
@ -239,23 +275,42 @@ func (c *QQClient) uploadGroupNoticePic(img []byte) (*noticeImage, error) {
}
// 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))
_, 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")
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 发群公告带图片
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)
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)
_, 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 {
return errors.Wrap(err, "request error")
}

View File

@ -1,11 +1,9 @@
package client
import (
"bytes"
"encoding/hex"
"crypto/rand"
"fmt"
"io"
"math/rand"
"os"
"strings"
"time"
@ -32,19 +30,31 @@ func init() {
var imgWaiter = utils.NewUploadWaiter()
type imageUploadResponse struct {
UploadKey []byte
UploadIp []uint32
UploadPort []uint32
ResourceId string
Message string
FileId int64
Width int32
Height int32
ResultCode int32
IsExists bool
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) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*message.GroupImageElement, error) {
func (c *QQClient) UploadImage(target message.Source, img io.ReadSeeker) (message.IMessageElement, error) {
switch target.SourceType {
case message.SourceGroup, message.SourceGuildChannel, message.SourceGuildDirect:
return c.uploadGroupOrGuildImage(target, img)
case message.SourcePrivate:
return c.uploadPrivateImage(target.PrimaryID, img, 0)
default:
return nil, errors.New("unsupported target type")
}
}
func (c *QQClient) uploadGroupOrGuildImage(target message.Source, img io.ReadSeeker) (message.IMessageElement, error) {
_, _ = img.Seek(0, io.SeekStart) // safe
fh, length := utils.ComputeMd5AndLength(img)
_, _ = img.Seek(0, io.SeekStart)
@ -53,8 +63,27 @@ func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*messag
imgWaiter.Wait(key)
defer imgWaiter.Done(key)
seq, pkt := c.buildGroupImageStorePacket(groupCode, fh, int32(length))
r, err := c.sendAndWait(seq, pkt)
cmd := int32(2)
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 {
return nil, err
}
@ -70,16 +99,19 @@ func (c *QQClient) UploadGroupImage(groupCode int64, img io.ReadSeeker) (*messag
c.highwaySession.AppendAddr(addr, rsp.UploadPort[i])
}
}
if _, err = c.highwaySession.UploadBDH(highway.BdhInput{
CommandID: 2,
input = highway.Transaction{
CommandID: cmd,
Body: img,
Size: length,
Sum: fh,
Ticket: rsp.UploadKey,
Ext: EmptyBytes,
Encrypt: false,
}); err == nil {
goto ok
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:
_, _ = img.Seek(0, io.SeekStart)
i, t, _ := imgsz.DecodeSize(img)
@ -87,61 +119,30 @@ ok:
if t == "gif" {
imageType = 2000
}
return message.NewGroupImage(binary.CalculateImageResourceId(fh), fh, rsp.FileId, int32(length), int32(i.Width), int32(i.Height), imageType), nil
}
func (c *QQClient) UploadGroupImageByFile(groupCode int64, path string) (*message.GroupImageElement, error) {
img, err := os.OpenFile(path, os.O_RDONLY, 0o666)
if err != nil {
return nil, err
width := int32(i.Width)
height := int32(i.Height)
if err != nil && target.SourceType != message.SourceGroup {
c.warning("warning: decode image error: %v. this image will be displayed by wrong size in pc guild client", err)
width = 200
height = 200
}
defer func() { _ = img.Close() }()
fh, length := utils.ComputeMd5AndLength(img)
key := hex.EncodeToString(fh)
imgWaiter.Wait(key)
defer imgWaiter.Done(key)
seq, pkt := c.buildGroupImageStorePacket(groupCode, fh, int32(length))
r, err := c.sendAndWait(seq, pkt)
if err != nil {
return nil, err
if target.SourceType == message.SourceGroup {
return message.NewGroupImage(
binary.CalculateImageResourceId(fh),
fh, rsp.FileId, int32(length),
int32(i.Width), int32(i.Height), imageType,
), nil
}
rsp := r.(*imageUploadResponse)
if rsp.ResultCode != 0 {
return nil, errors.New(rsp.Message)
}
if rsp.IsExists {
goto ok
}
if c.highwaySession.AddrLength() == 0 {
for i, addr := range rsp.UploadIp {
c.highwaySession.AppendAddr(addr, rsp.UploadPort[i])
}
}
if _, err = c.highwaySession.UploadBDHMultiThread(highway.BdhInput{
CommandID: 2,
File: path,
Ticket: rsp.UploadKey,
Ext: EmptyBytes,
Encrypt: false,
}, 1); err == nil {
goto ok
}
return nil, errors.Wrap(err, "upload failed")
ok:
_, _ = img.Seek(0, io.SeekStart)
i, t, _ := imgsz.DecodeSize(img)
var imageType int32 = 1000
if t == "gif" {
imageType = 2000
}
return message.NewGroupImage(binary.CalculateImageResourceId(fh), fh, rsp.FileId, int32(length), int32(i.Width), int32(i.Height), imageType), nil
}
func (c *QQClient) UploadPrivateImage(target int64, img io.ReadSeeker) (*message.FriendImageElement, error) {
return c.uploadPrivateImage(target, img, 0)
return &message.GuildImageElement{
FileId: rsp.FileId,
FilePath: fmt.Sprintf("%x.jpg", fh),
Size: int32(length),
DownloadIndex: rsp.DownloadIndex,
Width: width,
Height: height,
ImageType: imageType,
Md5: fh,
}, nil
}
func (c *QQClient) GetGroupImageDownloadUrl(fileId, groupCode int64, fileMd5 []byte) (string, error) {
@ -152,15 +153,23 @@ func (c *QQClient) GetGroupImageDownloadUrl(fileId, groupCode int64, fileMd5 []b
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)
count++
fh, length := utils.ComputeMd5AndLength(img)
_, _ = 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))
if errors.Is(err, ErrNotExists) {
groupSource := message.Source{
SourceType: message.SourceGroup,
PrimaryID: target,
}
// 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
}
if count >= 5 {
@ -171,21 +180,23 @@ func (c *QQClient) uploadPrivateImage(target int64, img io.ReadSeeker, count int
if err != nil {
return nil, err
}
e.Height = height
e.Width = width
return e, nil
}
func (c *QQClient) ImageOcr(img interface{}) (*OcrResponse, error) {
func (c *QQClient) ImageOcr(img any) (*OcrResponse, error) {
url := ""
switch e := img.(type) {
case *message.GroupImageElement:
url = e.Url
if b, err := utils.HTTPGetReadCloser(e.Url, ""); err == nil {
if url, err = c.uploadOcrImage(b); err != nil {
if url, err = c.uploadOcrImage(b, e.Size, e.Md5); err != nil {
url = e.Url
}
_ = 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 {
return nil, err
}
@ -222,6 +233,7 @@ func (c *QQClient) QueryFriendImage(target int64, hash []byte, size int32) (*mes
return &message.FriendImageElement{
ImageId: rsp.ResourceId,
Md5: hash,
Size: size,
Url: "https://c2cpicdw.qpic.cn/offpic_new/0/" + rsp.ResourceId + "/0?term=2",
}, errors.WithStack(ErrNotExists)
}
@ -229,6 +241,9 @@ func (c *QQClient) QueryFriendImage(target int64, hash []byte, size int32) (*mes
ImageId: rsp.ResourceId,
Md5: hash,
Url: "https://c2cpicdw.qpic.cn/offpic_new/0/" + rsp.ResourceId + "/0?term=2",
Size: size,
Height: rsp.Height,
Width: rsp.Width,
}, nil
}
@ -285,7 +300,7 @@ func (c *QQClient) buildGroupImageDownloadPacket(fileId, groupCode int64, fileMd
return c.uniPacket("ImgStore.GroupPicDown", payload)
}
func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) {
func (c *QQClient) uploadOcrImage(img io.Reader, size int32, sum []byte) (string, error) {
r := make([]byte, 16)
rand.Read(r)
ext, _ := proto.Marshal(&highway2.CommFileExtReq{
@ -293,13 +308,13 @@ func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) {
Uuid: binary.GenUUID(r),
})
buf, _ := io.ReadAll(img)
rsp, err := c.highwaySession.UploadBDH(highway.BdhInput{
rsp, err := c.highwaySession.Upload(highway.Transaction{
CommandID: 76,
Body: bytes.NewReader(buf),
Body: img,
Size: int64(size),
Sum: sum,
Ticket: c.highwaySession.SigSession,
Ext: ext,
Encrypt: false,
})
if err != nil {
return "", errors.Wrap(err, "upload ocr image error")
@ -308,7 +323,7 @@ func (c *QQClient) uploadOcrImage(img io.Reader) (string, error) {
if err = proto.Unmarshal(rsp, &rspExt); err != nil {
return "", errors.Wrap(err, "error unmarshal highway resp")
}
return string(rspExt.GetDownloadUrl()), nil
return string(rspExt.DownloadUrl), nil
}
// OidbSvc.0xe07_0
@ -332,56 +347,53 @@ func (c *QQClient) buildImageOcrRequestPacket(url, md5 string, size, weight, hei
}
// ImgStore.GroupPicUp
func decodeGroupImageStoreResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeGroupImageStoreResponse(_ *QQClient, packet *network.Packet) (any, error) {
pkt := cmd0x388.D388RspBody{}
err := proto.Unmarshal(payload, &pkt)
err := proto.Unmarshal(packet.Payload, &pkt)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
rsp := pkt.TryupImgRsp[0]
if rsp.GetResult() != 0 {
if rsp.Result.Unwrap() != 0 {
return &imageUploadResponse{
ResultCode: int32(rsp.GetResult()),
Message: utils.B2S(rsp.GetFailMsg()),
ResultCode: int32(rsp.Result.Unwrap()),
Message: utils.B2S(rsp.FailMsg),
}, nil
}
if rsp.GetFileExit() {
if rsp.GetImgInfo() != nil {
return &imageUploadResponse{IsExists: true, FileId: int64(rsp.GetFileid()), Width: int32(rsp.ImgInfo.GetFileWidth()), Height: int32(rsp.ImgInfo.GetFileHeight())}, nil
if rsp.FileExit.Unwrap() {
if rsp.ImgInfo != 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{
FileId: int64(rsp.GetFileid()),
FileId: int64(rsp.Fileid.Unwrap()),
UploadKey: rsp.UpUkey,
UploadIp: rsp.GetUpIp(),
UploadPort: rsp.GetUpPort(),
UploadIp: rsp.UpIp,
UploadPort: rsp.UpPort,
}, nil
}
func decodeGroupImageDownloadResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkt := cmd0x388.D388RspBody{}
if err := proto.Unmarshal(payload, &pkt); err != nil {
func decodeGroupImageDownloadResponse(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := cmd0x388.D388RspBody{}
if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
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")
}
if len(pkt.GetimgUrlRsp[0].FailMsg) != 0 {
return nil, errors.New(utils.B2S(pkt.GetimgUrlRsp[0].FailMsg))
if len(rsp.GetimgUrlRsp[0].FailMsg) != 0 {
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
func decodeImageOcrResponse(_ *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
func decodeImageOcrResponse(_ *QQClient, pkt *network.Packet) (any, error) {
rsp := oidb.DE07RspBody{}
if err := proto.Unmarshal(payload, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
}
if rsp.Wording != "" {
if strings.Contains(rsp.Wording, "服务忙") {

View File

@ -1,15 +1,155 @@
package auth
//go:generate stringer -type=Protocol -linecomment
type Protocol int
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/pkg/errors"
)
//go:generate stringer -type=ProtocolType -linecomment
type ProtocolType int
const (
Unset Protocol = iota
AndroidPhone // Android Phone
AndroidWatch // Android Watch
MacOS // MacOS
QiDian // 企点
IPad // iPad
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 {
@ -19,95 +159,47 @@ type AppVersion struct {
SdkVersion string
AppId uint32
SubAppId uint32
AppKey string
BuildTime uint32
SSOVersion uint32
MiscBitmap uint32
SubSigmap uint32
MainSigMap uint32
Protocol Protocol
QUA string
Protocol ProtocolType
}
func (i Protocol) Version() *AppVersion {
switch i {
case AndroidPhone: // Dumped by mirai from qq android v8.8.38
return &AppVersion{
ApkId: "com.tencent.mobileqq",
AppId: 537100432,
SubAppId: 537100432,
SortVersionName: "8.8.38",
BuildTime: 1634310940,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2487",
SSOVersion: 16,
MiscBitmap: 184024956,
SubSigmap: 0x10400,
MainSigMap: 34869472,
Protocol: i,
}
case AndroidWatch:
return &AppVersion{
ApkId: "com.tencent.qqlite",
AppId: 537064446,
SubAppId: 537064446,
SortVersionName: "2.0.5",
BuildTime: 1559564731,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.236",
SSOVersion: 5,
MiscBitmap: 16252796,
SubSigmap: 0x10400,
MainSigMap: 34869472,
Protocol: i,
}
case IPad:
return &AppVersion{
ApkId: "com.tencent.minihd.qq",
AppId: 537097188,
SubAppId: 537097188,
SortVersionName: "8.8.35",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: 1970400,
Protocol: i,
}
case MacOS:
return &AppVersion{
ApkId: "com.tencent.minihd.qq",
AppId: 537064315,
SubAppId: 537064315,
SortVersionName: "5.8.9",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: 1970400,
Protocol: i,
}
case QiDian:
return &AppVersion{
ApkId: "com.tencent.qidian",
AppId: 537061386,
SubAppId: 537036590,
SortVersionName: "3.8.6",
BuildTime: 1556628836,
ApkSign: []byte{160, 30, 236, 171, 133, 233, 227, 186, 43, 15, 106, 21, 140, 133, 92, 41},
SdkVersion: "6.0.0.2365",
SSOVersion: 5,
MiscBitmap: 49807228,
SubSigmap: 66560,
MainSigMap: 34869472,
Protocol: i,
}
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
@ -137,6 +229,7 @@ type SigInfo struct {
G []byte
T402 []byte
RandSeed []byte // t403
T547 []byte
// rollbackSig []byte
// t149 []byte
// t150 []byte
@ -149,3 +242,20 @@ type SigInfo struct {
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

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

View File

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

View File

@ -1,28 +0,0 @@
// Code generated by "stringer -type=Protocol -linecomment"; DO NOT EDIT.
package auth
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Unset-0]
_ = x[AndroidPhone-1]
_ = x[AndroidWatch-2]
_ = x[MacOS-3]
_ = x[QiDian-4]
_ = x[IPad-5]
}
const _Protocol_name = "UnsetAndroid PhoneAndroid WatchMacOS企点iPad"
var _Protocol_index = [...]uint8{0, 5, 18, 31, 36, 42, 46}
func (i Protocol) String() string {
if i < 0 || i >= Protocol(len(_Protocol_index)-1) {
return "Protocol(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Protocol_name[_Protocol_index[i]:_Protocol_index[i+1]]
}

View File

@ -0,0 +1,29 @@
// Code generated by "stringer -type=ProtocolType -linecomment"; DO NOT EDIT.
package auth
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Unset-0]
_ = x[AndroidPhone-1]
_ = x[AndroidWatch-2]
_ = x[MacOS-3]
_ = x[QiDian-4]
_ = x[IPad-5]
_ = x[AndroidPad-6]
}
const _ProtocolType_name = "UnsetAndroid PhoneAndroid WatchMacOS企点iPadAndroid Pad"
var _ProtocolType_index = [...]uint8{0, 5, 18, 31, 36, 42, 46, 57}
func (i ProtocolType) String() string {
if i < 0 || i >= ProtocolType(len(_ProtocolType_index)-1) {
return "ProtocolType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ProtocolType_name[_ProtocolType_index[i]:_ProtocolType_index[i+1]]
}

View File

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

View File

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

View File

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

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

View File

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

View File

@ -9,34 +9,34 @@ import (
"github.com/pkg/errors"
)
type TCPListener struct {
type TCPClient struct {
lock sync.RWMutex
conn net.Conn
connected bool
plannedDisconnect func(*TCPListener)
unexpectedDisconnect func(*TCPListener, error)
plannedDisconnect func(*TCPClient)
unexpectedDisconnect func(*TCPClient, error)
}
var ErrConnectionClosed = errors.New("connection closed")
// PlannedDisconnect 预料中的断开连接
// 如调用 Close() Connect()
func (t *TCPListener) PlannedDisconnect(f func(*TCPListener)) {
func (t *TCPClient) PlannedDisconnect(f func(*TCPClient)) {
t.lock.Lock()
defer t.lock.Unlock()
t.plannedDisconnect = f
}
// UnexpectedDisconnect 未预料的断开连接
func (t *TCPListener) UnexpectedDisconnect(f func(*TCPListener, error)) {
func (t *TCPClient) UnexpectedDisconnect(f func(*TCPClient, error)) {
t.lock.Lock()
defer t.lock.Unlock()
t.unexpectedDisconnect = f
}
func (t *TCPListener) Connect(addr *net.TCPAddr) error {
func (t *TCPClient) Connect(addr string) error {
t.Close()
conn, err := net.DialTCP("tcp", nil, addr)
conn, err := net.Dial("tcp", addr)
if err != nil {
return errors.Wrap(err, "dial tcp error")
}
@ -47,7 +47,7 @@ func (t *TCPListener) Connect(addr *net.TCPAddr) error {
return nil
}
func (t *TCPListener) Write(buf []byte) error {
func (t *TCPClient) Write(buf []byte) error {
if conn := t.getConn(); conn != nil {
_, err := conn.Write(buf)
if err != nil {
@ -60,7 +60,7 @@ func (t *TCPListener) Write(buf []byte) error {
return ErrConnectionClosed
}
func (t *TCPListener) ReadBytes(len int) ([]byte, error) {
func (t *TCPClient) ReadBytes(len int) ([]byte, error) {
buf := make([]byte, len)
if conn := t.getConn(); conn != nil {
_, err := io.ReadFull(conn, buf)
@ -75,7 +75,7 @@ func (t *TCPListener) ReadBytes(len int) ([]byte, error) {
return nil, ErrConnectionClosed
}
func (t *TCPListener) ReadInt32() (int32, error) {
func (t *TCPClient) ReadInt32() (int32, error) {
b, err := t.ReadBytes(4)
if err != nil {
return 0, err
@ -83,17 +83,17 @@ func (t *TCPListener) ReadInt32() (int32, error) {
return int32(binary.BigEndian.Uint32(b)), nil
}
func (t *TCPListener) Close() {
func (t *TCPClient) Close() {
t.close()
t.invokePlannedDisconnect()
}
func (t *TCPListener) unexpectedClose(err error) {
func (t *TCPClient) unexpectedClose(err error) {
t.close()
t.invokeUnexpectedDisconnect(err)
}
func (t *TCPListener) close() {
func (t *TCPClient) close() {
t.lock.Lock()
defer t.lock.Unlock()
if t.conn != nil {
@ -102,7 +102,7 @@ func (t *TCPListener) close() {
}
}
func (t *TCPListener) invokePlannedDisconnect() {
func (t *TCPClient) invokePlannedDisconnect() {
t.lock.RLock()
defer t.lock.RUnlock()
if t.plannedDisconnect != nil && t.connected {
@ -111,7 +111,7 @@ func (t *TCPListener) invokePlannedDisconnect() {
}
}
func (t *TCPListener) invokeUnexpectedDisconnect(err error) {
func (t *TCPClient) invokeUnexpectedDisconnect(err error) {
t.lock.RLock()
defer t.lock.RUnlock()
if t.unexpectedDisconnect != nil && t.connected {
@ -120,7 +120,7 @@ func (t *TCPListener) invokeUnexpectedDisconnect(err error) {
}
}
func (t *TCPListener) getConn() net.Conn {
func (t *TCPClient) getConn() net.Conn {
t.lock.RLock()
defer t.lock.RUnlock()
return t.conn

View File

@ -1,12 +1,13 @@
package network
type IncomingPacketInfo struct {
CommandName string
type Packet struct {
SequenceId uint16
CommandName string
Payload []byte
Params RequestParams
}
type RequestParams map[string]interface{}
type RequestParams map[string]any
func (p RequestParams) Bool(k string) bool {
if p == nil {

View File

@ -7,8 +7,6 @@ const (
RequestTypeSimple = 0x0B
)
var emptyKey = make([]byte, 16)
type EncryptType uint32
const (

View File

@ -46,6 +46,7 @@ func (t *Transport) ReadResponse(head []byte) (*Response, error) {
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)
@ -55,7 +56,7 @@ func (t *Transport) ReadResponse(head []byte) (*Response, error) {
func (t *Transport) readSSOFrame(resp *Response, payload []byte) error {
reader := binary.NewReader(payload)
headLen := reader.ReadInt32()
if headLen-4 > int32(reader.Len()) {
if headLen < 4 || headLen-4 > int32(reader.Len()) {
return errors.WithStack(ErrPacketDropped)
}

View File

@ -2,23 +2,116 @@ package network
import (
"strconv"
"sync"
"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 {
sessionMu sync.Mutex
Sig *auth.SigInfo
Version *auth.AppVersion
Device *auth.Device
Sig *auth.SigInfo
Version *auth.AppVersion
Device *auth.Device
// connection
// conn *TCPListener
// 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 {
@ -38,19 +131,58 @@ func (t *Transport) packBody(req *Request, w *binary.Writer) {
w.WriteUInt32(uint32(len(t.Sig.OutPacketSessionID) + 4))
w.Write(t.Sig.OutPacketSessionID)
if req.Type == RequestTypeLogin {
w.WriteString(t.Device.IMEI)
w.WriteString((*t.Device).IMEI)
w.WriteUInt32(0x04)
w.WriteUInt16(uint16(len(t.Sig.Ksid)) + 2)
w.Write(t.Sig.Ksid)
}
w.WriteUInt32(0x04)
if strings.Contains(WhiteListCommands, req.CommandName) {
secSign := t.PackSecSign(req)
w.WriteUInt32(uint32(len(secSign) + 4))
w.Write(secSign)
}
w.WriteUInt32(0x04 + uint32(len(t.Device.QImei16)))
w.Write([]byte(t.Device.QImei16))
w.WriteUInt32At(pos, uint32(w.Len()-pos))
w.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
@ -89,6 +221,7 @@ func (t *Transport) PackPacket(req *Request) []byte {
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)

View File

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

View File

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

View File

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

43
client/log.go Normal file
View File

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

View File

@ -0,0 +1,43 @@
// Code generated by "stringer -type=LoginError"; DO NOT EDIT.
package client
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[NeedCaptcha-1]
_ = x[OtherLoginError-3]
_ = x[UnsafeDeviceError-4]
_ = x[SMSNeededError-5]
_ = x[TooManySMSRequestError-6]
_ = x[SMSOrVerifyNeededError-7]
_ = x[SliderNeededError-8]
_ = x[UnknownLoginError - -1]
}
const (
_LoginError_name_0 = "UnknownLoginError"
_LoginError_name_1 = "NeedCaptcha"
_LoginError_name_2 = "OtherLoginErrorUnsafeDeviceErrorSMSNeededErrorTooManySMSRequestErrorSMSOrVerifyNeededErrorSliderNeededError"
)
var (
_LoginError_index_2 = [...]uint8{0, 15, 32, 46, 68, 90, 107}
)
func (i LoginError) String() string {
switch {
case i == -1:
return _LoginError_name_0
case i == 1:
return _LoginError_name_1
case 3 <= i && i <= 8:
i -= 3
return _LoginError_name_2[_LoginError_index_2[i]:_LoginError_index_2[i+1]]
default:
return "LoginError(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

View File

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

View File

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

View File

@ -2,7 +2,9 @@ package client
import (
"net"
"net/netip"
"runtime/debug"
"sort"
"sync"
"time"
@ -10,7 +12,6 @@ import (
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils"
)
@ -40,34 +41,36 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo {
r := &ConnectionQualityInfo{}
wg := sync.WaitGroup{}
wg.Add(2)
currentServerAddr := c.servers[c.currServerIndex].String()
go func() {
defer wg.Done()
var err error
if r.ChatServerLatency, err = qualityTest(c.servers[c.currServerIndex].String()); err != nil {
c.Error("test chat server latency error: %v", err)
if r.ChatServerLatency, err = qualityTest(currentServerAddr); err != nil {
c.error("test chat server latency error: %v", err)
r.ChatServerLatency = 9999
}
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 {
c.Error("test long message server latency error: %v", err)
c.error("test long message server latency error: %v", err)
r.LongMessageServerLatency = 9999
}
} else {
c.Error("resolve long message server error: %v", err)
c.error("resolve long message server error: %v", err)
r.LongMessageServerLatency = 9999
}
if c.highwaySession.AddrLength() > 0 {
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
}
}
}()
go func() {
defer wg.Done()
res := utils.RunTCPPingLoop(c.servers[c.currServerIndex].String(), 10)
res := utils.RunTCPPingLoop(currentServerAddr, 10)
r.ChatServerPacketLoss = res.PacketsLoss
if c.highwaySession.AddrLength() > 0 {
res = utils.RunTCPPingLoop(c.highwaySession.SsoAddr[0].String(), 10)
@ -78,17 +81,75 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo {
if _, err := utils.HttpGetBytes("https://ssl.htdata.qq.com", ""); err == nil {
r.LongMessageServerResponseLatency = time.Since(start).Milliseconds()
} 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
}
wg.Wait()
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 中的服务器
func (c *QQClient) connect() error {
c.Info("connect to server: %v", c.servers[c.currServerIndex].String())
err := c.TCP.Connect(c.servers[c.currServerIndex])
// init qq servers
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++
if c.currServerIndex == len(c.servers) {
c.currServerIndex = 0
@ -98,19 +159,19 @@ func (c *QQClient) connect() error {
if c.retryTimes > len(c.servers) {
return errors.New("All servers are unreachable")
}
c.Error("connect server error: %v", err)
c.error("connect server error: %v", err)
return err
}
c.once.Do(func() {
c.OnGroupMessage(func(_ *QQClient, _ *message.GroupMessage) {
c.GroupMessageEvent.Subscribe(func(_ *QQClient, _ *message.GroupMessage) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.OnPrivateMessage(func(_ *QQClient, _ *message.PrivateMessage) {
c.PrivateMessageEvent.Subscribe(func(_ *QQClient, _ *message.PrivateMessage) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.OnTempMessage(func(_ *QQClient, _ *TempMessageEvent) {
c.TempMessageEvent.Subscribe(func(_ *QQClient, _ *TempMessageEvent) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
@ -129,14 +190,14 @@ func (c *QQClient) quickReconnect() {
c.Disconnect()
time.Sleep(time.Millisecond * 200)
if err := c.connect(); err != nil {
c.Error("connect server error: %v", err)
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "quick reconnect failed"})
c.error("connect server error: %v", err)
c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "quick reconnect failed"})
return
}
if err := c.registerClient(); err != nil {
c.Error("register client failed: %v", err)
c.error("register client failed: %v", err)
c.Disconnect()
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "register error"})
c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"})
return
}
}
@ -148,9 +209,9 @@ func (c *QQClient) Disconnect() {
}
// sendAndWait 向服务器发送一个数据包, 并等待返回
func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.RequestParams) (interface{}, error) {
func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.RequestParams) (any, error) {
type T struct {
Response interface{}
Response any
Error error
}
ch := make(chan T, 1)
@ -160,7 +221,7 @@ func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.Request
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{
Response: i,
Error: err,
@ -204,7 +265,7 @@ func (c *QQClient) sendPacket(pkt []byte) error {
// waitPacket
// 等待一个或多个数据包解析, 优先级低于 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)
return func() {
c.waiters.Delete(cmd)
@ -213,9 +274,9 @@ func (c *QQClient) waitPacket(cmd string, f func(interface{}, error)) func() {
// waitPacketTimeoutSyncF
// 等待一个数据包解析, 优先级低于 sendAndWait
func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, filter func(interface{}) bool) (r interface{}, e error) {
notifyChan := make(chan bool)
defer c.waitPacket(cmd, func(i interface{}, err error) {
func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, filter func(any) bool) (r any, e error) {
notifyChan := make(chan bool, 4)
defer c.waitPacket(cmd, func(i any, err error) {
if filter(i) {
r = i
e = err
@ -234,7 +295,7 @@ func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, fil
// 发送数据包并返回需要解析的 response
func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) {
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)
if err != nil {
c.handlers.Delete(seq)
@ -249,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 计划中断线事件
func (c *QQClient) plannedDisconnect(_ *network.TCPListener) {
c.Debug("planned disconnect.")
func (c *QQClient) plannedDisconnect(_ *network.TCPClient) {
c.debug("planned disconnect.")
c.stat.DisconnectTimes.Add(1)
c.Online.Store(false)
}
// unexpectedDisconnect 非预期断线事件
func (c *QQClient) unexpectedDisconnect(_ *network.TCPListener, e error) {
c.Error("unexpected disconnect: %v", e)
func (c *QQClient) unexpectedDisconnect(_ *network.TCPClient, e error) {
c.error("unexpected disconnect: %v", e)
c.stat.DisconnectTimes.Add(1)
c.Online.Store(false)
if err := c.connect(); err != nil {
c.Error("connect server error: %v", err)
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "connection dropped by server."})
c.error("connect server error: %v", err)
c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "connection dropped by server."})
return
}
if err := c.registerClient(); err != nil {
c.Error("register client failed: %v", err)
c.error("register client failed: %v", err)
c.Disconnect()
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "register error"})
c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"})
return
}
}
@ -284,7 +352,7 @@ func (c *QQClient) netLoop() {
continue
}
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++
if errCount > 2 {
go c.quickReconnect()
@ -295,10 +363,10 @@ func (c *QQClient) netLoop() {
resp, err := c.transport.ReadResponse(data)
// pkt, err := packets.ParseIncomingPacket(data, c.sig.D2Key)
if err != nil {
c.Error("parse incoming packet error: %v", err)
c.error("parse incoming packet error: %v", err)
if errors.Is(err, network.ErrSessionExpired) || errors.Is(err, network.ErrPacketDropped) {
c.Disconnect()
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "session expired"})
go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "session expired"})
continue
}
errCount++
@ -310,7 +378,7 @@ func (c *QQClient) netLoop() {
if resp.EncryptType == network.EncryptTypeEmptyKey {
m, err := c.oicq.Unmarshal(resp.Body)
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()
}
@ -319,46 +387,43 @@ func (c *QQClient) netLoop() {
resp.Body = m.Body
}
errCount = 0
c.Debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID)
c.debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID)
c.stat.PacketReceived.Add(1)
pkt := &packets.IncomingPacket{
pkt := &network.Packet{
SequenceId: uint16(resp.SequenceID),
CommandName: resp.CommandName,
Payload: resp.Body,
}
go func(pkt *packets.IncomingPacket) {
go func(pkt *network.Packet) {
defer func() {
if pan := recover(); pan != nil {
c.Error("panic on decoder %v : %v\n%s", pkt.CommandName, pan, debug.Stack())
c.Dump("packet decode error: %v - %v", pkt.Payload, pkt.CommandName, pan)
c.error("panic on decoder %v : %v\n%s", pkt.CommandName, pan, debug.Stack())
c.dump("packet decode error: %v - %v", pkt.Payload, pkt.CommandName, pan)
}
}()
if decoder, ok := decoders[pkt.CommandName]; ok {
// found predefined decoder
info, ok := c.handlers.LoadAndDelete(pkt.SequenceId)
var decoded interface{}
var decoded any
decoded = pkt.Payload
if info == nil || !info.dynamic {
decoded, err = decoder(c, &network.IncomingPacketInfo{
SequenceId: pkt.SequenceId,
CommandName: pkt.CommandName,
Params: info.getParams(),
}, pkt.Payload)
pkt.Params = info.getParams()
decoded, err = decoder(c, pkt)
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 {
info.fun(decoded, err)
} 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 {
// does not need decoder
f.fun(pkt.Payload, nil)
} else {
c.Debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId)
c.debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId)
}
}(pkt)
}

View File

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

View File

@ -33,18 +33,18 @@ func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, [
return seq, packet
}
func decodeOfflineFileDownloadResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []byte) (interface{}, error) {
func decodeOfflineFileDownloadResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := cmd0x346.C346RspBody{}
if err := proto.Unmarshal(payload, &rsp); err != nil {
c.Error("unmarshal cmd0x346 rsp body error: %v", err)
if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
c.error("unmarshal cmd0x346 rsp body error: %v", err)
return nil, errors.Wrap(err, "unmarshal cmd0x346 rsp body error")
}
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")
}
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 "http://" + rsp.ApplyDownloadRsp.DownloadInfo.DownloadDomain + rsp.ApplyDownloadRsp.DownloadInfo.DownloadUrl, nil

View File

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

View File

@ -6,12 +6,12 @@ import (
)
//go:noinline
func (c *QQClient) buildOicqRequestPacket(uin int64, command uint16, body []byte) []byte {
func (c *QQClient) buildOicqRequestPacket(uin int64, command uint16, body *oicq.TLV) []byte {
req := oicq.Message{
Uin: uint32(uin),
Command: command,
EncryptionMethod: oicq.EM_ECDH,
Body: body,
Body: body.Marshal(),
}
return c.oicq.Marshal(&req)
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ syntax = "proto2";
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/msg/msg.proto";

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

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