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

8 Commits

Author SHA1 Message Date
102bda2cfa network: move server addr list to transport 2022-02-03 22:10:08 +08:00
aa4e0e0bbe network: replace Params with Request 2022-01-28 22:54:32 +08:00
c1cbb69110 Merge remote-tracking branch 'origin/master' into network 2022-01-28 22:17:44 +08:00
6a71884235 refactor: handle decode func in Request 2021-12-27 15:41:59 +08:00
LXY
f5b16b19c2 Network refactor (#229)
* 重构:netLoop下移到Listener

* 格式:修正NewClient中注释段缩进

* 格式:更名&将代码移动到对应位置

* 格式:TCPListener去锁化

* 修正:netLoop中的错误调用

* 修正:使其可用

* 修正:使功能一致

* 修正:现在可正常运行

* 优化:更早的释放锁(?

* 修正:未写完的部分

* 修正:潜在的断线时仍然认为在线这件事&删除空重复文件

* 文档:添加部分注释

* 修正:CoverError可能引起死锁

* 修正:永远不会被触发的DisconnectEvent

* 文档:将注释移动至对应位置
2021-12-27 15:36:52 +08:00
9bd6b38f90 refactor: handle params in request 2021-12-27 15:36:47 +08:00
435d2fd85f refactor: 手动指定decode函数
(cherry picked from commit 13d6711f27)
2021-12-27 15:36:44 +08:00
723563415f client: refactor decoder
(cherry picked from commit 3e201bd449)
2021-12-27 15:36:38 +08:00
235 changed files with 40991 additions and 10987 deletions

View File

@ -1,70 +0,0 @@
# 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, typeparam ]
branches: [ master ]
pull_request:
branches: [ master, typeparam ]
branches: [ master ]
jobs:
@ -16,7 +16,7 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: '1.20'
go-version: ^1.13
- 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.19
go-version: ^1.13
- name: Check out code into the Go module directory
uses: actions/checkout@v2

View File

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

View File

@ -21,10 +21,10 @@ func NewJceReader(data []byte) *JceReader {
return &JceReader{buf: data}
}
func (r *JceReader) readHead() HeadData {
hd, l := r.peakHead()
func (r *JceReader) readHead() (hd HeadData, l int) {
hd, l = r.peakHead()
r.off += l
return hd
return
}
func (r *JceReader) peakHead() (hd HeadData, l int) {
@ -49,7 +49,7 @@ func (r *JceReader) skipHead() {
}
func (r *JceReader) skip(l int) {
r.off += l
r.skipBytes(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,6 +105,17 @@ 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")
@ -115,21 +126,15 @@ func (r *JceReader) readByte() byte {
}
func (r *JceReader) readUInt16() uint16 {
b := make([]byte, 2)
r.off += copy(b, r.buf[r.off:])
return goBinary.BigEndian.Uint16(b)
return goBinary.BigEndian.Uint16(r.readBytes(2))
}
func (r *JceReader) readUInt32() uint32 {
b := make([]byte, 4)
r.off += copy(b, r.buf[r.off:])
return goBinary.BigEndian.Uint32(b)
return goBinary.BigEndian.Uint32(r.readBytes(4))
}
func (r *JceReader) readUInt64() uint64 {
b := make([]byte, 8)
r.off += copy(b, r.buf[r.off:])
return goBinary.BigEndian.Uint64(b)
return goBinary.BigEndian.Uint64(r.readBytes(8))
}
func (r *JceReader) readFloat32() float32 {
@ -151,10 +156,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()
}
}
@ -162,7 +167,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
@ -181,7 +186,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
@ -198,7 +203,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
@ -217,7 +222,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
@ -238,7 +243,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
@ -253,7 +258,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
@ -270,7 +275,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())))
@ -285,7 +290,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)
@ -306,7 +311,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)
@ -373,7 +378,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
}
@ -385,7 +390,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)
@ -403,7 +408,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)
@ -417,37 +422,11 @@ 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)
@ -512,7 +491,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)
@ -532,7 +511,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)
@ -552,7 +531,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)
@ -572,7 +551,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)
@ -592,7 +571,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)
@ -612,7 +591,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)
@ -632,7 +611,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)
@ -652,7 +631,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)
@ -672,7 +651,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)
@ -692,7 +671,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)
@ -712,7 +691,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 (
"crypto/rand"
"math/rand"
"reflect"
"strconv"
"sync"
@ -99,7 +99,7 @@ func TestJceReader_ReadBytes(t *testing.T) {
assert.Equal(t, b, rb)
}
func (w *JceWriter) WriteObject(i any, tag byte) {
func (w *JceWriter) WriteObject(i interface{}, 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 any) {
func (w *JceWriter) WriteJceStructRaw(s interface{}) {
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 any, tag byte) {
func (w *JceWriter) WriteSlice(i interface{}, 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 any, tag byte) {
func (w *JceWriter) WriteMap(m interface{}, tag byte) {
va := reflect.ValueOf(m)
if va.Kind() != reflect.Map {
panic("JceWriter.WriteMap: not a map")

View File

@ -527,12 +527,6 @@ 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) {
@ -742,9 +736,3 @@ 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,11 +750,3 @@ 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,13 +2,14 @@ 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() any {
New: func() interface{} {
return new(Writer)
},
}
@ -24,7 +25,7 @@ func SelectWriter() *Writer {
// PutWriter 将 Writer 放回池中
func PutWriter(w *Writer) {
// See https://golang.org/issue/23199
const maxSize = 32 * 1024
const maxSize = 1 << 16
if (*bytes.Buffer)(w).Cap() < maxSize { // 对于大Buffer直接丢弃
w.Reset()
bufferPool.Put(w)
@ -32,7 +33,7 @@ func PutWriter(w *Writer) {
}
var gzipPool = sync.Pool{
New: func() any {
New: func() interface{} {
buf := new(bytes.Buffer)
w := gzip.NewWriter(buf)
return &GzipWriter{
@ -64,7 +65,7 @@ type zlibWriter struct {
}
var zlibPool = sync.Pool{
New: func() any {
New: func() interface{} {
buf := new(bytes.Buffer)
w := zlib.NewWriter(buf)
return &zlibWriter{

94
binary/protobuf.go Normal file
View File

@ -0,0 +1,94 @@
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

@ -1,7 +1,6 @@
package proto
package binary
import (
"bytes"
"math"
"testing"
)
@ -9,7 +8,7 @@ import (
func benchEncoderUvarint(b *testing.B, v uint64) {
e := encoder{}
for i := 0; i < b.N; i++ {
e.buf = e.buf[:0]
e.Reset()
e.uvarint(v)
}
}
@ -17,7 +16,7 @@ func benchEncoderUvarint(b *testing.B, v uint64) {
func benchEncoderSvarint(b *testing.B, v int64) {
e := encoder{}
for i := 0; i < b.N; i++ {
e.buf = e.buf[:0]
e.Reset()
e.svarint(v)
}
}
@ -45,15 +44,3 @@ func Benchmark_encoder_svarint(b *testing.B) {
benchEncoderSvarint(b, math.MaxInt64)
})
}
func TestDynamicMessage_Encode(t *testing.T) {
input := DynamicMessage{
1: 2,
3: 4,
}
got := input.Encode()
expected := []byte{1 << 3, 2, 3 << 3, 4}
if !bytes.Equal(got, expected) {
t.Fatalf("expected %v but got %v", expected, got)
}
}

View File

@ -17,6 +17,8 @@ type NetworkReader struct {
conn net.Conn
}
type TlvMap map[uint16][]byte
// --- ByteStream reader ---
func NewReader(data []byte) *Reader {
@ -50,20 +52,17 @@ func (r *Reader) ReadBytesShort() []byte {
}
func (r *Reader) ReadUInt16() uint16 {
b := make([]byte, 2)
_, _ = r.buf.Read(b)
b := r.ReadBytes(2)
return binary.BigEndian.Uint16(b)
}
func (r *Reader) ReadInt32() int32 {
b := make([]byte, 4)
_, _ = r.buf.Read(b)
b := r.ReadBytes(4)
return int32(binary.BigEndian.Uint32(b))
}
func (r *Reader) ReadInt64() int64 {
b := make([]byte, 8)
_, _ = r.buf.Read(b)
b := r.ReadBytes(8)
return int64(binary.BigEndian.Uint64(b))
}
@ -90,12 +89,40 @@ 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()
}
func (r *Reader) Index() int64 {
return r.buf.Size()
func (tlv TlvMap) Exists(key uint16) bool {
_, ok := tlv[key]
return ok
}
// --- Network reader ---
@ -123,8 +150,7 @@ func (r *NetworkReader) ReadBytes(len int) ([]byte, error) {
}
func (r *NetworkReader) ReadInt32() (int32, error) {
b := make([]byte, 4)
_, err := r.conn.Read(b)
b, err := r.ReadBytes(4)
if err != nil {
return 0, err
}

View File

@ -7,6 +7,10 @@ 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的解释
@ -14,6 +18,9 @@ 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)
@ -51,81 +58,80 @@ 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)
}
@ -135,9 +141,9 @@ func NewTeaCipher(key []byte) (t TEA) {
if len(key) != 16 {
return TEA{}
}
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])
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:])
return t
}

View File

@ -2,12 +2,14 @@ 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"
)
@ -33,7 +35,7 @@ func ZlibUncompress(src []byte) []byte {
var out bytes.Buffer
r, _ := zlib.NewReader(b)
defer r.Close()
_, _ = out.ReadFrom(r)
io.Copy(&out, r)
return out.Bytes()
}
@ -41,8 +43,7 @@ func ZlibCompress(data []byte) []byte {
zw := acquireZlibWriter()
_, _ = zw.w.Write(data)
_ = zw.w.Close()
ret := make([]byte, len(zw.buf.Bytes()))
copy(ret, zw.buf.Bytes())
ret := append([]byte(nil), zw.buf.Bytes()...)
releaseZlibWriter(zw)
return ret
}
@ -51,8 +52,7 @@ func GZipCompress(data []byte) []byte {
gw := AcquireGzipWriter()
_, _ = gw.Write(data)
_ = gw.Close()
ret := make([]byte, len(gw.buf.Bytes()))
copy(ret, gw.buf.Bytes())
ret := append([]byte(nil), 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()
_, _ = out.ReadFrom(r)
_, _ = io.Copy(&out, r)
return out.Bytes()
}
@ -98,13 +98,28 @@ 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 ToBytes(i any) []byte {
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 {
return NewWriterF(func(w *Writer) {
// TODO: more types
switch t := i.(type) {

View File

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

View File

@ -1,7 +1,7 @@
package binary
import (
"crypto/rand"
"math/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.Packet){
var privateMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){
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.Packet
208: privatePttDecoder,
}
var nonSvcNotifyTroopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
var nonSvcNotifyTroopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){
36: troopSystemMessageDecoder, 85: troopSystemMessageDecoder,
}
var troopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
var troopSystemMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){
35: troopSystemMessageDecoder, 37: troopSystemMessageDecoder,
45: troopSystemMessageDecoder, 46: troopSystemMessageDecoder, 84: troopSystemMessageDecoder,
86: troopSystemMessageDecoder, 87: troopSystemMessageDecoder,
} // IsSvcNotify
var sysMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
var sysMsgDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){
187: systemMessageDecoder, 188: systemMessageDecoder, 189: systemMessageDecoder,
190: systemMessageDecoder, 191: systemMessageDecoder,
} // IsSvcNotify
var otherDecoders = map[int32]func(*QQClient, *msg.Message, *network.Packet){
var otherDecoders = map[int32]func(*QQClient, *msg.Message, *network.Response){
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.RequestParams) {
func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, resp *network.Response) {
c.sig.SyncCookie = rsp.SyncCookie
c.sig.PubAccountCookie = rsp.PubAccountCookie
// c.msgCtrlBuf = rsp.MsgCtrlBuf
@ -51,62 +51,63 @@ func (c *QQClient) c2cMessageSyncProcessor(rsp *msg.GetMessageResponse, info net
for _, pMsg := range pairMsg.Messages {
// delete message
delItem := &pb.MessageItem{
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(),
FromUin: pMsg.Head.GetFromUin(),
ToUin: pMsg.Head.GetToUin(),
MsgType: pMsg.Head.GetMsgType(),
MsgSeq: pMsg.Head.GetMsgSeq(),
MsgUid: pMsg.Head.GetMsgUid(),
}
delItems = append(delItems, delItem)
if pMsg.Head.ToUin.Unwrap() != c.Uin {
if pMsg.Head.GetToUin() != c.Uin {
continue
}
if (int64(pairMsg.LastReadTime.Unwrap()) & 4294967295) > int64(pMsg.Head.MsgTime.Unwrap()) {
if (int64(pairMsg.GetLastReadTime()) & 4294967295) > int64(pMsg.Head.GetMsgTime()) {
continue
}
c.commMsgProcessor(pMsg, info)
c.commMsgProcessor(pMsg, resp)
}
}
if delItems != nil {
_, _ = c.sendAndWait(c.buildDeleteMessageRequestPacket(delItems))
_, _ = c.call(c.buildDeleteMessageRequestPacket(delItems))
}
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)
if rsp.GetSyncFlag() != msg.SyncFlag_STOP {
c.Debug("continue sync with flag: %v", rsp.SyncFlag)
req := c.buildGetMessageRequest(rsp.GetSyncFlag(), time.Now().Unix())
req.Params = resp.Params()
_, _ = c.callAndDecode(req)
}
}
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())
func (c *QQClient) commMsgProcessor(pMsg *msg.Message, resp *network.Response) {
strKey := fmt.Sprintf("%d%d%d%d", pMsg.Head.GetFromUin(), pMsg.Head.GetToUin(), pMsg.Head.GetMsgSeq(), pMsg.Head.GetMsgUid())
if _, ok := c.msgSvcCache.GetAndUpdate(strKey, time.Hour); ok {
c.debug("c2c msg %v already exists in cache. skip.", pMsg.Head.MsgUid.Unwrap())
c.Debug("c2c msg %v already exists in cache. skip.", pMsg.Head.GetMsgUid())
return
}
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())
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())
return
}
c.lastC2CMsgTime = int64(pMsg.Head.MsgTime.Unwrap())
if info.Bool("init") {
c.lastC2CMsgTime = int64(pMsg.Head.GetMsgTime())
if resp.Params().Bool("init") {
return
}
if decoder, _ := peekC2CDecoder(pMsg.Head.MsgType.Unwrap()); decoder != nil {
decoder(c, pMsg, info)
if decoder, _ := peekC2CDecoder(pMsg.Head.GetMsgType()); decoder != nil {
decoder(c, pMsg, resp)
} else {
c.debug("unknown msg type on c2c processor: %v - %v", pMsg.Head.MsgType.Unwrap(), pMsg.Head.C2CCmd.Unwrap())
c.Debug("unknown msg type on c2c processor: %v - %v", pMsg.Head.GetMsgType(), pMsg.Head.GetC2CCmd())
}
}
func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
switch pMsg.Head.C2CCmd.Unwrap() {
func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ *network.Response) {
switch pMsg.Head.GetC2CCmd() {
case 11, 175: // friend msg
if pMsg.Head.FromUin.Unwrap() == c.Uin {
if pMsg.Head.GetFromUin() == c.Uin {
for {
frdSeq := c.friendSeq.Load()
if frdSeq < pMsg.Head.MsgSeq.Unwrap() {
if c.friendSeq.CompareAndSwap(frdSeq, pMsg.Head.MsgSeq.Unwrap()) {
if frdSeq < pMsg.Head.GetMsgSeq() {
if c.friendSeq.CAS(frdSeq, pMsg.Head.GetMsgSeq()) {
break
}
} else {
@ -117,67 +118,53 @@ func privateMessageDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestPara
if pMsg.Body.RichText == nil || pMsg.Body.RichText.Elems == nil {
return
}
// 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))
if pMsg.Head.GetFromUin() == c.Uin {
c.dispatchPrivateMessageSelf(c.parsePrivateMessage(pMsg))
return
}
c.PrivateMessageEvent.dispatch(c, c.parsePrivateMessage(pMsg))
c.dispatchPrivateMessage(c.parsePrivateMessage(pMsg))
default:
c.debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.C2CCmd.Unwrap())
c.Debug("unknown c2c cmd on private msg decoder: %v", pMsg.Head.GetC2CCmd())
}
}
func privatePttDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
func privatePttDecoder(c *QQClient, pMsg *msg.Message, _ *network.Response) {
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.PrivateMessageEvent.dispatch(c, 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.dispatchPrivateMessage(c.parsePrivateMessage(pMsg))
}
func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ *network.Response) {
if pMsg.Head.C2CTmpMsgHead == nil || pMsg.Body == nil {
return
}
if (pMsg.Head.MsgType.Unwrap() == 529 && pMsg.Head.C2CCmd.Unwrap() == 6) || pMsg.Body.RichText != nil {
if (pMsg.Head.GetMsgType() == 529 && pMsg.Head.GetC2CCmd() == 6) || pMsg.Body.RichText != nil {
genTempSessionInfo := func() *TempSessionInfo {
if pMsg.Head.C2CTmpMsgHead.ServiceType.Unwrap() == 0 {
group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GroupCode.Unwrap())
if pMsg.Head.C2CTmpMsgHead.GetServiceType() == 0 {
group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GetGroupCode())
if group == nil {
return nil
}
return &TempSessionInfo{
Source: GroupSource,
GroupCode: group.Code,
Sender: pMsg.Head.FromUin.Unwrap(),
Sender: pMsg.Head.GetFromUin(),
client: c,
}
}
info := &TempSessionInfo{
Source: 0,
Sender: pMsg.Head.FromUin.Unwrap(),
sig: pMsg.Head.C2CTmpMsgHead.Sig,
Sender: pMsg.Head.GetFromUin(),
sig: pMsg.Head.C2CTmpMsgHead.GetSig(),
client: c,
}
switch pMsg.Head.C2CTmpMsgHead.ServiceType.Unwrap() {
switch pMsg.Head.C2CTmpMsgHead.GetServiceType() {
case 1:
info.Source = MultiChatSource
case 130:
@ -198,41 +185,41 @@ func tempSessionDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams)
return
}
/*
group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GroupCode.Unwrap())
group := c.FindGroup(pMsg.Head.C2CTmpMsgHead.GetGroupCode())
if group == nil {
return
}
*/
if pMsg.Head.FromUin.Unwrap() == c.Uin {
if pMsg.Head.GetFromUin() == c.Uin {
return
}
c.TempMessageEvent.dispatch(c, &TempMessageEvent{
c.dispatchTempMessage(&TempMessageEvent{
Message: c.parseTempMessage(pMsg),
Session: session,
})
}
}
func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ network.RequestParams) {
func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, resp *network.Response) {
groupJoinLock.Lock()
defer groupJoinLock.Unlock()
group := c.FindGroupByUin(pMsg.Head.FromUin.Unwrap())
if pMsg.Head.AuthUin.Unwrap() == c.Uin {
group := c.FindGroupByUin(pMsg.Head.GetFromUin())
if pMsg.Head.GetAuthUin() == c.Uin {
if group == nil && c.ReloadGroupList() == nil {
c.GroupJoinEvent.dispatch(c, c.FindGroupByUin(pMsg.Head.FromUin.Unwrap()))
c.dispatchJoinGroupEvent(c.FindGroupByUin(pMsg.Head.GetFromUin()))
}
} else {
if group != nil && group.FindMember(pMsg.Head.AuthUin.Unwrap()) == nil {
mem, err := c.GetMemberInfo(group.Code, pMsg.Head.AuthUin.Unwrap())
if group != nil && group.FindMember(pMsg.Head.GetAuthUin()) == nil {
mem, err := c.GetMemberInfo(group.Code, pMsg.Head.GetAuthUin())
if err != nil {
c.debug("failed to fetch new member info: %v", err)
c.Debug("error to fetch new member info: %v", err)
return
}
group.Update(func(info *GroupInfo) {
info.Members = append(info.Members, mem)
info.sort()
})
c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
c.dispatchNewMemberEvent(&MemberJoinGroupEvent{
Group: group,
Member: mem,
})
@ -240,45 +227,44 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ network.Re
}
}
func systemMessageDecoder(c *QQClient, _ *msg.Message, _ network.RequestParams) {
_, pkt := c.buildSystemMsgNewFriendPacket()
_ = c.sendPacket(pkt)
func systemMessageDecoder(c *QQClient, _ *msg.Message, _ *network.Response) {
_, _ = c.call(c.buildSystemMsgNewFriendRequest())
}
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 {
func troopSystemMessageDecoder(c *QQClient, pMsg *msg.Message, info *network.Response) {
if !info.Params().Bool("used_reg_proxy") && pMsg.Head.GetMsgType() != 85 && pMsg.Head.GetMsgType() != 36 {
c.exceptAndDispatchGroupSysMsg()
}
if len(pMsg.Body.MsgContent) == 0 {
if len(pMsg.Body.GetMsgContent()) == 0 {
return
}
reader := binary.NewReader(pMsg.Body.MsgContent)
reader := binary.NewReader(pMsg.GetBody().GetMsgContent())
groupCode := uint32(reader.ReadInt32())
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()
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()
}
}
func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, info network.RequestParams) {
if pMsg.Head.C2CCmd.Unwrap() == 6 || pMsg.Head.C2CTmpMsgHead != nil {
func msgType0x211Decoder(c *QQClient, pMsg *msg.Message, info *network.Response) {
if pMsg.Head.GetC2CCmd() == 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.Subcmd.Unwrap() == 1 { // subcmd: 1 -> sendPacket, 2-> recv
rsp, err := c.sendAndWait(c.buildOfflineFileDownloadRequestPacket(sub4.NotOnlineFile.FileUuid)) // offline_file.go
if sub4.NotOnlineFile != nil && sub4.NotOnlineFile.GetSubcmd() == 1 { // subcmd: 1 -> sendPacket, 2-> recv
rsp, err := c.callAndDecode(c.buildOfflineFileDownloadRequestPacket(sub4.NotOnlineFile.FileUuid)) // offline_file.go
if err != nil {
return
}
c.OfflineFileEvent.dispatch(c, &OfflineFileEvent{
c.dispatchOfflineFileEvent(&OfflineFileEvent{
FileName: string(sub4.NotOnlineFile.FileName),
FileSize: sub4.NotOnlineFile.FileSize.Unwrap(),
Sender: pMsg.Head.FromUin.Unwrap(),
FileSize: sub4.NotOnlineFile.GetFileSize(),
Sender: pMsg.Head.GetFromUin(),
DownloadUrl: rsp.(string),
})
}

View File

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

View File

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

View File

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

View File

@ -23,11 +23,10 @@ type (
UserOnlineStatus int
ClientProtocol = auth.ProtocolType
ClientProtocol = auth.Protocol
LoginResponse struct {
Success bool
Code byte
Error LoginError
// Captcha info
@ -78,7 +77,6 @@ type (
Mobile string
LoginDays int64
Qid string
VipLevel string
}
OtherClientInfo struct {
@ -177,6 +175,12 @@ type (
client *QQClient
}
LogEvent struct {
Type string
Message string
Dump []byte
}
ServerUpdatedEvent struct {
Servers []jce.SsoServerInfo
}
@ -192,17 +196,6 @@ 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"`
@ -303,12 +296,8 @@ 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
@ -361,7 +350,6 @@ const (
MacOS = auth.MacOS
QiDian = auth.QiDian
IPad = auth.IPad
AndroidPad = auth.AndroidPad
)
func (r *UserJoinGroupRequest) Accept() {

View File

@ -2,62 +2,18 @@ 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 {
// todo: move to event handle
privateMessageHandlers []func(*QQClient, *message.PrivateMessage)
tempMessageHandlers []func(*QQClient, *TempMessageEvent)
groupMessageHandlers []func(*QQClient, *message.GroupMessage)
selfPrivateMessageHandlers []func(*QQClient, *message.PrivateMessage)
selfGroupMessageHandlers []func(*QQClient, *message.GroupMessage)
guildChannelMessageHandlers []func(*QQClient, *message.GuildChannelMessage)
guildMessageReactionsUpdatedHandlers []func(*QQClient, *GuildMessageReactionsUpdatedEvent)
guildMessageRecalledHandlers []func(*QQClient, *GuildMessageRecalledEvent)
@ -65,14 +21,58 @@ type eventHandlers struct {
guildChannelCreatedHandlers []func(*QQClient, *GuildChannelOperationEvent)
guildChannelDestroyedHandlers []func(*QQClient, *GuildChannelOperationEvent)
memberJoinedGuildHandlers []func(*QQClient, *MemberJoinGuildEvent)
serverUpdatedHandlers []func(*QQClient, *ServerUpdatedEvent) bool
subscribedEventHandlers []any
groupMessageReceiptHandlers sync.Map
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
}
func (c *QQClient) SubscribeEventHandler(handler any) {
c.eventHandlers.subscribedEventHandlers = append(c.eventHandlers.subscribedEventHandlers, handler)
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 (s *GuildService) OnGuildChannelMessage(f func(*QQClient, *message.GuildChannelMessage)) {
@ -103,10 +103,99 @@ 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
@ -121,6 +210,61 @@ 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
@ -198,13 +342,255 @@ 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 any) bool {
c.eventHandlers.groupMessageReceiptHandlers.Range(func(_, f interface{}) 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

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

View File

@ -2,16 +2,13 @@ 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"
@ -29,99 +26,106 @@ 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 GenRandomDevice() *DeviceInfo {
func init() {
r := make([]byte, 16)
crand.Read(r)
const numberRange = "0123456789"
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)
rand.Read(r)
t := md5.Sum(r)
device.IMSIMd5 = t[:]
device.IMEI = GenIMEI()
SystemDeviceInfo.IMSIMd5 = t[:]
SystemDeviceInfo.GenNewGuid()
SystemDeviceInfo.GenNewTgtgtKey()
}
func GenRandomDevice() {
r := make([]byte, 16)
rand.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)
t := md5.Sum(r)
SystemDeviceInfo.IMSIMd5 = t[:]
SystemDeviceInfo.IMEI = GenIMEI()
r = make([]byte, 8)
crand.Read(r)
hex.Encode(device.AndroidId, r)
device.GenNewGuid()
device.GenNewTgtgtKey()
device.RequestQImei()
return device
rand.Read(r)
hex.Encode(SystemDeviceInfo.AndroidId, r)
SystemDeviceInfo.GenNewGuid()
SystemDeviceInfo.GenNewTgtgtKey()
}
func GenIMEI() string {
sum := 0 // the control sum of digits
var final strings.Builder
randGen := rand.New(rand.NewSource(time.Now().UnixNano()))
randSrc := rand.NewSource(time.Now().UnixNano())
randGen := rand.New(randSrc)
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 // and even add them here!
sum += toAdd
final.WriteString(fmt.Sprintf("%d", toAdd)) // and even printing them here!
}
ctrlDigit := (sum * 9) % 10 // calculating the control digit
final.WriteString(strconv.Itoa(ctrlDigit))
final.WriteString(fmt.Sprintf("%d", ctrlDigit))
return final.String()
}
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()
func getSSOAddress() ([]*net.TCPAddr, error) {
protocol := SystemDeviceInfo.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(device.IMEI, 7).
WriteInt32(int32(protocol.AppId), 6).WriteString(SystemDeviceInfo.IMEI, 7).
WriteInt64(0, 8).WriteInt64(0, 9).WriteInt64(0, 10).
WriteInt64(0, 11).WriteByte(0, 12).WriteInt64(0, 13).Bytes()
WriteInt64(0, 11).WriteByte(0, 12).WriteInt64(0, 13).WriteByte(1, 14).Bytes()
buf := &jce.RequestDataVersion3{
Map: map[string][]byte{"HttpServerListReq": packUniRequestData(payload)},
}
@ -139,7 +143,7 @@ func getSSOAddress(device *auth.Device) ([]netip.AddrPort, error) {
tea := binary.NewTeaCipher(key)
encpkt := tea.Encrypt(b)
cl()
rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp?mType=getssolist", encpkt)
rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp", encpkt)
if err != nil {
return nil, errors.Wrap(err, "unable to fetch server list")
}
@ -149,15 +153,15 @@ func getSSOAddress(device *auth.Device) ([]netip.AddrPort, error) {
data.ReadFrom(jce.NewJceReader(rspPkt.SBuffer))
reader := jce.NewJceReader(data.Map["HttpServerListRes"][1:])
servers := reader.ReadSsoServerInfos(2)
adds := make([]netip.AddrPort, 0, len(servers))
adds := make([]*net.TCPAddr, 0, len(servers))
for _, s := range servers {
if strings.Contains(s.Server, "com") {
continue
}
ip, ok := netip.AddrFromSlice(net.ParseIP(s.Server))
if ok {
adds = append(adds, netip.AddrPortFrom(ip, uint16(s.Port)))
}
adds = append(adds, &net.TCPAddr{
IP: net.ParseIP(s.Server),
Port: int(s.Port),
})
}
return adds, nil
}
@ -174,42 +178,42 @@ func qualityTest(addr string) (int64, error) {
}
func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage {
friend := c.FindFriend(msg.Head.FromUin.Unwrap())
friend := c.FindFriend(msg.Head.GetFromUin())
var sender *message.Sender
if friend != nil {
if friend == nil {
sender = &message.Sender{
Uin: msg.Head.GetFromUin(),
Nickname: msg.Head.GetFromNick(),
}
} else {
sender = &message.Sender{
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.MsgSeq.Unwrap(),
Target: msg.Head.ToUin.Unwrap(),
Time: msg.Head.MsgTime.Unwrap(),
Id: msg.Head.GetMsgSeq(),
Target: msg.Head.GetToUin(),
Time: msg.Head.GetMsgTime(),
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.FileName.Unwrap(),
// Md5: msg.Body.RichText.Ptt.FileMd5,
// Size: msg.Body.RichText.Ptt.FileSize.Unwrap(),
// Url: string(msg.Body.RichText.Ptt.DownPara),
// },
// }
// }
if msg.Body.RichText.Ptt != nil {
return []message.IMessageElement{
&message.VoiceElement{
Name: msg.Body.RichText.Ptt.GetFileName(),
Md5: msg.Body.RichText.Ptt.FileMd5,
Size: msg.Body.RichText.Ptt.GetFileSize(),
Url: string(msg.Body.RichText.Ptt.DownPara),
},
}
}
return message.ParseMessageElems(msg.Body.RichText.Elems)
}(),
}
if msg.Body.RichText.Attr != nil {
ret.InternalId = msg.Body.RichText.Attr.Random.Unwrap()
ret.InternalId = msg.Body.RichText.Attr.GetRandom()
}
return ret
}
@ -217,23 +221,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.GroupUin.Unwrap())
group := c.FindGroupByUin(msg.Head.C2CTmpMsgHead.GetGroupUin())
sender := &message.Sender{
Uin: msg.Head.FromUin.Unwrap(),
Uin: msg.Head.GetFromUin(),
Nickname: "Unknown",
IsFriend: false,
}
if group != nil {
groupCode = group.Code
groupName = group.Name
mem := group.FindMember(msg.Head.FromUin.Unwrap())
mem := group.FindMember(msg.Head.GetFromUin())
if mem != nil {
sender.Nickname = mem.Nickname
sender.CardName = mem.CardName
}
}
return &message.TempMessage{
Id: msg.Head.MsgSeq.Unwrap(),
Id: msg.Head.GetMsgSeq(),
GroupCode: groupCode,
GroupName: groupName,
Self: c.Uin,
@ -242,44 +246,12 @@ func (c *QQClient) parseTempMessage(msg *msg.Message) *message.TempMessage {
}
}
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()
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()
})
base := b.slices[0]
for _, m := range b.slices[1:] {
base := b.MessageSlices[0]
for _, m := range b.MessageSlices[1:] {
base.Body.RichText.Elems = append(base.Body.RichText.Elems, m.Body.RichText.Elems...)
}
return base
@ -293,8 +265,15 @@ func packUniRequestData(data []byte) []byte {
return r
}
func genForwardTemplate(resID, preview, summary string, ts int64, items []*msg.PbMultiMsgItem) *message.ForwardElement {
template := forwardDisplay(resID, strconv.FormatInt(ts, 10), preview, summary)
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)
}
}
return &message.ForwardElement{
FileName: strconv.FormatInt(ts, 10),
Content: template,
@ -303,40 +282,46 @@ func genForwardTemplate(resID, preview, summary string, ts int64, items []*msg.P
}
}
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.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("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("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 := getOidbSSOPackage()
defer oidbSSOPool.Put(pkg)
*pkg = oidb.OIDBSSOPkg{
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 proto.DynamicMessage) []byte {
func (c *QQClient) packOIDBPackageDynamically(cmd, serviceType int32, msg binary.DynamicProtoMessage) []byte {
return c.packOIDBPackage(cmd, serviceType, msg.Encode())
}
@ -345,17 +330,59 @@ func (c *QQClient) packOIDBPackageProto(cmd, serviceType int32, msg proto.Messag
return c.packOIDBPackage(cmd, serviceType, b)
}
func unpackOIDBPackage(payload []byte, rsp proto.Message) error {
pkg := getOidbSSOPackage()
defer oidbSSOPool.Put(pkg)
if err := proto.Unmarshal(payload, pkg); err != nil {
func unpackOIDBPackage(buff []byte, payload proto.Message) error {
pkg := new(oidb.OIDBSSOPkg)
if err := proto.Unmarshal(buff, pkg); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message")
}
if pkg.Result != 0 {
return errors.Errorf("oidb result unsuccessful: %v msg: %v", pkg.Result, pkg.ErrorMsg)
if pkg.GetResult() != 0 {
return errors.Errorf("oidb result unsuccessful: %v msg: %v", pkg.GetResult(), pkg.GetErrorMsg())
}
if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil {
if err := proto.Unmarshal(pkg.Bodybuffer, payload); 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,17 +1,22 @@
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/message"
"github.com/Mrs4s/MiraiGo/utils"
)
type (
@ -50,23 +55,12 @@ type (
}
)
func init() {
decoders["OidbSvc.0x6d8_1"] = decodeOIDB6d81Response
decoders["OidbSvc.0x6d6_0"] = decodeOIDB6d60Response
decoders["OidbSvc.0x6d6_2"] = decodeOIDB6d62Response
decoders["OidbSvc.0x6d6_3"] = decodeOIDB6d63Response
decoders["OidbSvc.0x6d6_4"] = decodeOIDB6d64Response
decoders["OidbSvc.0x6d6_5"] = decodeOIDB6d65Response
decoders["OidbSvc.0x6d7_0"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d7_1"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d7_2"] = decodeOIDB6d7Response
decoders["OidbSvc.0x6d9_4"] = ignoreDecoder
}
var fsWaiter = utils.NewUploadWaiter()
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")
}
}()
@ -74,32 +68,32 @@ func (c *QQClient) GetGroupFileSystem(groupCode int64) (fs *GroupFileSystem, err
if g == nil {
return nil, errors.New("group not found")
}
rsp, e := c.sendAndWait(c.buildGroupFileCountRequestPacket(groupCode))
rsp, e := c.callAndDecode(c.buildGroupFileCountRequest(groupCode))
if e != nil {
return nil, e
}
fs = &GroupFileSystem{
FileCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.AllFileCount.Unwrap(),
LimitCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.LimitCount.Unwrap(),
FileCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.GetAllFileCount(),
LimitCount: rsp.(*oidb.D6D8RspBody).FileCountRsp.GetLimitCount(),
GroupCode: groupCode,
client: c,
}
rsp, err = c.sendAndWait(c.buildGroupFileSpaceRequestPacket(groupCode))
rsp, err = c.callAndDecode(c.buildGroupFileSpaceRequest(groupCode))
if err != nil {
return nil, err
}
fs.TotalSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.TotalSpace.Unwrap()
fs.UsedSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.UsedSpace.Unwrap()
fs.TotalSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.GetTotalSpace()
fs.UsedSpace = rsp.(*oidb.D6D8RspBody).GroupSpaceRsp.GetUsedSpace()
return fs, nil
}
func (c *QQClient) GetGroupFileUrl(groupCode int64, fileId string, busId int32) string {
i, err := c.sendAndWait(c.buildGroupFileDownloadReqPacket(groupCode, fileId, busId))
i, err := c.callAndDecode(c.buildGroupFileDownloadReq(groupCode, fileId, busId))
if err != nil {
return ""
}
url := i.(string)
url += fmt.Sprintf("?fname=%x", fileId)
url += "?fname=" + hex.EncodeToString([]byte(fileId))
return url
}
@ -112,7 +106,8 @@ func (fs *GroupFileSystem) GetFilesByFolder(folderID string) ([]*GroupFile, []*G
var files []*GroupFile
var folders []*GroupFolder
for {
i, err := fs.client.sendAndWait(fs.client.buildGroupFileListRequestPacket(fs.GroupCode, folderID, startIndex))
req := fs.client.buildGroupFileListRequest(fs.GroupCode, folderID, startIndex)
i, err := fs.client.callAndDecode(req)
if err != nil {
return nil, nil, err
}
@ -124,54 +119,118 @@ func (fs *GroupFileSystem) GetFilesByFolder(folderID string) ([]*GroupFile, []*G
if item.FileInfo != nil {
files = append(files, &GroupFile{
GroupCode: fs.GroupCode,
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(),
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(),
})
}
if item.FolderInfo != nil {
folders = append(folders, &GroupFolder{
GroupCode: fs.GroupCode,
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(),
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(),
})
}
}
if rsp.FileListInfoRsp.IsEnd.Unwrap() {
if rsp.FileListInfoRsp.GetIsEnd() {
break
}
startIndex = rsp.FileListInfoRsp.NextIndex.Unwrap()
startIndex = rsp.FileListInfoRsp.GetNextIndex()
}
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() }()
f := &LocalFile{
FileName: name,
Body: file,
RemoteFolder: folderId,
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)
req := fs.client.buildGroupFileUploadReq(folderId, name, fs.GroupCode, size, md5Hash, sha1Hash)
i, err := fs.client.callAndDecode(req)
if err != nil {
return errors.Wrap(err, "query upload failed")
}
target := message.Source{
SourceType: message.SourceGroup,
PrimaryID: fs.GroupCode,
rsp := i.(*oidb.UploadFileRspBody)
if rsp.GetBoolFileExist() {
req := fs.client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.GetFileId(), rsp.GetBusId(), rand.Int31())
_, err := fs.client.call(req)
return err
}
return fs.client.UploadFile(target, f)
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")
}
req = client.buildGroupFileFeedsRequest(fs.GroupCode, rsp.GetFileId(), rsp.GetBusId(), rand.Int31())
_, err = client.call(req)
return err
}
func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string {
@ -179,21 +238,21 @@ func (fs *GroupFileSystem) GetDownloadUrl(file *GroupFile) string {
}
func (fs *GroupFileSystem) CreateFolder(parentFolder, name string) error {
if _, err := fs.client.sendAndWait(fs.client.buildGroupFileCreateFolderPacket(fs.GroupCode, parentFolder, name)); err != nil {
if _, err := fs.client.callAndDecode(fs.client.buildGroupFileCreateFolderRequest(fs.GroupCode, parentFolder, name)); err != nil {
return errors.Wrap(err, "create folder error")
}
return nil
}
func (fs *GroupFileSystem) RenameFolder(folderId, newName string) error {
if _, err := fs.client.sendAndWait(fs.client.buildGroupFileRenameFolderPacket(fs.GroupCode, folderId, newName)); err != nil {
if _, err := fs.client.callAndDecode(fs.client.buildGroupFileRenameFolderRequest(fs.GroupCode, folderId, newName)); err != nil {
return errors.Wrap(err, "rename folder error")
}
return nil
}
func (fs *GroupFileSystem) DeleteFolder(folderId string) error {
if _, err := fs.client.sendAndWait(fs.client.buildGroupFileDeleteFolderPacket(fs.GroupCode, folderId)); err != nil {
if _, err := fs.client.callAndDecode(fs.client.buildGroupFileDeleteFolderRequest(fs.GroupCode, folderId)); err != nil {
return errors.Wrap(err, "rename folder error")
}
return nil
@ -202,281 +261,252 @@ func (fs *GroupFileSystem) DeleteFolder(folderId string) error {
// DeleteFile 删除群文件,需要管理权限.
// 返回错误, 空为删除成功
func (fs *GroupFileSystem) DeleteFile(parentFolderID, fileId string, busId int32) string {
i, err := fs.client.sendAndWait(fs.client.buildGroupFileDeleteReqPacket(fs.GroupCode, parentFolderID, fileId, busId))
i, err := fs.client.callAndDecode(fs.client.buildGroupFileDeleteReq(fs.GroupCode, parentFolderID, fileId, busId))
if err != nil {
return err.Error()
}
return i.(string)
}
// 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),
func (c *QQClient) buildGroupFileUploadReq(parentFolderID, fileName string, groupCode, fileSize int64, md5, sha1 []byte) *network.Request {
b, _ := proto.Marshal(&oidb.D6D6ReqBody{UploadFileReq: &oidb.UploadFileReqBody{
GroupCode: &groupCode,
AppId: proto.Int32(3),
BusId: proto.Int32(102),
Entrance: proto.Int32(5),
ParentFolderId: proto.Some(file.RemoteFolder),
FileName: proto.Some(file.FileName),
LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + file.FileName),
Int64FileSize: proto.Some(file.size),
Sha: file.sha1,
Md5: file.md5,
ParentFolderId: &parentFolderID,
FileName: &fileName,
LocalPath: proto.String("/storage/emulated/0/Pictures/files/s/" + fileName),
Int64FileSize: &fileSize,
Sha: sha1,
Md5: md5,
SupportMultiUpload: proto.Bool(true),
}}
payload := c.packOIDBPackageProto(1750, 0, body)
return c.uniPacket("OidbSvc.0x6d6_0", payload)
}})
req := &oidb.OIDBSSOPkg{
Command: 1750,
ServiceType: 0,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d6_0", payload, decodeOIDB6d60Response)
}
func (c *QQClient) buildGroupFileFeedsRequest(groupCode int64, fileID string, busId, msgRand int32) (uint16, []byte) {
func (c *QQClient) buildGroupFileFeedsRequest(groupCode int64, fileID string, busId, msgRand int32) *network.Request {
req := c.packOIDBPackageProto(1753, 4, &oidb.D6D9ReqBody{FeedsInfoReq: &oidb.FeedsReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
FeedsInfoList: []*oidb.GroupFileFeedsInfo{{
FileId: proto.Some(fileID),
FileId: &fileID,
FeedFlag: proto.Uint32(1),
BusId: proto.Uint32(uint32(busId)),
MsgRandom: proto.Uint32(uint32(msgRand)),
}},
}})
return c.uniPacket("OidbSvc.0x6d9_4", req)
return c.uniRequest("OidbSvc.0x6d9_4", req, nil)
}
// OidbSvc.0x6d8_1
func (c *QQClient) buildGroupFileListRequestPacket(groupCode int64, folderID string, startIndex uint32) (uint16, []byte) {
func (c *QQClient) buildGroupFileListRequest(groupCode int64, folderID string, startIndex uint32) *network.Request {
body := &oidb.D6D8ReqBody{FileListInfoReq: &oidb.GetFileListReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
FolderId: proto.Some(folderID),
FolderId: &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: proto.Some(startIndex),
StartIndex: &startIndex,
Context: EmptyBytes,
}}
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: 1,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload := c.packOIDBPackageProto(1752, 2, body)
return c.uniPacket("OidbSvc.0x6d8_1", payload)
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d8_1", payload, decodeOIDB6d81Response)
}
func (c *QQClient) buildGroupFileSpaceRequestPacket(groupCode int64) (uint16, []byte) {
func (c *QQClient) buildGroupFileCountRequest(groupCode int64) *network.Request {
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",
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d8_1", payload, decodeOIDB6d81Response)
}
func (c *QQClient) buildGroupFileSpaceRequest(groupCode int64) *network.Request {
body := &oidb.D6D8ReqBody{GroupSpaceReq: &oidb.GetSpaceReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
}}
payload := c.packOIDBPackageProto(1752, 3, body)
return c.uniPacket("OidbSvc.0x6d8_1", payload)
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 1752,
ServiceType: 3,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d8_1", payload, decodeOIDB6d81Response)
}
func (c *QQClient) buildGroupFileCreateFolderPacket(groupCode int64, parentFolder, name string) (uint16, []byte) {
func (c *QQClient) buildGroupFileCreateFolderRequest(groupCode int64, parentFolder, name string) *network.Request {
payload := c.packOIDBPackageProto(1751, 0, &oidb.D6D7ReqBody{CreateFolderReq: &oidb.CreateFolderReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
ParentFolderId: proto.Some(parentFolder),
FolderName: proto.Some(name),
ParentFolderId: &parentFolder,
FolderName: &name,
}})
return c.uniPacket("OidbSvc.0x6d7_0", payload)
return c.uniRequest("OidbSvc.0x6d7_0", payload, decodeOIDB6d7Response)
}
func (c *QQClient) buildGroupFileRenameFolderPacket(groupCode int64, folderId, newName string) (uint16, []byte) {
func (c *QQClient) buildGroupFileRenameFolderRequest(groupCode int64, folderId, newName string) *network.Request {
payload := c.packOIDBPackageProto(1751, 2, &oidb.D6D7ReqBody{RenameFolderReq: &oidb.RenameFolderReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
FolderId: proto.String(folderId),
NewFolderName: proto.String(newName),
}})
return c.uniPacket("OidbSvc.0x6d7_2", payload)
return c.uniRequest("OidbSvc.0x6d7_2", payload, decodeOIDB6d7Response)
}
func (c *QQClient) buildGroupFileDeleteFolderPacket(groupCode int64, folderId string) (uint16, []byte) {
func (c *QQClient) buildGroupFileDeleteFolderRequest(groupCode int64, folderId string) *network.Request {
payload := c.packOIDBPackageProto(1751, 1, &oidb.D6D7ReqBody{DeleteFolderReq: &oidb.DeleteFolderReqBody{
GroupCode: proto.Uint64(uint64(groupCode)),
AppId: proto.Uint32(3),
FolderId: proto.String(folderId),
}})
return c.uniPacket("OidbSvc.0x6d7_1", payload)
return c.uniRequest("OidbSvc.0x6d7_1", payload, decodeOIDB6d7Response)
}
// OidbSvc.0x6d6_2
func (c *QQClient) buildGroupFileDownloadReqPacket(groupCode int64, fileId string, busId int32) (uint16, []byte) {
func (c *QQClient) buildGroupFileDownloadReq(groupCode int64, fileId string, busId int32) *network.Request {
body := &oidb.D6D6ReqBody{
DownloadFileReq: &oidb.DownloadFileReqBody{
GroupCode: proto.Some(groupCode),
GroupCode: &groupCode,
AppId: proto.Int32(3),
BusId: proto.Some(busId),
FileId: proto.Some(fileId),
BusId: &busId,
FileId: &fileId,
},
}
payload := c.packOIDBPackageProto(1750, 2, body)
return c.uniPacket("OidbSvc.0x6d6_2", payload)
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 1750,
ServiceType: 2,
Bodybuffer: b,
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d6_2", payload, decodeOIDB6d62Response)
}
func (c *QQClient) buildGroupFileDeleteReqPacket(groupCode int64, parentFolderId, fileId string, busId int32) (uint16, []byte) {
func (c *QQClient) buildGroupFileDeleteReq(groupCode int64, parentFolderId, fileId string, busId int32) *network.Request {
body := &oidb.D6D6ReqBody{DeleteFileReq: &oidb.DeleteFileReqBody{
GroupCode: proto.Some(groupCode),
GroupCode: &groupCode,
AppId: proto.Int32(3),
BusId: proto.Some(busId),
ParentFolderId: proto.Some(parentFolderId),
FileId: proto.Some(fileId),
BusId: &busId,
ParentFolderId: &parentFolderId,
FileId: &fileId,
}}
payload := c.packOIDBPackageProto(1750, 3, body)
return c.uniPacket("OidbSvc.0x6d6_3", payload)
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 1750,
ServiceType: 3,
Bodybuffer: b,
ClientVersion: "android 8.4.8",
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x6d6_3", payload, decodeOIDB6d63Response)
}
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) {
func decodeOIDB6d81Response(_ *QQClient, resp *network.Response) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D8RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
if err := proto.Unmarshal(resp.Body, &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")
}
return &rsp, nil
}
// OidbSvc.0x6d6_2 GroupFileDownloadRespPacket
func decodeOIDB6d62Response(_ *QQClient, pkt *network.Packet) (any, error) {
// OidbSvc.0x6d6_2
func decodeOIDB6d62Response(_ *QQClient, resp *network.Response) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D6RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
if err := proto.Unmarshal(resp.Body, &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")
}
if rsp.DownloadFileRsp.DownloadUrl == nil {
return nil, errors.New(rsp.DownloadFileRsp.ClientWording.Unwrap())
return nil, errors.New(rsp.DownloadFileRsp.GetClientWording())
}
ip := rsp.DownloadFileRsp.DownloadIp.Unwrap()
ip := rsp.DownloadFileRsp.GetDownloadIp()
url := hex.EncodeToString(rsp.DownloadFileRsp.DownloadUrl)
return fmt.Sprintf("http://%s/ftn_handler/%s/", ip, url), nil
}
// GroupFileDeleteRespPacket
func decodeOIDB6d63Response(_ *QQClient, pkt *network.Packet) (any, error) {
func decodeOIDB6d63Response(_ *QQClient, resp *network.Response) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D6RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
if err := proto.Unmarshal(resp.Body, &pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return rsp.DeleteFileRsp.ClientWording.Unwrap(), nil
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
}
// GroupFileUploadRespPacket
func decodeOIDB6d60Response(_ *QQClient, pkt *network.Packet) (any, error) {
func decodeOIDB6d60Response(_ *QQClient, resp *network.Response) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D6RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
if err := proto.Unmarshal(resp.Body, &pkg); 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(),
if err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return r, nil
return rsp.UploadFileRsp, nil
}
// GroupFileCreateFolderPacket, GroupFileDeleteFolderPacket, GroupFileRenameFolderPacket
func decodeOIDB6d7Response(_ *QQClient, pkt *network.Packet) (any, error) {
func decodeOIDB6d7Response(_ *QQClient, resp *network.Response) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D6D7RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
if err := proto.Unmarshal(resp.Body, &pkg); 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 err := proto.Unmarshal(pkg.Bodybuffer, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if renameRsp := rsp.RenameFolderRsp; renameRsp != nil {
if retCode := renameRsp.RetCode.Unwrap(); retCode != 0 {
return nil, errors.Errorf("rename folder error: %v", retCode)
}
if rsp.CreateFolderRsp != nil && rsp.CreateFolderRsp.GetRetCode() != 0 {
return nil, errors.Errorf("create 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)
}
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())
}
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,6 +27,7 @@ type (
Uin int64
Code int64
Name string
Memo string
OwnerUin int64
GroupCreateTime uint32
GroupLevel uint32
@ -42,17 +43,18 @@ type (
}
GroupMemberInfo struct {
Group *GroupInfo
Uin int64
Nickname string
CardName string
JoinTime int64
LastSpeakTime int64
SpecialTitle string
ShutUpTimestamp int64
Permission MemberPermission
Level uint16
Gender byte
Group *GroupInfo
Uin int64
Gender byte
Nickname string
CardName string
Level uint16
JoinTime int64
LastSpeakTime int64
SpecialTitle string
SpecialTitleExpireTime int64
ShutUpTimestamp int64
Permission MemberPermission
}
// GroupSearchInfo 通过搜索得到的群信息
@ -63,13 +65,8 @@ type (
}
)
func init() {
decoders["SummaryCard.ReqSearch"] = decodeGroupSearchResponse
decoders["OidbSvc.0x88d_0"] = decodeGroupInfoResponse
}
func (c *QQClient) GetGroupInfo(groupCode int64) (*GroupInfo, error) {
i, err := c.sendAndWait(c.buildGroupInfoRequestPacket(groupCode))
i, err := c.callAndDecode(c.buildGroupInfoRequestPacket(groupCode))
if err != nil {
return nil, err
}
@ -77,9 +74,9 @@ func (c *QQClient) GetGroupInfo(groupCode int64) (*GroupInfo, error) {
}
// OidbSvc.0x88d_0
func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte) {
func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) *network.Request {
body := &oidb.D88DReqBody{
AppId: proto.Uint32(c.version().AppId),
AppId: proto.Uint32(c.version.AppId),
ReqGroupInfo: []*oidb.ReqGroupInfo{
{
GroupCode: proto.Uint64(uint64(groupCode)),
@ -114,13 +111,18 @@ func (c *QQClient) buildGroupInfoRequestPacket(groupCode int64) (uint16, []byte)
},
PcClientVersion: proto.Uint32(0),
}
payload := c.packOIDBPackageProto(2189, 0, body)
return c.uniPacket("OidbSvc.0x88d_0", payload)
b, _ := proto.Marshal(body)
req := &oidb.OIDBSSOPkg{
Command: 2189,
Bodybuffer: b,
}
payload, _ := proto.Marshal(req)
return c.uniRequest("OidbSvc.0x88d_0", payload, decodeGroupInfoResponse)
}
// SearchGroupByKeyword 通过关键词搜索陌生群组
func (c *QQClient) SearchGroupByKeyword(keyword string) ([]GroupSearchInfo, error) {
rsp, err := c.sendAndWait(c.buildGroupSearchPacket(keyword))
rsp, err := c.callAndDecode(c.buildGroupSearchPacket(keyword))
if err != nil {
return nil, errors.Wrap(err, "group search failed")
}
@ -128,7 +130,7 @@ func (c *QQClient) SearchGroupByKeyword(keyword string) ([]GroupSearchInfo, erro
}
// SummaryCard.ReqSearch
func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
func (c *QQClient) buildGroupSearchPacket(keyword string) *network.Request {
comm, _ := proto.Marshal(&profilecard.BusiComm{
Ver: proto.Int32(1),
Seq: proto.Int32(rand.Int31()),
@ -140,7 +142,7 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
search, _ := proto.Marshal(&profilecard.AccountSearch{
Start: proto.Int32(0),
End: proto.Uint32(4),
Keyword: proto.Some(keyword),
Keyword: &keyword,
Highlight: []string{keyword},
UserLocation: &profilecard.Location{
Latitude: proto.Float64(0),
@ -177,13 +179,13 @@ func (c *QQClient) buildGroupSearchPacket(keyword string) (uint16, []byte) {
Context: make(map[string]string),
Status: make(map[string]string),
}
return c.uniPacket("SummaryCard.ReqSearch", pkt.ToBytes())
return c.uniRequest("SummaryCard.ReqSearch", pkt.ToBytes(), decodeGroupSearchResponse)
}
// SummaryCard.ReqSearch
func decodeGroupSearchResponse(_ *QQClient, pkt *network.Packet) (any, error) {
func decodeGroupSearchResponse(_ *QQClient, resp *network.Response) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(pkt.Payload))
request.ReadFrom(jce.NewJceReader(resp.Body))
data := &jce.RequestDataVersion2{}
data.ReadFrom(jce.NewJceReader(request.SBuffer))
if len(data.Map["RespHead"]["SummaryCard.RespHead"]) > 20 {
@ -206,11 +208,11 @@ func decodeGroupSearchResponse(_ *QQClient, pkt *network.Packet) (any, error) {
return nil, errors.Wrap(err, "get search result failed")
}
var ret []GroupSearchInfo
for _, g := range searchRsp.List {
for _, g := range searchRsp.GetList() {
ret = append(ret, GroupSearchInfo{
Code: int64(g.Code.Unwrap()),
Name: g.Name.Unwrap(),
Memo: g.Brief.Unwrap(),
Code: int64(g.GetCode()),
Name: g.GetName(),
Memo: g.GetBrief(),
})
}
return ret, nil
@ -219,11 +221,14 @@ func decodeGroupSearchResponse(_ *QQClient, pkt *network.Packet) (any, error) {
}
// OidbSvc.0x88d_0
func decodeGroupInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
func decodeGroupInfoResponse(c *QQClient, resp *network.Response) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D88DRspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
if err := proto.Unmarshal(resp.Body, &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")
}
if len(rsp.RspGroupInfo) == 0 {
return nil, errors.New(string(rsp.StrErrorInfo))
@ -233,16 +238,17 @@ func decodeGroupInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
return nil, errors.New("group info not found")
}
return &GroupInfo{
Uin: int64(info.GroupInfo.GroupUin.Unwrap()),
Code: int64(info.GroupCode.Unwrap()),
Uin: int64(*info.GroupInfo.GroupUin),
Code: int64(*info.GroupCode),
Name: string(info.GroupInfo.GroupName),
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()),
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),
Members: []*GroupMemberInfo{},
LastMsgSeq: int64(info.GroupInfo.GroupCurMsgSeq.Unwrap()),
LastMsgSeq: int64(info.GroupInfo.GetGroupCurMsgSeq()),
client: c,
}, nil
}
@ -250,7 +256,7 @@ func decodeGroupInfoResponse(c *QQClient, pkt *network.Packet) (any, error) {
func (c *QQClient) uploadGroupHeadPortrait(groupCode int64, img []byte) error {
url := fmt.Sprintf("http://htdata3.qq.com/cgi-bin/httpconn?htcmd=0x6ff0072&ver=5520&ukey=%v&range=0&uin=%v&seq=23&groupuin=%v&filetype=3&imagetype=5&userdata=0&subcmd=1&subver=101&clip=0_0_0_0&filesize=%v",
c.getSKey(), c.Uin, groupCode, len(img))
req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(img))
req, _ := http.NewRequest("POST", url, bytes.NewReader(img))
req.Header["User-Agent"] = []string{"Dalvik/2.1.0 (Linux; U; Android 7.1.2; PCRT00 Build/N2G48H)"}
req.Header["Content-Type"] = []string{"multipart/form-data;boundary=****"}
rsp, err := http.DefaultClient.Do(req)
@ -268,6 +274,13 @@ 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)
@ -318,7 +331,7 @@ func (g *GroupInfo) AdministratorOrOwner() bool {
}
func (g *GroupInfo) FindMember(uin int64) *GroupMemberInfo {
r := g.Read(func(info *GroupInfo) any {
r := g.Read(func(info *GroupInfo) interface{} {
return info.FindMemberWithoutLock(uin)
})
if r == nil {
@ -350,7 +363,7 @@ func (g *GroupInfo) Update(f func(*GroupInfo)) {
f(g)
}
func (g *GroupInfo) Read(f func(*GroupInfo) any) any {
func (g *GroupInfo) Read(f func(*GroupInfo) interface{}) interface{} {
g.lock.RLock()
defer g.lock.RUnlock()
return f(g)
@ -389,7 +402,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.KickGroupMembers(m.Group.Code, msg, block, m.Uin)
m.Group.client.kickGroupMember(m.Group.Code, m.Uin, msg, block)
return nil
} else {
return errors.New("not manageable")
@ -408,12 +421,6 @@ 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,6 +4,7 @@ import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"math"
"math/rand"
"strconv"
@ -12,8 +13,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"
@ -25,26 +26,43 @@ import (
func init() {
decoders["OnlinePush.PbPushGroupMsg"] = decodeGroupMessagePacket
decoders["MessageSvc.PbSendMsg"] = decodeMsgSendResponse
decoders["MessageSvc.PbGetGroupMsg"] = decodeGetGroupMsgResponse
decoders["OidbSvc.0x8a7_0"] = decodeAtAllRemainResponse
decoders["OidbSvc.0xeac_1"] = decodeEssenceMsgResponse
decoders["OidbSvc.0xeac_2"] = decodeEssenceMsgResponse
}
// SendGroupMessage 发送群消息
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage) *message.GroupMessage {
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage, f ...bool) *message.GroupMessage {
useFram := false
if len(f) > 0 {
useFram = f[0]
}
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)
}
@ -57,8 +75,9 @@ func (c *QQClient) SendGroupForwardMessage(groupCode int64, m *message.ForwardEl
// GetGroupMessages 从服务器获取历史信息
func (c *QQClient) GetGroupMessages(groupCode, beginSeq, endSeq int64) ([]*message.GroupMessage, error) {
seq, pkt := c.buildGetGroupMsgRequest(groupCode, beginSeq, endSeq)
i, err := c.sendAndWait(seq, pkt, network.RequestParams{"raw": false})
req := c.buildGetGroupMsgRequest(groupCode, beginSeq, endSeq)
req.Params = network.Params{"raw": false}
i, err := c.callAndDecode(req)
if err != nil {
return nil, err
}
@ -66,7 +85,7 @@ func (c *QQClient) GetGroupMessages(groupCode, beginSeq, endSeq int64) ([]*messa
}
func (c *QQClient) GetAtAllRemain(groupCode int64) (*AtAllRemainInfo, error) {
i, err := c.sendAndWait(c.buildAtAllRemainRequestPacket(groupCode))
i, err := c.callAndDecode(c.buildAtAllRemainRequestPacket(groupCode))
if err != nil {
return nil, err
}
@ -96,16 +115,16 @@ func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.Se
serviceFlag = false
}
}
if !forward && serviceFlag && c.UseFragmentMessage && (imgCount > 1 || message.EstimateLength(m.Elements) > 100) {
if !forward && serviceFlag && (imgCount > 1 || message.EstimateLength(m.Elements) > 100) {
div := int32(rand.Uint32())
fragmented := m.ToFragmented()
for i, elems := range fragmented {
_, pkt := c.buildGroupSendingPacket(groupCode, mr, int32(len(fragmented)), int32(i), div, forward, elems)
_ = c.sendPacket(pkt)
req := c.buildGroupSendingReq(groupCode, mr, int32(len(fragmented)), int32(i), div, forward, elems)
c.sendReq(req)
}
} else {
_, pkt := c.buildGroupSendingPacket(groupCode, mr, 1, 0, 0, forward, m.Elements)
_ = c.sendPacket(pkt)
req := c.buildGroupSendingReq(groupCode, mr, 1, 0, 0, forward, m.Elements)
c.sendReq(req)
}
var mid int32
ret := &message.GroupMessage{
@ -138,8 +157,60 @@ 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)))
i, err := c.callAndDecode(c.buildMultiApplyUpPacket(data, hash, buType, utils.ToGroupUin(groupCode)))
if err != nil {
return nil, nil, err
}
@ -162,7 +233,7 @@ func (c *QQClient) multiMsgApplyUp(groupCode int64, data []byte, hash []byte, bu
}
// MessageSvc.PbSendMsg
func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex, pkgDiv int32, forward bool, m []message.IMessageElement) (uint16, []byte) {
func (c *QQClient) buildGroupSendingReq(groupCode int64, r, pkgNum, pkgIndex, pkgDiv int32, forward bool, m []message.IMessageElement) *network.Request {
var ptt *message.GroupVoiceElement
if len(m) > 0 {
if p, ok := m[0].(*message.GroupVoiceElement); ok {
@ -171,8 +242,8 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
}
}
req := &msg.SendMessageRequest{
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)},
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: &groupCode}},
ContentHead: &msg.ContentHead{PkgNum: &pkgNum, PkgIndex: &pkgIndex, DivSeq: &pkgDiv},
MsgBody: &msg.MessageBody{
RichText: &msg.RichText{
Elems: message.ToProtoElems(m, true),
@ -185,7 +256,7 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
},
},
MsgSeq: proto.Int32(c.nextGroupSeq()),
MsgRand: proto.Some(r),
MsgRand: &r,
SyncCookie: EmptyBytes,
MsgVia: proto.Int32(1),
MsgCtrl: func() *msg.MsgCtrl {
@ -196,10 +267,10 @@ func (c *QQClient) buildGroupSendingPacket(groupCode int64, r, pkgNum, pkgIndex,
}(),
}
payload, _ := proto.Marshal(req)
return c.uniPacket("MessageSvc.PbSendMsg", payload)
return c.uniRequest("MessageSvc.PbSendMsg", payload, nil)
}
func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) (uint16, []byte) {
func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) *network.Request {
req := &msg.GetGroupMsgReq{
GroupCode: proto.Uint64(uint64(groupCode)),
BeginSeq: proto.Uint64(uint64(beginSeq)),
@ -207,10 +278,10 @@ func (c *QQClient) buildGetGroupMsgRequest(groupCode, beginSeq, endSeq int64) (u
PublicGroup: proto.Bool(false),
}
payload, _ := proto.Marshal(req)
return c.uniPacket("MessageSvc.PbGetGroupMsg", payload)
return c.uniRequest("MessageSvc.PbGetGroupMsg", payload, decodeGetGroupMsgResponse)
}
func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byte) {
func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) *network.Request {
payload := c.packOIDBPackageProto(2215, 0, &oidb.D8A7ReqBody{
SubCmd: proto.Uint32(1),
LimitIntervalTypeForUin: proto.Uint32(2),
@ -218,94 +289,84 @@ func (c *QQClient) buildAtAllRemainRequestPacket(groupCode int64) (uint16, []byt
Uin: proto.Uint64(uint64(c.Uin)),
GroupCode: proto.Uint64(uint64(groupCode)),
})
return c.uniPacket("OidbSvc.0x8a7_0", payload)
return c.uniRequest("OidbSvc.0x8a7_0", payload, decodeAtAllRemainResponse)
}
// OnlinePush.PbPushGroupMsg
func decodeGroupMessagePacket(c *QQClient, packet *network.Packet) (any, error) {
func decodeGroupMessagePacket(c *QQClient, resp *network.Response) (interface{}, error) {
pkt := msg.PushMessagePacket{}
err := proto.Unmarshal(packet.Payload, &pkt)
err := proto.Unmarshal(resp.Body, &pkt)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
if pkt.Message.Head.GetFromUin() == c.Uin {
c.dispatchGroupMessageReceiptEvent(&groupMessageReceiptEvent{
Rand: pkt.Message.Body.RichText.Attr.Random.Unwrap(),
Seq: pkt.Message.Head.MsgSeq.Unwrap(),
Rand: pkt.Message.Body.RichText.Attr.GetRandom(),
Seq: pkt.Message.Head.GetMsgSeq(),
Msg: c.parseGroupMessage(pkt.Message),
})
}
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()))
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()))
} else {
c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(builder.build()))
c.dispatchGroupMessage(c.parseGroupMessage(builder.build()))
}
}
return nil, nil
}
if pkt.Message.Head.FromUin.Unwrap() == c.Uin {
c.SelfGroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message))
if pkt.Message.Head.GetFromUin() == c.Uin {
c.dispatchGroupMessageSelf(c.parseGroupMessage(pkt.Message))
} else {
c.GroupMessageEvent.dispatch(c, c.parseGroupMessage(pkt.Message))
c.dispatchGroupMessage(c.parseGroupMessage(pkt.Message))
}
return nil, nil
}
func decodeMsgSendResponse(c *QQClient, pkt *network.Packet) (any, error) {
rsp := msg.SendMessageResponse{}
if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
switch rsp.Result.Unwrap() {
case 0: // OK.
case 46:
c.error("sendPacket msg error: 需要使用安全设备验证")
case 55:
c.error("sendPacket msg error: %v Bot has been blocked ta.'s content", rsp.Result.Unwrap())
default:
c.error("sendPacket msg error: %v %v", rsp.Result.Unwrap(), rsp.ErrMsg.Unwrap())
}
return nil, nil
}
func decodeGetGroupMsgResponse(c *QQClient, pkt *network.Packet) (any, error) {
func decodeGetGroupMsgResponse(c *QQClient, resp *network.Response) (interface{}, error) {
rsp := msg.GetGroupMsgResp{}
if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
if err := proto.Unmarshal(resp.Body, &rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if rsp.Result.Unwrap() != 0 {
c.error("get msg error: %v %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
return nil, errors.Errorf("get msg error: %v msg: %v", rsp.Result.Unwrap(), rsp.Errmsg.Unwrap())
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())
}
var ret []*message.GroupMessage
for _, m := range rsp.Msg {
if m.Head.FromUin.IsNone() {
if m.Head.FromUin == nil {
continue
}
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{}
if m.Content != nil && m.Content.GetPkgNum() > 1 && !resp.Params().Bool("raw") {
if m.Content.GetPkgIndex() == 0 {
c.Debug("build fragmented message from history")
i := m.Head.GetMsgSeq() - m.Content.GetPkgNum()
builder := &groupMessageBuilder{}
for {
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})
end := int32(math.Min(float64(i+19), float64(m.Head.GetMsgSeq()+m.Content.GetPkgNum())))
req := c.buildGetGroupMsgRequest(m.Head.GroupInfo.GetGroupCode(), int64(i), int64(end))
req.Params = network.Params{"raw": true}
data, err := c.callAndDecode(req)
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.DivSeq.Unwrap() == m.Content.DivSeq.Unwrap() {
builder.append(fm.OriginalObject)
if fm.OriginalObject.Content != nil && fm.OriginalObject.Content.GetDivSeq() == m.Content.GetDivSeq() {
builder.MessageSlices = append(builder.MessageSlices, fm.OriginalObject)
}
}
if end >= m.Head.MsgSeq.Unwrap()+m.Content.PkgNum.Unwrap() {
if end >= m.Head.GetMsgSeq()+m.Content.GetPkgNum() {
break
}
i = end
@ -323,26 +384,29 @@ func decodeGetGroupMsgResponse(c *QQClient, pkt *network.Packet) (any, error) {
return ret, nil
}
func decodeAtAllRemainResponse(_ *QQClient, pkt *network.Packet) (any, error) {
func decodeAtAllRemainResponse(_ *QQClient, resp *network.Response) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := oidb.D8A7RspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
if err := proto.Unmarshal(resp.Body, &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")
}
return &AtAllRemainInfo{
CanAtAll: rsp.CanAtAll.Unwrap(),
RemainAtAllCountForGroup: rsp.RemainAtAllCountForGroup.Unwrap(),
RemainAtAllCountForUin: rsp.RemainAtAllCountForUin.Unwrap(),
CanAtAll: rsp.GetCanAtAll(),
RemainAtAllCountForGroup: rsp.GetRemainAtAllCountForGroup(),
RemainAtAllCountForUin: rsp.GetRemainAtAllCountForUin(),
}, nil
}
func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
group := c.FindGroup(m.Head.GroupInfo.GroupCode.Unwrap())
group := c.FindGroup(m.Head.GroupInfo.GetGroupCode())
if group == nil {
c.debug("sync group %v.", m.Head.GroupInfo.GroupCode.Unwrap())
info, err := c.GetGroupInfo(m.Head.GroupInfo.GroupCode.Unwrap())
c.Debug("sync group %v.", m.Head.GroupInfo.GetGroupCode())
info, err := c.GetGroupInfo(m.Head.GroupInfo.GetGroupCode())
if err != nil {
c.error("failed to sync group %v : %+v", m.Head.GroupInfo.GroupCode.Unwrap(), err)
c.Error("error to sync group %v : %+v", m.Head.GroupInfo.GetGroupCode(), err)
return nil
}
group = info
@ -351,7 +415,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("failed to sync group %v members : %+v", m.Head.GroupInfo.GroupCode, err)
c.Error("error to sync group %v member : %+v", m.Head.GroupInfo.GroupCode, err)
return nil
}
group.Members = mem
@ -374,20 +438,20 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
IsFriend: false,
}
} else {
mem := group.FindMember(m.Head.FromUin.Unwrap())
mem := group.FindMember(m.Head.GetFromUin())
if mem == nil {
group.Update(func(_ *GroupInfo) {
if mem = group.FindMemberWithoutLock(m.Head.FromUin.Unwrap()); mem != nil {
if mem = group.FindMemberWithoutLock(m.Head.GetFromUin()); mem != nil {
return
}
info, _ := c.GetMemberInfo(group.Code, m.Head.FromUin.Unwrap())
info, _ := c.GetMemberInfo(group.Code, m.Head.GetFromUin())
if info == nil {
return
}
mem = info
group.Members = append(group.Members, mem)
group.sort()
go c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{
Group: group,
Member: info,
})
@ -405,11 +469,11 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
}
var g *message.GroupMessage
g = &message.GroupMessage{
Id: m.Head.MsgSeq.Unwrap(),
Id: m.Head.GetMsgSeq(),
GroupCode: group.Code,
GroupName: string(m.Head.GroupInfo.GroupName),
Sender: sender,
Time: m.Head.MsgTime.Unwrap(),
Time: m.Head.GetMsgTime(),
Elements: message.ParseMessageElems(m.Body.RichText.Elems),
OriginalObject: m,
}
@ -417,14 +481,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.LongTextResid.Unwrap() != "" && len(g.Elements) == 1 {
if f := c.GetForwardMessage(elem.GeneralFlags.LongTextResid.Unwrap()); f != nil && len(f.Nodes) == 1 {
if elem.GeneralFlags != nil && elem.GeneralFlags.GetLongTextResid() != "" && len(g.Elements) == 1 {
if f := c.GetForwardMessage(elem.GeneralFlags.GetLongTextResid()); f != nil && len(f.Nodes) == 1 {
g = &message.GroupMessage{
Id: m.Head.MsgSeq.Unwrap(),
Id: m.Head.GetMsgSeq(),
GroupCode: group.Code,
GroupName: string(m.Head.GroupInfo.GroupName),
Sender: sender,
Time: m.Head.MsgTime.Unwrap(),
Time: m.Head.GetMsgTime(),
Elements: f.Nodes[0].Message,
OriginalObject: m,
}
@ -435,8 +499,8 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
}
}
if !sender.IsAnonymous() {
mem := group.FindMember(m.Head.FromUin.Unwrap())
groupCard := m.Head.GroupInfo.GroupCard.Unwrap()
mem := group.FindMember(m.Head.GetFromUin())
groupCard := m.Head.GroupInfo.GetGroupCard()
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 {
@ -455,7 +519,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
mem.CardName = groupCard
}
if old != mem.CardName {
c.MemberCardUpdatedEvent.dispatch(c, &MemberCardUpdatedEvent{
go c.dispatchMemberCardUpdatedEvent(&MemberCardUpdatedEvent{
Group: group,
OldCard: old,
Member: mem,
@ -464,95 +528,66 @@ 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.FileName.Unwrap(),
Name: m.Body.RichText.Ptt.GetFileName(),
Md5: m.Body.RichText.Ptt.FileMd5,
Size: m.Body.RichText.Ptt.FileSize.Unwrap(),
Url: url,
Size: m.Body.RichText.Ptt.GetFileSize(),
Url: "http://grouptalk.c2c.qq.com" + string(m.Body.RichText.Ptt.DownPara),
},
}
}
if m.Body.RichText.Attr != nil {
g.InternalId = m.Body.RichText.Attr.Random.Unwrap()
g.InternalId = m.Body.RichText.Attr.GetRandom()
}
return g
}
// SetEssenceMessage 设为群精华消息
func (c *QQClient) SetEssenceMessage(groupCode int64, msgID, msgInternalId int32) error {
r, err := c.sendAndWait(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 1))
r, err := c.callAndDecode(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 1))
if err != nil {
return errors.Wrap(err, "set essence msg network")
}
rsp := r.(*oidb.EACRspBody)
if rsp.ErrorCode.Unwrap() != 0 {
return errors.New(rsp.Wording.Unwrap())
if rsp.GetErrorCode() != 0 {
return errors.New(rsp.GetWording())
}
return nil
}
// DeleteEssenceMessage 移出群精华消息
func (c *QQClient) DeleteEssenceMessage(groupCode int64, msgID, msgInternalId int32) error {
r, err := c.sendAndWait(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 2))
r, err := c.callAndDecode(c.buildEssenceMsgOperatePacket(groupCode, uint32(msgID), uint32(msgInternalId), 2))
if err != nil {
return errors.Wrap(err, "set essence msg networ")
}
rsp := r.(*oidb.EACRspBody)
if rsp.ErrorCode.Unwrap() != 0 {
return errors.New(rsp.Wording.Unwrap())
if rsp.GetErrorCode() != 0 {
return errors.New(rsp.GetWording())
}
return nil
}
func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand, opType uint32) (uint16, []byte) {
func (c *QQClient) buildEssenceMsgOperatePacket(groupCode int64, msgSeq, msgRand, opType uint32) *network.Request {
commandName := "OidbSvc.0xeac_" + strconv.FormatInt(int64(opType), 10)
payload := c.packOIDBPackageProto(3756, int32(opType), &oidb.EACReqBody{ // serviceType 2 取消
GroupCode: proto.Uint64(uint64(groupCode)),
Seq: proto.Uint32(msgSeq),
Random: proto.Uint32(msgRand),
})
return c.uniPacket(commandName, payload)
return c.uniRequest(commandName, payload, decodeEssenceMsgResponse)
}
// OidbSvc.0xeac_1/2
func decodeEssenceMsgResponse(_ *QQClient, pkt *network.Packet) (any, error) {
func decodeEssenceMsgResponse(_ *QQClient, resp *network.Response) (interface{}, error) {
pkg := oidb.OIDBSSOPkg{}
rsp := &oidb.EACRspBody{}
err := unpackOIDBPackage(pkt.Payload, &rsp)
if err != nil {
return nil, err
if err := proto.Unmarshal(resp.Body, &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")
}
return rsp, nil
}

View File

@ -15,6 +15,7 @@ import (
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/client/pb/channel"
"github.com/Mrs4s/MiraiGo/utils"
)
@ -175,31 +176,31 @@ func (g *GuildInfo) removeChannel(id uint64) {
}
func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error) {
flags := proto.DynamicMessage{}
flags := binary.DynamicProtoMessage{}
for i := 3; i <= 29; i++ {
flags[uint64(i)] = 1
flags[uint64(i)] = uint32(1)
}
flags[99] = 1
flags[100] = 1
payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
flags[99] = uint32(1)
flags[100] = uint32(1)
payload := s.c.packOIDBPackageDynamically(3976, 1, binary.DynamicProtoMessage{
1: flags,
3: tinyId,
4: 0,
4: uint32(0),
})
rsp, err := s.c.sendAndWaitDynamic(s.c.uniPacket("OidbSvcTrpcTcp.0xfc9_1", payload))
rsp, err := s.c.uniCall("OidbSvcTrpcTcp.0xfc9_1", payload)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0Xfc9Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
// todo: 解析个性档案
return &GuildUserProfile{
TinyId: tinyId,
Nickname: body.Profile.Nickname.Unwrap(),
AvatarUrl: body.Profile.AvatarUrl.Unwrap(),
JoinTime: body.Profile.JoinTime.Unwrap(),
Nickname: body.Profile.GetNickname(),
AvatarUrl: body.Profile.GetAvatarUrl(),
JoinTime: body.Profile.GetJoinTime(),
}, nil
}
@ -207,92 +208,81 @@ func (s *GuildService) GetUserProfile(tinyId uint64) (*GuildUserProfile, error)
// 第一次请求: startIndex = 0 , roleIdIndex = 2 param = ""
// 后续请求请根据上次请求的返回值进行设置
func (s *GuildService) FetchGuildMemberListWithRole(guildId, channelId uint64, startIndex uint32, roleIdIndex uint64, param string) (*FetchGuildMemberListWithRoleResult, error) {
seq := s.c.nextSeq()
m := proto.DynamicMessage{
u1 := uint32(1)
m := binary.DynamicProtoMessage{
1: guildId, // guild id
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,
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,
},
6: startIndex,
8: 50, // count
8: uint32(50), // count
12: channelId,
}
if param != "" {
m[13] = param
}
m[14] = roleIdIndex
packet := s.c.uniPacketWithSeq(seq, "OidbSvcTrpcTcp.0xf5b_1", s.c.packOIDBPackageDynamically(3931, 1, m))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
rsp, err := s.c.uniCall("OidbSvcTrpcTcp.0xf5b_1", s.c.packOIDBPackageDynamically(3931, 1, m))
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0Xf5BRsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
var ret []*GuildMemberInfo
for _, memberWithRole := range body.MemberWithRoles {
for _, mem := range memberWithRole.Members {
ret = append(ret, &GuildMemberInfo{
TinyId: mem.TinyId.Unwrap(),
Title: mem.Title.Unwrap(),
Nickname: mem.Nickname.Unwrap(),
LastSpeakTime: mem.LastSpeakTime.Unwrap(),
Role: memberWithRole.RoleId.Unwrap(),
RoleName: memberWithRole.RoleName.Unwrap(),
TinyId: mem.GetTinyId(),
Title: mem.GetTitle(),
Nickname: mem.GetNickname(),
LastSpeakTime: mem.GetLastSpeakTime(),
Role: memberWithRole.GetRoleId(),
RoleName: memberWithRole.GetRoleName(),
})
}
}
for _, mem := range body.Members {
ret = append(ret, &GuildMemberInfo{
TinyId: mem.TinyId.Unwrap(),
Title: mem.Title.Unwrap(),
Nickname: mem.Nickname.Unwrap(),
LastSpeakTime: mem.LastSpeakTime.Unwrap(),
TinyId: mem.GetTinyId(),
Title: mem.GetTitle(),
Nickname: mem.GetNickname(),
LastSpeakTime: mem.GetLastSpeakTime(),
Role: 1,
RoleName: "普通成员",
})
}
return &FetchGuildMemberListWithRoleResult{
Members: ret,
NextIndex: body.NextIndex.Unwrap(),
NextRoleId: body.NextRoleIdIndex.Unwrap(),
NextQueryParam: body.NextQueryParam.Unwrap(),
Finished: body.NextIndex.IsNone(),
NextIndex: body.GetNextIndex(),
NextRoleId: body.GetNextRoleIdIndex(),
NextQueryParam: body.GetNextQueryParam(),
Finished: body.NextIndex == nil,
}, nil
}
// FetchGuildMemberProfileInfo 获取单个频道成员资料
func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*GuildUserProfile, error) {
seq := s.c.nextSeq()
flags := proto.DynamicMessage{}
flags := binary.DynamicProtoMessage{}
for i := 3; i <= 29; i++ {
flags[uint64(i)] = 1
flags[uint64(i)] = uint32(1)
}
flags[99] = 1
flags[100] = 1
payload := s.c.packOIDBPackageDynamically(3976, 1, proto.DynamicMessage{
flags[99] = uint32(1)
flags[100] = uint32(1)
payload := s.c.packOIDBPackageDynamically(3976, 1, binary.DynamicProtoMessage{
1: flags,
3: tinyId,
4: guildId,
})
packet := s.c.uniPacketWithSeq(seq, "OidbSvcTrpcTcp.0xf88_1", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
rsp, err := s.c.uniCall("OidbSvcTrpcTcp.0xf88_1", payload)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0Xf88Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
roles, err := s.fetchMemberRoles(guildId, tinyId)
@ -302,72 +292,73 @@ func (s *GuildService) FetchGuildMemberProfileInfo(guildId, tinyId uint64) (*Gui
// todo: 解析个性档案
return &GuildUserProfile{
TinyId: tinyId,
Nickname: body.Profile.Nickname.Unwrap(),
AvatarUrl: body.Profile.AvatarUrl.Unwrap(),
JoinTime: body.Profile.JoinTime.Unwrap(),
Nickname: body.Profile.GetNickname(),
AvatarUrl: body.Profile.GetAvatarUrl(),
JoinTime: body.Profile.GetJoinTime(),
Roles: roles,
}, nil
}
func (s *GuildService) GetGuildRoles(guildId uint64) ([]*GuildRole, error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1019_1",
s.c.packOIDBPackageDynamically(4121, 1, proto.DynamicMessage{1: guildId}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
req := s.c.uniRequest("OidbSvcTrpcTcp.0x1019_1",
s.c.packOIDBPackageDynamically(4121, 1, binary.DynamicProtoMessage{1: guildId}), nil)
rsp, err := s.c.call(req)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0X1019Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
roles := make([]*GuildRole, 0, len(body.Roles))
for _, role := range body.Roles {
roles := make([]*GuildRole, 0, len(body.GetRoles()))
for _, role := range body.GetRoles() {
roles = append(roles, &GuildRole{
RoleId: role.RoleId.Unwrap(),
RoleName: role.Name.Unwrap(),
ArgbColor: role.ArgbColor.Unwrap(),
Independent: role.Independent.Unwrap() == 1,
Num: role.Num.Unwrap(),
Owned: role.Owned.Unwrap() == 1,
Disabled: role.Disabled.Unwrap() == 1,
MaxNum: role.MaxNum.Unwrap(),
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(),
})
}
return roles, nil
}
func (s *GuildService) CreateGuildRole(guildId uint64, name string, color uint32, independent bool, initialUsers []uint64) (uint64, error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1016_1", s.c.packOIDBPackageDynamically(4118, 1, proto.DynamicMessage{
u1 := uint32(1)
req := s.c.uniRequest("OidbSvcTrpcTcp.0x1016_1", s.c.packOIDBPackageDynamically(4118, 1, binary.DynamicProtoMessage{
1: guildId,
2: proto.DynamicMessage{ // todo: 未知参数
1: 1,
2: 1,
3: 1,
2: binary.DynamicProtoMessage{ // todo: 未知参数
1: u1,
2: u1,
3: u1,
},
3: proto.DynamicMessage{
3: binary.DynamicProtoMessage{
1: name,
2: color,
3: independent,
},
4: initialUsers,
}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
}), nil)
rsp, err := s.c.call(req)
if err != nil {
return 0, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0X1016Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return 0, errors.Wrap(err, "decode packet error")
}
return body.RoleId.Unwrap(), nil
return body.GetRoleId(), nil
}
func (s *GuildService) DeleteGuildRole(guildId uint64, roleId uint64) error {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100e_1", s.c.packOIDBPackageDynamically(4110, 1, proto.DynamicMessage{
req := s.c.uniRequest("OidbSvcTrpcTcp.0x100e_1", s.c.packOIDBPackageDynamically(4110, 1, binary.DynamicProtoMessage{
1: guildId,
2: roleId,
}))
_, err := s.c.sendAndWaitDynamic(seq, packet)
}), nil)
_, err := s.c.call(req)
if err != nil {
return errors.Wrap(err, "send packet error")
}
@ -375,7 +366,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 := proto.DynamicMessage{
setOrRemove := binary.DynamicProtoMessage{
1: roleId,
}
if set {
@ -383,11 +374,11 @@ 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, proto.DynamicMessage{
req := s.c.uniRequest("OidbSvcTrpcTcp.0x101a_1", s.c.packOIDBPackageDynamically(4122, 1, binary.DynamicProtoMessage{
1: guildId,
2: setOrRemove,
}))
_, err := s.c.sendAndWaitDynamic(seq, packet)
}), nil)
_, err := s.c.call(req)
if err != nil {
return errors.Wrap(err, "send packet error")
}
@ -395,21 +386,22 @@ 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 {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x100d_1", s.c.packOIDBPackageDynamically(4109, 1, proto.DynamicMessage{
u1 := uint32(1)
req := s.c.uniRequest("OidbSvcTrpcTcp.0x100d_1", s.c.packOIDBPackageDynamically(4109, 1, binary.DynamicProtoMessage{
1: guildId,
2: roleId,
3: proto.DynamicMessage{
1: 1,
2: 1,
3: 1,
3: binary.DynamicProtoMessage{
1: u1,
2: u1,
3: u1,
},
4: proto.DynamicMessage{
4: binary.DynamicProtoMessage{
1: name,
2: color,
3: indepedent,
},
}))
_, err := s.c.sendAndWaitDynamic(seq, packet)
}), nil)
_, err := s.c.call(req)
if err != nil {
return errors.Wrap(err, "send packet error")
}
@ -417,56 +409,57 @@ func (s *GuildService) ModifyRoleInGuild(guildId uint64, roleId uint64, name str
}
func (s *GuildService) FetchGuestGuild(guildId uint64) (*GuildMeta, error) {
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,
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,
},
2: proto.DynamicMessage{
3: 1, 4: 1, 6: 1, 11: 1, 14: 1, 15: 1, 16: 1, 17: 1,
2: binary.DynamicProtoMessage{
3: u1, 4: u1, 6: u1, 11: u1, 14: u1, 15: u1, 16: u1, 17: u1,
},
},
2: proto.DynamicMessage{
2: binary.DynamicProtoMessage{
1: guildId,
},
})
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf57_9", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
req := s.c.uniRequest("OidbSvcTrpcTcp.0xf57_9", payload, nil)
rsp, err := s.c.call(req)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0Xf57Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
return &GuildMeta{
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(),
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(),
}, nil
}
func (s *GuildService) FetchChannelList(guildId uint64) (r []*ChannelInfo, e error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0xf5d_1",
req := s.c.uniRequest("OidbSvcTrpcTcp.0xf5d_1",
s.c.packOIDBPackageDynamically(3933, 1,
proto.DynamicMessage{
binary.DynamicProtoMessage{
1: guildId,
3: proto.DynamicMessage{
1: 1,
3: binary.DynamicProtoMessage{
1: uint32(1),
},
}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
}), nil)
rsp, err := s.c.call(req)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0Xf5DRsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
for _, info := range body.Rsp.Channels {
@ -476,13 +469,17 @@ 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, proto.DynamicMessage{1: guildId, 2: channelId}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
req := s.c.uniRequest("OidbSvcTrpcTcp.0xf55_1",
s.c.packOIDBPackageDynamically(3925, 1, binary.DynamicProtoMessage{
1: guildId,
2: channelId,
}), nil)
rsp, err := s.c.call(req)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0Xf55Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
return convertChannelInfo(body.Info), nil
@ -504,8 +501,8 @@ func (s *GuildService) GetTopicChannelFeeds(guildId, channelId uint64) ([]*topic
Count: proto.Uint32(12),
From: proto.Uint32(0),
ChannelSign: &channel.StChannelSign{
GuildId: proto.Some(guildId),
ChannelId: proto.Some(channelId),
GuildId: &guildId,
ChannelId: &channelId,
},
FeedAttchInfo: proto.String(""), // isLoadMore
})
@ -530,14 +527,14 @@ func (s *GuildService) GetTopicChannelFeeds(guildId, channelId uint64) ([]*topic
},
},
})
seq, packet := s.c.uniPacket("QChannelSvr.trpc.qchannel.commreader.ComReader.GetChannelTimelineFeeds", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
call := s.c.uniRequest("QChannelSvr.trpc.qchannel.commreader.ComReader.GetChannelTimelineFeeds", payload, nil)
rsp, err := s.c.call(call)
if err != nil {
return nil, errors.New("send packet error")
}
pkg := new(qweb.QWebRsp)
body := new(channel.StGetChannelFeedsRsp)
if err = proto.Unmarshal(rsp, pkg); err != nil {
if err = proto.Unmarshal(rsp.Body, pkg); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil {
@ -600,53 +597,53 @@ func (s *GuildService) PostTopicChannelFeed(guildId, channelId uint64, feed *top
},
},
})
seq, packet := s.c.uniPacket("QChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed", payload)
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
rsp, err := s.c.call(s.c.uniRequest("QChannelSvr.trpc.qchannel.commwriter.ComWriter.PublishFeed", payload, nil))
if err != nil {
return errors.New("send packet error")
}
pkg := new(qweb.QWebRsp)
body := new(channel.StPublishFeedRsp)
if err = proto.Unmarshal(rsp, pkg); err != nil {
if err = proto.Unmarshal(rsp.Body, pkg); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message")
}
if err = proto.Unmarshal(pkg.BusiBuff, body); err != nil {
return errors.Wrap(err, "failed to unmarshal protobuf message")
}
if body.Feed != nil && body.Feed.Id.IsNone() {
if body.Feed != nil && body.Feed.Id != nil {
return nil
}
return errors.New("post feed error")
}
func (s *GuildService) fetchMemberRoles(guildId uint64, tinyId uint64) ([]*GuildRole, error) {
seq, packet := s.c.uniPacket("OidbSvcTrpcTcp.0x1017_1", s.c.packOIDBPackageDynamically(4119, 1, proto.DynamicMessage{
u1 := uint32(1)
req := s.c.uniRequest("OidbSvcTrpcTcp.0x1017_1", s.c.packOIDBPackageDynamically(4119, 1, binary.DynamicProtoMessage{
1: guildId,
2: tinyId,
4: proto.DynamicMessage{
1: 1,
2: 1,
3: 1,
4: binary.DynamicProtoMessage{
1: u1,
2: u1,
3: u1,
},
}))
rsp, err := s.c.sendAndWaitDynamic(seq, packet)
}), nil)
rsp, err := s.c.call(req)
if err != nil {
return nil, errors.Wrap(err, "send packet error")
}
body := new(channel.ChannelOidb0X1017Rsp)
if err = unpackOIDBPackage(rsp, body); err != nil {
if err = unpackOIDBPackage(rsp.Body, body); err != nil {
return nil, errors.Wrap(err, "decode packet error")
}
p1 := body.P1
p1 := body.GetP1()
if p1 == nil {
return nil, errors.New("packet OidbSvcTrpcTcp.0x1017_1: decode p1 error")
}
roles := make([]*GuildRole, 0, len(p1.Roles))
for _, role := range p1.Roles {
roles := make([]*GuildRole, 0, len(p1.GetRoles()))
for _, role := range p1.GetRoles() {
roles = append(roles, &GuildRole{
RoleId: role.RoleId.Unwrap(),
RoleName: role.Name.Unwrap(),
ArgbColor: role.ArgbColor.Unwrap(),
RoleId: role.GetRoleId(),
RoleName: role.GetName(),
ArgbColor: role.GetArgbColor(),
})
}
return roles, nil
@ -659,8 +656,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.DynamicMessage{
1: binary.DynamicMessage{
payload := s.c.packOIDBPackageDynamically(4104, 1, binary.DynamicProtoMessage{
1: binary.DynamicProtoMessage{
1: guildId,
2: ids,
},
@ -679,96 +676,96 @@ func (s *GuildService) fetchChannelListState(guildId uint64, channels []*Channel
func convertChannelInfo(info *channel.GuildChannelInfo) *ChannelInfo {
meta := &ChannelMeta{
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(),
CreatorUin: info.GetCreatorUin(),
CreatorTinyId: info.GetCreatorTinyId(),
CreateTime: info.GetCreateTime(),
GuildId: info.GetGuildId(),
VisibleType: info.GetVisibleType(),
CurrentSlowMode: info.GetCurrentSlowModeKey(),
TalkPermission: info.GetTalkPermission(),
}
if info.TopMsg != nil {
meta.TopMessageSeq = info.TopMsg.TopMsgSeq.Unwrap()
meta.TopMessageTime = info.TopMsg.TopMsgTime.Unwrap()
meta.TopMessageOperatorId = info.TopMsg.TopMsgOperatorTinyId.Unwrap()
meta.TopMessageSeq = info.TopMsg.GetTopMsgSeq()
meta.TopMessageTime = info.TopMsg.GetTopMsgTime()
meta.TopMessageOperatorId = info.TopMsg.GetTopMsgOperatorTinyId()
}
for _, slow := range info.SlowModeInfos {
meta.SlowModes = append(meta.SlowModes, &ChannelSlowModeInfo{
SlowModeKey: slow.SlowModeKey.Unwrap(),
SpeakFrequency: slow.SpeakFrequency.Unwrap(),
SlowModeCircle: slow.SlowModeCircle.Unwrap(),
SlowModeText: slow.SlowModeText.Unwrap(),
SlowModeKey: slow.GetSlowModeKey(),
SpeakFrequency: slow.GetSpeakFrequency(),
SlowModeCircle: slow.GetSlowModeCircle(),
SlowModeText: slow.GetSlowModeText(),
})
}
return &ChannelInfo{
ChannelId: info.ChannelId.Unwrap(),
ChannelName: info.ChannelName.Unwrap(),
NotifyType: uint32(info.FinalNotifyType.Unwrap()),
ChannelType: ChannelType(info.ChannelType.Unwrap()),
ChannelId: info.GetChannelId(),
ChannelName: info.GetChannelName(),
NotifyType: uint32(info.GetFinalNotifyType()),
ChannelType: ChannelType(info.GetChannelType()),
Meta: meta,
fetchTime: time.Now().Unix(),
}
}
func (c *QQClient) syncChannelFirstView() {
rsp, err := c.sendAndWaitDynamic(c.buildSyncChannelFirstViewPacket())
rsp, err := c.call(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 {
if err = proto.Unmarshal(rsp.Body, firstViewRsp); err != nil {
return
}
c.GuildService.TinyId = firstViewRsp.SelfTinyid.Unwrap()
c.GuildService.GuildCount = firstViewRsp.GuildCount.Unwrap()
c.GuildService.TinyId = firstViewRsp.GetSelfTinyid()
c.GuildService.GuildCount = firstViewRsp.GetGuildCount()
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)
}
}
func (c *QQClient) buildSyncChannelFirstViewPacket() (uint16, []byte) {
func (c *QQClient) buildSyncChannelFirstViewPacket() *network.Request {
req := &channel.FirstViewReq{
LastMsgTime: proto.Uint64(0),
Seq: proto.Uint32(0),
DirectMessageFlag: proto.Uint32(1),
}
payload, _ := proto.Marshal(req)
return c.uniPacket("trpc.group_pro.synclogic.SyncLogic.SyncFirstView", payload)
return c.uniRequest("trpc.group_pro.synclogic.SyncLogic.SyncFirstView", payload, nil)
}
func decodeGuildPushFirstView(c *QQClient, pkt *network.Packet) (any, error) {
func decodeGuildPushFirstView(c *QQClient, resp *network.Response) (interface{}, error) {
firstViewMsg := new(channel.FirstViewMsg)
if err := proto.Unmarshal(pkt.Payload, firstViewMsg); err != nil {
if err := proto.Unmarshal(resp.Body, 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.GuildId.Unwrap(),
GuildCode: guild.GuildCode.Unwrap(),
GuildId: guild.GetGuildId(),
GuildCode: guild.GetGuildCode(),
GuildName: utils.B2S(guild.GuildName),
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()),
CoverUrl: fmt.Sprintf("https://groupprocover-76483.picgzc.qpic.cn/%v", guild.GetGuildId()),
AvatarUrl: fmt.Sprintf("https://groupprohead-76292.picgzc.qpic.cn/%v", guild.GetGuildId()),
}
channels, err := c.GuildService.FetchChannelList(info.GuildId)
if err != nil {
c.warning("warning: fetch guild %v channel error %v. will use sync node to fill channel list field", guild.GuildId, err)
c.Warning("waring: 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.ChannelId.Unwrap(),
ChannelId: node.GetChannelId(),
ChannelName: utils.B2S(node.ChannelName),
Time: node.Time.Unwrap(),
EventTime: node.EventTime.Unwrap(),
NotifyType: node.NotifyType.Unwrap(),
ChannelType: ChannelType(node.ChannelType.Unwrap()),
AtAllSeq: meta.AtAllSeq.Unwrap(),
Time: node.GetTime(),
EventTime: node.GetEventTime(),
NotifyType: node.GetNotifyType(),
ChannelType: ChannelType(node.GetChannelType()),
AtAllSeq: meta.GetAtAllSeq(),
})
}
} else {
@ -778,7 +775,7 @@ func decodeGuildPushFirstView(c *QQClient, pkt *network.Packet) (any, error) {
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, pkt *network.Packet) (any, error) {
func decodeGuildEventFlowPacket(c *QQClient, resp *network.Response) (interface{}, error) {
push := new(channel.MsgOnlinePush)
if err := proto.Unmarshal(pkt.Payload, push); err != nil {
if err := proto.Unmarshal(resp.Body, push); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if push.CompressFlag.Unwrap() == 1 && len(push.CompressMsg) > 0 {
if push.GetCompressFlag() == 1 && len(push.CompressMsg) > 0 {
press := new(channel.PressMsg)
dst := make([]byte, len(push.CompressMsg)*2)
i, err := lz4.UncompressBlock(push.CompressMsg, dst)
@ -49,13 +49,10 @@ func decodeGuildEventFlowPacket(c *QQClient, pkt *network.Packet) (any, error) {
push.Msgs = press.Msgs
}
for _, m := range push.Msgs {
if m.Head == nil {
continue
}
if m.Head.ContentHead.Type.Unwrap() == 3841 {
if m.Head.ContentHead.GetType() == 3841 {
// todo: 回头 event flow 的处理移出去重构下逻辑, 先暂时这样方便改
var common *msg.CommonElem
if m.Body != nil && m.Body.RichText != nil {
if m.Body != nil {
for _, e := range m.Body.RichText.Elems {
if e.CommonElem != nil {
common = e.CommonElem
@ -63,13 +60,13 @@ func decodeGuildEventFlowPacket(c *QQClient, pkt *network.Packet) (any, error) {
}
}
}
if m.Head.ContentHead.SubType.Unwrap() == 2 { // todo: tips?
// if common == nil { // empty tips
// }
if m.Head.ContentHead.GetSubType() == 2 { // todo: tips?
if common == nil { // empty tips
}
tipsInfo := &tipsPushInfo{
TinyId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: m.Head.RoutingHead.ChannelId.Unwrap(),
TinyId: m.Head.RoutingHead.GetFromTinyid(),
GuildId: m.Head.RoutingHead.GetGuildId(),
ChannelId: m.Head.RoutingHead.GetChannelId(),
}
/*
if len(m.CtrlHead.IncludeUin) > 0 {
@ -78,23 +75,23 @@ func decodeGuildEventFlowPacket(c *QQClient, pkt *network.Packet) (any, error) {
*/
return tipsInfo, nil
}
if common == nil || common.ServiceType.Unwrap() != 500 {
if common == nil || common.GetServiceType() != 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.Type.Unwrap() == 3840 {
if m.Head.RoutingHead.DirectMessageFlag.Unwrap() == 1 {
if m.Head.ContentHead.GetType() == 3840 {
if m.Head.RoutingHead.GetDirectMessageFlag() == 1 {
// todo: direct message decode
continue
}
if m.Head.RoutingHead.FromTinyid.Unwrap() == c.GuildService.TinyId {
if m.Head.RoutingHead.GetFromTinyid() == c.GuildService.TinyId {
continue
}
if cm := c.GuildService.parseGuildChannelMessage(m); cm != nil {
@ -107,39 +104,39 @@ func decodeGuildEventFlowPacket(c *QQClient, pkt *network.Packet) (any, error) {
func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody *channel.EventBody) {
var guild *GuildInfo
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.")
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.")
return
}
}
switch {
case eventBody.CreateChan != nil:
for _, chanId := range eventBody.CreateChan.CreateId {
if guild.FindChannel(chanId.ChanId.Unwrap()) != nil {
if guild.FindChannel(chanId.GetChanId()) != nil {
continue
}
channelInfo, err := c.GuildService.FetchChannelInfo(guild.GuildId, chanId.ChanId.Unwrap())
channelInfo, err := c.GuildService.FetchChannelInfo(guild.GuildId, chanId.GetChanId())
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.FromTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
OperatorId: m.Head.RoutingHead.GetFromTinyid(),
GuildId: m.Head.RoutingHead.GetGuildId(),
ChannelInfo: channelInfo,
})
}
case eventBody.DestroyChan != nil:
for _, chanId := range eventBody.DestroyChan.DeleteId {
channelInfo := guild.FindChannel(chanId.ChanId.Unwrap())
channelInfo := guild.FindChannel(chanId.GetChanId())
if channelInfo == nil {
continue
}
guild.removeChannel(chanId.ChanId.Unwrap())
guild.removeChannel(chanId.GetChanId())
c.dispatchGuildChannelDestroyedEvent(&GuildChannelOperationEvent{
OperatorId: m.Head.RoutingHead.FromTinyid.Unwrap(),
OperatorId: m.Head.RoutingHead.GetFromTinyid(),
GuildId: guild.GuildId,
ChannelInfo: channelInfo,
})
@ -147,11 +144,11 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody
case eventBody.ChangeChanInfo != nil:
updateChanLock.Lock()
defer updateChanLock.Unlock()
oldInfo := guild.FindChannel(eventBody.ChangeChanInfo.ChanId.Unwrap())
oldInfo := guild.FindChannel(eventBody.ChangeChanInfo.GetChanId())
if oldInfo == nil {
info, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GuildId.Unwrap(), eventBody.ChangeChanInfo.ChanId.Unwrap())
info, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GetGuildId(), eventBody.ChangeChanInfo.GetChanId())
if err != nil {
c.error("failed to decode channel info updated event: fetch channel info failed: %v", err)
c.Error("error to decode channel info updated event: fetch channel info failed: %v", err)
return
}
guild.Channels = append(guild.Channels, info)
@ -160,9 +157,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.GuildId.Unwrap(), eventBody.ChangeChanInfo.ChanId.Unwrap())
newInfo, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GetGuildId(), eventBody.ChangeChanInfo.GetChanId())
if err != nil {
c.error("failed to decode channel info updated event: fetch channel info failed: %v", err)
c.Error("error to decode channel info updated event: fetch channel info failed: %v", err)
return
}
for i := range guild.Channels {
@ -172,22 +169,22 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody
}
}
c.dispatchGuildChannelUpdatedEvent(&GuildChannelUpdatedEvent{
OperatorId: m.Head.RoutingHead.FromTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: eventBody.ChangeChanInfo.ChanId.Unwrap(),
OperatorId: m.Head.RoutingHead.GetFromTinyid(),
GuildId: m.Head.RoutingHead.GetGuildId(),
ChannelId: eventBody.ChangeChanInfo.GetChanId(),
OldChannelInfo: oldInfo,
NewChannelInfo: newInfo,
})
case eventBody.JoinGuild != nil:
/* 应该不会重复推送把, 不会吧不会吧
if mem := guild.FindMember(eventBody.JoinGuild.MemberTinyid.Unwrap()); mem != nil {
c.info("ignore join guild event: member %v already exists", mem.TinyId)
if mem := guild.FindMember(eventBody.JoinGuild.GetMemberTinyid()); mem != nil {
c.Info("ignore join guild event: member %v already exists", mem.TinyId)
return
}
*/
profile, err := c.GuildService.FetchGuildMemberProfileInfo(guild.GuildId, eventBody.JoinGuild.MemberTinyid.Unwrap())
profile, err := c.GuildService.FetchGuildMemberProfileInfo(guild.GuildId, eventBody.JoinGuild.GetMemberTinyid())
if err != nil {
c.error("failed to decode member join guild event: get member profile error: %v", err)
c.Error("error to decode member join guild event: get member profile error: %v", err)
return
}
info := &GuildMemberInfo{
@ -200,33 +197,33 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody
Member: info,
})
case eventBody.UpdateMsg != nil:
if eventBody.UpdateMsg.EventType.Unwrap() == 1 || eventBody.UpdateMsg.EventType.Unwrap() == 2 {
if eventBody.UpdateMsg.GetEventType() == 1 || eventBody.UpdateMsg.GetEventType() == 2 {
c.dispatchGuildMessageRecalledEvent(&GuildMessageRecalledEvent{
OperatorId: eventBody.UpdateMsg.OperatorTinyid.Unwrap(),
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: m.Head.RoutingHead.ChannelId.Unwrap(),
MessageId: eventBody.UpdateMsg.MsgSeq.Unwrap(),
RecallTime: int64(m.Head.ContentHead.Time.Unwrap()),
OperatorId: eventBody.UpdateMsg.GetOperatorTinyid(),
GuildId: m.Head.RoutingHead.GetGuildId(),
ChannelId: m.Head.RoutingHead.GetChannelId(),
MessageId: eventBody.UpdateMsg.GetMsgSeq(),
RecallTime: int64(m.Head.ContentHead.GetTime()),
})
return
}
if eventBody.UpdateMsg.EventType.Unwrap() == 4 { // 消息贴表情更新 (包含添加或删除)
t, err := c.GuildService.pullChannelMessages(m.Head.RoutingHead.GuildId.Unwrap(), m.Head.RoutingHead.ChannelId.Unwrap(), eventBody.UpdateMsg.MsgSeq.Unwrap(), eventBody.UpdateMsg.MsgSeq.Unwrap(), eventBody.UpdateMsg.EventVersion.Unwrap()-1, false)
if 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 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.FromTinyid.Unwrap() == c.GuildService.TinyId {
if t[0].Head.RoutingHead.GetFromTinyid() == c.GuildService.TinyId {
return
}
updatedEvent := &GuildMessageReactionsUpdatedEvent{
GuildId: m.Head.RoutingHead.GuildId.Unwrap(),
ChannelId: m.Head.RoutingHead.ChannelId.Unwrap(),
MessageId: t[0].Head.ContentHead.Seq.Unwrap(),
GuildId: m.Head.RoutingHead.GetGuildId(),
ChannelId: m.Head.RoutingHead.GetChannelId(),
MessageId: t[0].Head.ContentHead.GetSeq(),
CurrentReactions: decodeGuildMessageEmojiReactions(t[0]),
}
tipsInfo, err := c.waitPacketTimeoutSyncF("MsgPush.PushGroupProMsg", time.Second, func(i any) bool {
tipsInfo, err := c.waitPacketTimeoutSyncF("MsgPush.PushGroupProMsg", time.Second, func(i interface{}) bool {
if i == nil {
return false
}

View File

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

View File

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

View File

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

View File

@ -1,155 +1,15 @@
package auth
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/pkg/errors"
)
//go:generate stringer -type=ProtocolType -linecomment
type ProtocolType int
//go:generate stringer -type=Protocol -linecomment
type Protocol int
const (
Unset ProtocolType = iota
AndroidPhone // Android Phone
AndroidWatch // Android Watch
MacOS // MacOS
QiDian // 企点
IPad // iPad
AndroidPad // Android Pad
)
var (
AppVersions = map[ProtocolType]*AppVersion{
AndroidPhone: {
ApkId: "com.tencent.mobileqq",
AppId: 537164840,
SubAppId: 537164840,
AppKey: "0S200MNJT807V3GE",
SortVersionName: "8.9.63.11390",
BuildTime: 1685069178,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2546",
SSOVersion: 20,
MiscBitmap: 150470524,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
QUA: "V1_AND_SQ_8.9.63_4194_YYB_D",
Protocol: AndroidPhone,
},
AndroidPad: {
ApkId: "com.tencent.mobileqq",
AppId: 537164888,
SubAppId: 537164888,
AppKey: "0S200MNJT807V3GE",
SortVersionName: "8.9.63.11390",
BuildTime: 1685069178,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2546",
SSOVersion: 20,
MiscBitmap: 150470524,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
QUA: "V1_AND_SQ_8.9.63_4194_YYB_D",
Protocol: AndroidPad,
},
AndroidWatch: {
ApkId: "com.tencent.qqlite",
AppId: 537065138,
SubAppId: 537065138,
SortVersionName: "2.0.8",
BuildTime: 1559564731,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2365",
SSOVersion: 5,
MiscBitmap: 16252796,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
Protocol: AndroidWatch,
},
IPad: {
ApkId: "com.tencent.minihd.qq",
AppId: 537151363,
SubAppId: 537151363,
SortVersionName: "8.9.33.614",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 19,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_VKEY | WLOGIN_D2 | WLOGIN_SID | WLOGIN_PSKEY, // 1970400
Protocol: IPad,
},
MacOS: {
ApkId: "com.tencent.minihd.qq",
AppId: 537128930,
SubAppId: 537128930,
SortVersionName: "5.8.9",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_VKEY | WLOGIN_D2 | WLOGIN_SID | WLOGIN_PSKEY, // 1970400
Protocol: MacOS,
},
QiDian: {
ApkId: "com.tencent.qidian",
AppId: 537096038,
SubAppId: 537036590,
SortVersionName: "5.0.0",
BuildTime: 1630062176,
ApkSign: []byte{160, 30, 236, 171, 133, 233, 227, 186, 43, 15, 106, 21, 140, 133, 92, 41},
SdkVersion: "6.0.0.2484",
SSOVersion: 18,
MiscBitmap: 184024956,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_D2 | WLOGIN_PSKEY | WLOGIN_DA2, // 34869472
Protocol: QiDian,
},
}
)
// see oicq/wlogin_sdk/request/WtloginHelper.java class SigType
const (
_ = 1 << iota
WLOGIN_A5
_
_
WLOGIN_RESERVED
WLOGIN_STWEB
WLOGIN_A2
WLOGIN_ST
_
WLOGIN_LSKEY
_
_
WLOGIN_SKEY
WLOGIN_SIG64
WLOGIN_OPENKEY
WLOGIN_TOKEN
_
WLOGIN_VKEY
WLOGIN_D2
WLOGIN_SID
WLOGIN_PSKEY
WLOGIN_AQSIG
WLOGIN_LHSIG
WLOGIN_PAYTOKEN
WLOGIN_PF
WLOGIN_DA2
WLOGIN_QRPUSH
WLOGIN_PT4Token
Unset Protocol = iota
AndroidPhone // Android Phone
AndroidWatch // Android Watch
MacOS // MacOS
QiDian // 企点
IPad // iPad
)
type AppVersion struct {
@ -159,47 +19,95 @@ type AppVersion struct {
SdkVersion string
AppId uint32
SubAppId uint32
AppKey string
BuildTime uint32
SSOVersion uint32
MiscBitmap uint32
SubSigmap uint32
MainSigMap uint32
QUA string
Protocol ProtocolType
Protocol Protocol
}
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")
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,
}
}
// 按 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
@ -229,7 +137,6 @@ type SigInfo struct {
G []byte
T402 []byte
RandSeed []byte // t403
T547 []byte
// rollbackSig []byte
// t149 []byte
// t150 []byte
@ -242,20 +149,3 @@ 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,9 +45,7 @@ type Device struct {
VendorOSName []byte
Guid []byte
TgtgtKey []byte
QImei16 string
QImei36 string
Protocol ProtocolType
Protocol Protocol
Version *OSVersion
}
@ -136,22 +134,13 @@ func (info *Device) ReadJson(d []byte) error {
}
switch f.Protocol {
case 1, 2, 3, 4, 5, 6:
info.Protocol = ProtocolType(f.Protocol)
case 1, 2, 3, 4, 5:
info.Protocol = Protocol(f.Protocol)
default:
info.Protocol = AndroidPad
info.Protocol = IPad
}
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

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

View File

@ -0,0 +1,28 @@
// 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

@ -1,29 +0,0 @@
// 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

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

View File

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

View File

@ -1,35 +0,0 @@
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,10 +1,12 @@
package highway
import (
"bytes"
"crypto/md5"
"fmt"
"io"
"net"
"runtime"
"sync"
"net/http"
"sync/atomic"
"time"
@ -13,59 +15,26 @@ 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),
@ -73,6 +42,162 @@ 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)
}
@ -82,7 +207,7 @@ func (s *Session) sendHeartbreak(conn net.Conn) error {
MsgBasehead: &pb.DataHighwayHead{
Version: 1,
Uin: s.Uin,
Command: _REQ_CMD_HEART_BREAK,
Command: "PicUp.Echo",
Seq: s.nextSeq(),
Appid: s.AppID,
Dataflag: 4096,
@ -90,170 +215,49 @@ func (s *Session) sendHeartbreak(conn net.Conn) error {
LocaleId: 2052,
},
})
buffers := frame(head, nil)
_, err := buffers.WriteTo(conn)
w := binary.SelectWriter()
writeHeadBody(w, head, nil)
_, err := conn.Write(w.Bytes())
binary.PutWriter(w)
return err
}
func (s *Session) ping(pc *persistConn) error {
start := time.Now()
err := s.sendHeartbreak(pc.conn)
func (s *Session) sendEcho(conn net.Conn) error {
err := s.sendHeartbreak(conn)
if err != nil {
return errors.Wrap(err, "echo error")
}
if _, err = readResponse(binary.NewNetworkReader(pc.conn)); err != nil {
if _, _, err = readResponse(binary.NewNetworkReader(conn)); err != nil {
return errors.Wrap(err, "echo error")
}
// update delay
pc.ping = time.Since(start).Milliseconds()
return nil
}
func readResponse(r *binary.NetworkReader) (*pb.RspDataHighwayHead, error) {
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) {
_, err := r.ReadByte()
if err != nil {
return nil, errors.Wrap(err, "failed to read byte")
return nil, nil, errors.Wrap(err, "failed to read byte")
}
hl, _ := r.ReadInt32()
a2, _ := r.ReadInt32()
if hl > highwayMaxResponseSize || a2 > highwayMaxResponseSize {
return nil, errors.Errorf("highway response invild. head size: %v body size: %v", hl, a2)
return nil, nil, errors.Errorf("highway response invild. head size: %v body size: %v", hl, a2)
}
head, _ := r.ReadBytes(int(hl))
_, _ = r.ReadBytes(int(a2)) // skip payload
payload, _ := r.ReadBytes(int(a2))
_, _ = r.ReadByte()
rsp := new(pb.RspDataHighwayHead)
if err = proto.Unmarshal(head, rsp); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
return nil, nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
return rsp, nil
}
type persistConn struct {
conn net.Conn
addr Addr
ping int64 // echo ping
}
const maxIdleConn = 7
type idle struct {
pc persistConn
next *idle
}
// getIdleConn ...
func (s *Session) getIdleConn() persistConn {
s.idleMu.Lock()
defer s.idleMu.Unlock()
// no idle
if s.idle == nil {
return persistConn{}
}
// switch the fastest idle conn
conn := s.idle.pc
s.idle = s.idle.next
s.idleCount--
if s.idleCount < 0 {
panic("idle count underflow")
}
return conn
}
func (s *Session) putIdleConn(pc persistConn) {
s.idleMu.Lock()
defer s.idleMu.Unlock()
// check persistConn
if pc.conn == nil || pc.addr.empty() {
panic("put bad idle conn")
}
cur := &idle{pc: pc}
s.idleCount++
if s.idle == nil { // quick path
s.idle = cur
return
}
// insert between pre and succ
var pre, succ *idle
succ = s.idle
for succ != nil && succ.pc.ping < pc.ping { // keep idle list sorted by delay incremental
pre = succ
succ = succ.next
}
if pre != nil {
pre.next = cur
}
cur.next = succ
// remove the slowest idle conn if idle count greater than maxIdleConn
if s.idleCount > maxIdleConn {
for cur.next != nil {
pre = cur
cur = cur.next
}
pre.next = nil
s.idleCount--
}
}
func (s *Session) connect(addr Addr) (persistConn, error) {
conn, err := net.DialTimeout("tcp", addr.String(), time.Second*3)
if err != nil {
return persistConn{}, err
}
_ = conn.(*net.TCPConn).SetKeepAlive(true)
// close conn
runtime.SetFinalizer(conn, func(conn net.Conn) {
_ = conn.Close()
})
pc := persistConn{conn: conn, addr: addr}
if err = s.ping(&pc); err != nil {
return persistConn{}, err
}
return pc, nil
}
func (s *Session) nextAddr() Addr {
s.addrMu.Lock()
defer s.addrMu.Unlock()
addr := s.SsoAddr[s.idx]
s.idx = (s.idx + 1) % len(s.SsoAddr)
return addr
}
func (s *Session) selectConn() (pc persistConn, err error) {
for { // select from idle pc
pc = s.getIdleConn()
if pc.conn == nil {
// no idle connection
break
}
err = s.ping(&pc) // ping
if err == nil {
return
}
}
try := 0
for {
addr := s.nextAddr()
pc, err = s.connect(addr)
if err == nil {
break
}
try++
if try > 5 {
break
}
}
return
return rsp, payload, nil
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,117 +1,74 @@
package network
import (
goBinary "encoding/binary"
"fmt"
"io"
"net"
"strconv"
"strings"
"sync"
"github.com/pkg/errors"
"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
// connection
// conn *TCPClient
connMu sync.Mutex
servers []*net.TCPAddr
curServerAddr *net.TCPAddr
conn TCPListener
}
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.0x126d_200
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) AddServerAddr(addr *net.TCPAddr) {
t.connMu.Lock()
defer t.connMu.Unlock()
t.servers = append(t.servers, addr)
}
func (t *Transport) GetServerAddr() *net.TCPAddr {
t.connMu.Lock()
defer t.connMu.Unlock()
return t.curServerAddr
}
func (t *Transport) ServerCount() int {
t.connMu.Lock()
defer t.connMu.Unlock()
return len(t.servers)
}
func (t *Transport) PlannedDisconnect(fun func(*TCPListener)) {
t.conn.PlannedDisconnect = fun
}
func (t *Transport) UnexpectedDisconnect(fun func(*TCPListener, error)) {
t.conn.UnexpectedDisconnect = fun
}
func (t *Transport) ConnectFastest() (chosen *net.TCPAddr, err error) {
t.connMu.Lock()
defer t.connMu.Unlock()
chosen, err = t.conn.ConnectFastest(t.servers)
t.curServerAddr = chosen
return
}
func (t *Transport) Close() {
t.conn.Close()
}
func (t *Transport) Write(data []byte) error {
return t.conn.Write(data)
}
func (t *Transport) packBody(req *Request, w *binary.Writer) {
pos := w.FillUInt32()
@ -132,58 +89,19 @@ 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)
}
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.WriteUInt32(0x04)
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
@ -222,7 +140,6 @@ 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)
@ -231,3 +148,59 @@ func (t *Transport) PackPacket(req *Request) []byte {
w.WriteUInt32At(pos, uint32(w.Len()))
return append([]byte(nil), w.Bytes()...)
}
type PktHandler func(pkt *Request, netErr error)
type RequestHandler func(head []byte) (*Request, error)
func (t *Transport) NetLoop(pktHandler PktHandler, respHandler RequestHandler) {
go t.netLoop(pktHandler, respHandler)
}
// readPacket 帮助函数(Helper function)
func readPacket(conn *net.TCPConn, minSize, maxSize uint32) ([]byte, error) {
lBuf := make([]byte, 4)
_, err := io.ReadFull(conn, lBuf)
if err != nil {
return nil, err
}
l := goBinary.BigEndian.Uint32(lBuf)
if l < minSize || l > maxSize {
return nil, fmt.Errorf("parse incoming packet error: invalid packet length %v", l)
}
data := make([]byte, l-4)
_, err = io.ReadFull(conn, data)
return data, err
}
// netLoop 整个函数周期使用同一个连接,确保不会发生串线这种奇怪的事情
func (t *Transport) netLoop(pktHandler PktHandler, respHandler RequestHandler) {
conn := t.conn.getConn()
defer func() {
if r := recover(); r != nil {
pktHandler(nil, fmt.Errorf("panic: %v", r))
}
t.conn.Close()
}()
errCount := 0
for {
data, err := readPacket(conn, 4, 10<<20) // max 10MB
if err != nil {
// 在且仅在没有新连接建立时断线才被认为是意外的
if t.conn.getConn() == conn {
pktHandler(nil, errors.Wrap(ErrConnectionBroken, err.Error()))
}
return
}
req, err := respHandler(data)
if err == nil {
errCount = 0
goto ok
}
errCount++
if errCount > 2 {
err = errors.Wrap(ErrConnectionBroken, err.Error())
}
ok:
go pktHandler(req, err)
}
}

View File

@ -1,16 +1,17 @@
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 *session
ecdh *crypto.ECDH
randomKey []byte
WtSessionTicketKey []byte
@ -18,11 +19,11 @@ type Codec struct {
func NewCodec(uin int64) *Codec {
c := &Codec{
ecdh: newSession(),
ecdh: crypto.NewECDH(),
randomKey: make([]byte, 16),
}
rand.Read(c.randomKey)
c.ecdh.fetchPubKey(uin)
c.ecdh.FetchPubKey(uin)
return c
}
@ -83,8 +84,7 @@ func (c *Codec) Marshal(m *Message) []byte {
}
w.WriteByte(0x03)
buf := make([]byte, len(w.Bytes()))
copy(buf, w.Bytes())
buf := append([]byte(nil), w.Bytes()...)
goBinary.BigEndian.PutUint16(buf[1:3], uint16(len(buf)))
return buf
}
@ -110,13 +110,16 @@ func (c *Codec) Unmarshal(data []byte) (*Message, error) {
reader.ReadByte()
switch encryptType {
case 0:
d := reader.ReadBytes(reader.Len() - 1)
defer func() {
if pan := recover(); pan != nil {
m.Body = binary.NewTeaCipher(c.randomKey).Decrypt(d)
}
m.Body = 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)
}()
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)
@ -125,25 +128,3 @@ 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

@ -1,105 +0,0 @@
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
}

View File

@ -1,43 +0,0 @@
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

@ -1,43 +0,0 @@
// 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,8 +48,9 @@ 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) {
@ -59,7 +60,7 @@ func (c *QQClient) GetModelShow(modelName string) ([]*ModelVariant, error) {
Uin: c.Uin,
Model: strings.ReplaceAll(url.QueryEscape(modelName), "+", "%20"),
AppType: 0,
IMei: c.Device().IMEI,
IMei: c.deviceInfo.IMEI,
ShowInfo: true,
ModelShow: "",
RecoverDefault: false,
@ -96,7 +97,7 @@ func (c *QQClient) SetModelShow(modelName string, modelShow string) error {
Uin: c.Uin,
Model: strings.ReplaceAll(url.QueryEscape(modelName), "+", "%20"),
AppType: 0,
IMei: c.Device().IMEI,
IMei: c.deviceInfo.IMEI,
ShowInfo: true,
ModelShow: strings.ReplaceAll(url.QueryEscape(modelShow), "+", "%20"),
RecoverDefault: modelShow == "",

View File

@ -1,19 +1,13 @@
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"
@ -23,13 +17,8 @@ import (
"github.com/Mrs4s/MiraiGo/utils"
)
func init() {
decoders["MultiMsg.ApplyUp"] = decodeMultiApplyUpResponse
decoders["MultiMsg.ApplyDown"] = decodeMultiApplyDownResponse
}
// MultiMsg.ApplyUp
func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) (uint16, []byte) {
func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, groupUin int64) *network.Request {
req := &multimsg.MultiReqBody{
Subcmd: 1,
TermType: 5,
@ -47,13 +36,13 @@ func (c *QQClient) buildMultiApplyUpPacket(data, hash []byte, buType int32, grou
BuType: buType,
}
payload, _ := proto.Marshal(req)
return c.uniPacket("MultiMsg.ApplyUp", payload)
return c.uniRequest("MultiMsg.ApplyUp", payload, decodeMultiApplyUpResponse)
}
// MultiMsg.ApplyUp
func decodeMultiApplyUpResponse(_ *QQClient, pkt *network.Packet) (any, error) {
func decodeMultiApplyUpResponse(_ *QQClient, resp *network.Response) (interface{}, error) {
body := multimsg.MultiRspBody{}
if err := proto.Unmarshal(pkt.Payload, &body); err != nil {
if err := proto.Unmarshal(resp.Body, &body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if len(body.MultimsgApplyupRsp) == 0 {
@ -70,7 +59,7 @@ func decodeMultiApplyUpResponse(_ *QQClient, pkt *network.Packet) (any, error) {
}
// MultiMsg.ApplyDown
func (c *QQClient) buildMultiApplyDownPacket(resID string) (uint16, []byte) {
func (c *QQClient) buildMultiApplyDownPacket(resID string) *network.Request {
req := &multimsg.MultiReqBody{
Subcmd: 2,
TermType: 5,
@ -87,33 +76,25 @@ func (c *QQClient) buildMultiApplyDownPacket(resID string) (uint16, []byte) {
ReqChannelType: 2,
}
payload, _ := proto.Marshal(req)
return c.uniPacket("MultiMsg.ApplyDown", payload)
return c.uniRequest("MultiMsg.ApplyDown", payload, decodeMultiApplyDownResponse)
}
// MultiMsg.ApplyDown
func decodeMultiApplyDownResponse(_ *QQClient, pkt *network.Packet) (any, error) {
func decodeMultiApplyDownResponse(_ *QQClient, resp *network.Response) (interface{}, error) {
body := multimsg.MultiRspBody{}
if err := proto.Unmarshal(pkt.Payload, &body); err != nil {
if err := proto.Unmarshal(resp.Body, &body); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal protobuf message")
}
if len(body.MultimsgApplydownRsp) == 0 {
return nil, errors.New("message not found")
return nil, errors.New("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 {
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])
prefix = fmt.Sprintf("http://%s:%d", binary.UInt32ToIPV4Address(uint32(rsp.Uint32DownIp[0])), body.MultimsgApplydownRsp[0].Uint32DownPort[0])
}
b, err := utils.HttpGetBytes(fmt.Sprintf("%s%s", prefix, string(rsp.ThumbDownPara)), "")
if err != nil {
@ -155,11 +136,11 @@ func (l *forwardMsgLinker) link(name string) *message.ForwardMessage {
if item == nil {
return nil
}
nodes := make([]*message.ForwardNode, 0, len(item.Buffer.Msg))
for _, m := range item.Buffer.Msg {
name := m.Head.FromNick.Unwrap()
if m.Head.MsgType.Unwrap() == 82 && m.Head.GroupInfo != nil {
name = m.Head.GroupInfo.GroupCard.Unwrap()
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()
}
msgElems := message.ParseMessageElems(m.Body.RichText.Elems)
@ -171,15 +152,10 @@ 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{
GroupId: gid,
SenderId: m.Head.FromUin.Unwrap(),
SenderId: m.Head.GetFromUin(),
SenderName: name,
Time: m.Head.MsgTime.Unwrap(),
Time: m.Head.GetMsgTime(),
Message: msgElems,
})
}
@ -195,131 +171,39 @@ func (c *QQClient) GetForwardMessage(resID string) *message.ForwardMessage {
items: make(map[string]*msg.PbMultiMsgItem),
}
for _, item := range m.Items {
linker.items[item.FileName.Unwrap()] = item
linker.items[item.GetFileName()] = item
}
return linker.link("MultiMsg")
return linker.link(m.FileName)
}
func (c *QQClient) DownloadForwardMessage(resId string) *message.ForwardElement {
i, err := c.sendAndWait(c.buildMultiApplyDownPacket(resId))
i, err := c.callAndDecode(c.buildMultiApplyDownPacket(resId))
if err != nil {
return nil
}
multiMsg := i.(*msg.PbMultiMsgTransmit)
if multiMsg.PbItemList == nil {
if multiMsg.GetPbItemList() == nil {
return nil
}
var pv bytes.Buffer
for i := 0; i < int(math.Min(4, float64(len(multiMsg.Msg)))); i++ {
var pv string
for i := 0; i < int(math.Min(4, float64(len(multiMsg.GetMsg())))); i++ {
m := multiMsg.Msg[i]
sender := m.Head.FromNick.Unwrap()
if m.Head.MsgType.Unwrap() == 82 && m.Head.GroupInfo != nil {
sender = m.Head.GroupInfo.GroupCard.Unwrap()
}
brief := message.ToReadableString(message.ParseMessageElems(multiMsg.Msg[i].Body.RichText.Elems))
fmt.Fprintf(&pv, `<title size="26" color="#777777">%s: %s</title>`, sender, brief)
pv += fmt.Sprintf(`<title size="26" color="#777777">%s: %s</title>`,
func() string {
if m.Head.GetMsgType() == 82 && m.Head.GroupInfo != nil {
return m.Head.GroupInfo.GetGroupCard()
}
return m.Head.GetFromNick()
}(),
message.ToReadableString(
message.ParseMessageElems(multiMsg.Msg[i].GetBody().GetRichText().Elems),
),
)
}
return genForwardTemplate(
resId, pv.String(),
fmt.Sprintf("查看 %d 条转发消息", len(multiMsg.Msg)),
resId, pv, "群聊的聊天记录", "[聊天记录]", "聊天记录",
fmt.Sprintf("查看 %d 条转发消息", len(multiMsg.GetMsg())),
time.Now().UnixNano(),
multiMsg.PbItemList,
multiMsg.GetPbItemList(),
)
}
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,17 +2,16 @@ package client
import (
"net"
"net/netip"
"runtime/debug"
"sort"
"sync"
"time"
"github.com/Mrs4s/MiraiGo/message"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/MiraiGo/utils"
)
@ -41,36 +40,34 @@ 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(currentServerAddr); err != nil {
c.error("test chat server latency error: %v", err)
if r.ChatServerLatency, err = qualityTest(c.transport.GetServerAddr().String()); 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(currentServerAddr, 10)
res := utils.RunTCPPingLoop(c.transport.GetServerAddr().String(), 10)
r.ChatServerPacketLoss = res.PacketsLoss
if c.highwaySession.AddrLength() > 0 {
res = utils.RunTCPPingLoop(c.highwaySession.SsoAddr[0].String(), 10)
@ -81,75 +78,49 @@ 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
func (c *QQClient) connectFastest() error {
c.Disconnect()
addr, err := c.transport.ConnectFastest()
if err != nil {
c.Disconnect()
return err
}
c.Debug("connected to server: %v [fastest]", addr.String())
c.transport.NetLoop(c.pktProc, c.transport.ReadRequest)
c.ConnectTime = time.Now()
return nil
}
// connect 连接到 QQClient.servers 中的服务器
func (c *QQClient) connect() error {
// 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.once.Do(func() {
c.OnGroupMessage(func(_ *QQClient, _ *message.GroupMessage) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.OnPrivateMessage(func(_ *QQClient, _ *message.PrivateMessage) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.OnTempMessage(func(_ *QQClient, _ *TempMessageEvent) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.onGroupMessageReceipt("internal", func(_ *QQClient, _ *groupMessageReceiptEvent) {
c.stat.MessageSent.Add(1)
})
// go c.netLoop()
})
return c.connectFastest() // 暂时?
/*c.Info("connect to server: %v", c.servers[c.currServerIndex].String())
err := c.TCP.Connect(c.servers[c.currServerIndex])
c.currServerIndex++
if c.currServerIndex == len(c.servers) {
c.currServerIndex = 0
@ -159,30 +130,16 @@ 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.GroupMessageEvent.Subscribe(func(_ *QQClient, _ *message.GroupMessage) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.PrivateMessageEvent.Subscribe(func(_ *QQClient, _ *message.PrivateMessage) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.TempMessageEvent.Subscribe(func(_ *QQClient, _ *TempMessageEvent) {
c.stat.MessageReceived.Add(1)
c.stat.LastMessageTime.Store(time.Now().Unix())
})
c.onGroupMessageReceipt("internal", func(_ *QQClient, _ *groupMessageReceiptEvent) {
c.stat.MessageSent.Add(1)
})
go c.netLoop()
})
c.retryTimes = 0
c.ConnectTime = time.Now()
return nil
return nil*/
}
func (c *QQClient) QuickReconnect() {
c.quickReconnect() // TODO "用户请求快速重连"
}
// quickReconnect 快速重连
@ -190,14 +147,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.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "quick reconnect failed"})
c.Error("connect server error: %v", err)
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "快速重连失败"})
return
}
if err := c.registerClient(); err != nil {
c.error("register client failed: %v", err)
c.Error("register client failed: %v", err)
c.Disconnect()
c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"})
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "register error"})
return
}
}
@ -205,55 +162,59 @@ func (c *QQClient) quickReconnect() {
// Disconnect 中断连接, 不释放资源
func (c *QQClient) Disconnect() {
c.Online.Store(false)
c.TCP.Close()
c.transport.Close()
}
// sendAndWait 向服务器发送一个数据包, 并等待返回
func (c *QQClient) sendAndWait(seq uint16, pkt []byte, params ...network.RequestParams) (any, error) {
type T struct {
Response any
Error error
func (c *QQClient) send(call *network.Call) {
if call.Done == nil {
call.Done = make(chan *network.Call, 3) // use buffered channel
}
ch := make(chan T, 1)
var p network.RequestParams
seq := call.Request.SequenceID
c.pendingMu.Lock()
c.pending[seq] = call
c.pendingMu.Unlock()
if len(params) != 0 {
p = params[0]
}
c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) {
ch <- T{
Response: i,
Error: err,
}
}, params: p, dynamic: false})
err := c.sendPacket(pkt)
err := c.sendPacket(c.transport.PackPacket(call.Request))
c.Debug("send pkt: %v seq: %d", call.Request.CommandName, call.Request.SequenceID)
if err != nil {
c.pendingMu.Lock()
call = c.pending[seq]
delete(c.pending, seq)
c.pendingMu.Unlock()
call.Err = err
call.Done <- call
}
}
func (c *QQClient) sendReq(req *network.Request) {
c.send(&network.Call{Request: req})
}
func (c *QQClient) call(req *network.Request) (*network.Response, error) {
call := &network.Call{
Request: req,
Done: make(chan *network.Call, 3),
}
c.send(call)
select {
case <-call.Done:
return call.Response, call.Err
case <-time.After(time.Second * 15):
return nil, errors.New("Packet timed out")
}
}
func (c *QQClient) callAndDecode(req *network.Request) (interface{}, error) {
resp, err := c.call(req)
if err != nil {
c.handlers.Delete(seq)
return nil, err
}
retry := 0
for {
select {
case rsp := <-ch:
return rsp.Response, rsp.Error
case <-time.After(time.Second * 15):
retry++
if retry < 2 {
_ = c.sendPacket(pkt)
continue
}
c.handlers.Delete(seq)
return nil, errors.New("Packet timed out")
}
}
return req.Decode(resp)
}
// sendPacket 向服务器发送一个数据包
func (c *QQClient) sendPacket(pkt []byte) error {
err := c.TCP.Write(pkt)
err := c.transport.Write(pkt)
if err != nil {
c.stat.PacketLost.Add(1)
} else {
@ -265,7 +226,7 @@ func (c *QQClient) sendPacket(pkt []byte) error {
// waitPacket
// 等待一个或多个数据包解析, 优先级低于 sendAndWait
// 返回终止解析函数
func (c *QQClient) waitPacket(cmd string, f func(any, error)) func() {
func (c *QQClient) waitPacket(cmd string, f func(interface{}, error)) func() {
c.waiters.Store(cmd, f)
return func() {
c.waiters.Delete(cmd)
@ -274,9 +235,9 @@ func (c *QQClient) waitPacket(cmd string, f func(any, error)) func() {
// waitPacketTimeoutSyncF
// 等待一个数据包解析, 优先级低于 sendAndWait
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) {
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) {
if filter(i) {
r = i
e = err
@ -291,140 +252,104 @@ func (c *QQClient) waitPacketTimeoutSyncF(cmd string, timeout time.Duration, fil
}
}
// sendAndWaitDynamic
// 发送数据包并返回需要解析的 response
func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) {
ch := make(chan []byte, 1)
c.handlers.Store(seq, &handlerInfo{fun: func(i any, err error) { ch <- i.([]byte) }, dynamic: true})
err := c.sendPacket(pkt)
if err != nil {
c.handlers.Delete(seq)
return nil, err
}
select {
case rsp := <-ch:
return rsp, nil
case <-time.After(time.Second * 15):
c.handlers.Delete(seq)
return nil, errors.New("Packet timed out")
}
}
// SendSsoPacket
// 发送签名回调包给服务器并获取返回结果供提交
func (c *QQClient) SendSsoPacket(cmd string, body []byte) ([]byte, error) {
seq, data := c.uniPacket(cmd, body)
return c.sendAndWaitDynamic(seq, data)
}
// plannedDisconnect 计划中断线事件
func (c *QQClient) plannedDisconnect(_ *network.TCPClient) {
c.debug("planned disconnect.")
func (c *QQClient) plannedDisconnect(_ *network.TCPListener) {
c.Debug("planned disconnect.")
c.stat.DisconnectTimes.Add(1)
c.Online.Store(false)
}
// unexpectedDisconnect 非预期断线事件
func (c *QQClient) unexpectedDisconnect(_ *network.TCPClient, e error) {
c.error("unexpected disconnect: %v", e)
func (c *QQClient) unexpectedDisconnect(_ *network.TCPListener, 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.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "connection dropped by server."})
c.Error("connect server error: %v", err)
c.dispatchDisconnectEvent(&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.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "register error"})
c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "register error"})
return
}
}
// netLoop 通过循环来不停接收数据包
func (c *QQClient) netLoop() {
errCount := 0
for c.alive {
l, err := c.TCP.ReadInt32()
if err != nil {
time.Sleep(time.Millisecond * 500)
continue
func (c *QQClient) pktProc(req *network.Request, netErr error) {
if netErr != nil {
switch true {
case errors.Is(netErr, network.ErrConnectionBroken):
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: netErr.Error()})
c.QuickReconnect()
case errors.Is(netErr, network.ErrSessionExpired) || errors.Is(netErr, network.ErrPacketDropped):
c.Disconnect()
go c.dispatchDisconnectEvent(&ClientDisconnectedEvent{Message: "session expired"})
}
if l < 4 || l > 1024*1024*10 { // max 10MB
c.error("parse incoming packet error: invalid packet length %v", l)
errCount++
if errCount > 2 {
go c.quickReconnect()
}
continue
}
data, _ := c.TCP.ReadBytes(int(l) - 4)
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)
if errors.Is(err, network.ErrSessionExpired) || errors.Is(err, network.ErrPacketDropped) {
c.Disconnect()
go c.DisconnectedEvent.dispatch(c, &ClientDisconnectedEvent{Message: "session expired"})
continue
}
errCount++
if errCount > 2 {
go c.quickReconnect()
}
continue
}
if resp.EncryptType == network.EncryptTypeEmptyKey {
m, err := c.oicq.Unmarshal(resp.Body)
if err != nil {
c.error("decrypt payload error: %v", err)
if errors.Is(err, oicq.ErrUnknownFlag) {
go c.quickReconnect()
}
continue
}
resp.Body = m.Body
}
errCount = 0
c.debug("rev pkt: %v seq: %v", resp.CommandName, resp.SequenceID)
c.stat.PacketReceived.Add(1)
pkt := &network.Packet{
SequenceId: uint16(resp.SequenceID),
CommandName: resp.CommandName,
Payload: resp.Body,
}
go func(pkt *network.Packet) {
defer func() {
if pan := recover(); pan != nil {
c.error("panic on decoder %v : %v\n%s", pkt.CommandName, pan, debug.Stack())
c.dump("packet decode error: %v - %v", pkt.Payload, pkt.CommandName, pan)
}
}()
c.Error("parse incoming packet error: %v", netErr)
return
}
if decoder, ok := decoders[pkt.CommandName]; ok {
// found predefined decoder
info, ok := c.handlers.LoadAndDelete(pkt.SequenceId)
var decoded any
decoded = pkt.Payload
if info == nil || !info.dynamic {
pkt.Params = info.getParams()
decoded, err = decoder(c, pkt)
if err != nil {
c.debug("decode pkt %v error: %+v", pkt.CommandName, err)
}
}
if ok {
info.fun(decoded, err)
} else if f, ok := c.waiters.Load(pkt.CommandName); ok { // 在不存在handler的情况下触发wait
f(decoded, err)
}
} else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok {
// does not need decoder
f.fun(pkt.Payload, nil)
} else {
c.debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId)
if req.EncryptType == network.EncryptTypeEmptyKey {
m, err := c.oicq.Unmarshal(req.Body)
if err != nil {
c.Error("decrypt payload error: %v", err)
if errors.Is(err, oicq.ErrUnknownFlag) {
go c.quickReconnect() // TODO "服务器发送未知响应"
}
}(pkt)
}
req.Body = m.Body
}
defer func() {
if pan := recover(); pan != nil {
c.Error("panic on decoder %v : %v\n%s", req.CommandName, pan, debug.Stack())
c.Dump("packet decode error: %v - %v", req.Body, req.CommandName, pan)
}
}()
c.Debug("recv pkt: %v seq: %v", req.CommandName, req.SequenceID)
c.stat.PacketReceived.Add(1)
// snapshot of read call
c.pendingMu.Lock()
call := c.pending[req.SequenceID]
if call != nil {
call.Response = &network.Response{
SequenceID: req.SequenceID,
CommandName: req.CommandName,
Body: req.Body,
Request: call.Request,
}
delete(c.pending, req.SequenceID)
}
c.pendingMu.Unlock()
if call != nil && call.Request.CommandName == req.CommandName {
select {
case call.Done <- call:
default:
// we don't want blocking
}
return
}
if decoder, ok := decoders[req.CommandName]; ok {
// found predefined decoder
resp := network.Response{
SequenceID: req.SequenceID,
CommandName: req.CommandName,
Body: req.Body,
// Request: nil,
}
decoded, err := decoder(c, &resp)
if err != nil {
c.Debug("decode req %v error: %+v", req.CommandName, err)
}
if f, ok := c.waiters.Load(req.CommandName); ok { // 在不存在handler的情况下触发wait
f.(func(interface{}, error))(decoded, err)
}
} else {
c.Debug("Unhandled Command: %s\nSeq: %d\nThis message can be ignored.", req.CommandName, req.SequenceID)
}
}

View File

@ -62,7 +62,7 @@ func (c *QQClient) grayTipProcessor(groupCode int64, tipInfo *notify.GeneralGray
}
}
if sender != 0 {
c.GroupNotifyEvent.dispatch(c, &GroupPokeNotifyEvent{
c.dispatchGroupNotifyEvent(&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.GroupNotifyEvent.dispatch(c, &MemberHonorChangedNotifyEvent{
c.dispatchGroupNotifyEvent(&MemberHonorChangedNotifyEvent{
GroupCode: groupCode,
Honor: func() HonorType {
switch tipInfo.TemplId {
@ -127,7 +127,8 @@ func (c *QQClient) msgGrayTipProcessor(groupCode int64, tipInfo *notify.AIOGrayT
}
}
// 好像只能这么判断
if strings.Contains(content, "头衔") {
switch {
case strings.Contains(content, "头衔"):
event := &MemberSpecialTitleUpdatedEvent{GroupCode: groupCode}
for _, cmd := range tipCmds {
if cmd.Command == 5 {
@ -138,13 +139,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.MemberSpecialTitleUpdatedEvent.dispatch(c, event)
c.dispatchMemberSpecialTitleUpdateEvent(event)
}
}
@ -184,7 +185,6 @@ 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

@ -8,11 +8,7 @@ import (
"github.com/Mrs4s/MiraiGo/internal/proto"
)
func init() {
decoders["OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200"] = decodeOfflineFileDownloadResponse
}
func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, []byte) {
func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) *network.Request {
seq := c.nextSeq()
req := &cmd0x346.C346ReqBody{
Cmd: 1200,
@ -29,22 +25,21 @@ func (c *QQClient) buildOfflineFileDownloadRequestPacket(uuid []byte) (uint16, [
},
}
payload, _ := proto.Marshal(req)
packet := c.uniPacketWithSeq(seq, "OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200", payload)
return seq, packet
return c.uniPacketWithSeq(seq, "OfflineFilleHandleSvr.pb_ftn_CMD_REQ_APPLY_DOWNLOAD-1200", payload, decodeOfflineFileDownloadResponse)
}
func decodeOfflineFileDownloadResponse(c *QQClient, pkt *network.Packet) (any, error) {
func decodeOfflineFileDownloadResponse(c *QQClient, resp *network.Response) (interface{}, error) {
rsp := cmd0x346.C346RspBody{}
if err := proto.Unmarshal(pkt.Payload, &rsp); err != nil {
c.error("unmarshal cmd0x346 rsp body error: %v", err)
if err := proto.Unmarshal(resp.Body, &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, pkt *network.Packet) (any, error) {
func decodeOnlinePushReqPacket(c *QQClient, resp *network.Response) (interface{}, error) {
request := &jce.RequestPacket{}
request.ReadFrom(jce.NewJceReader(pkt.Payload))
request.ReadFrom(jce.NewJceReader(resp.Body))
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, pkt.SequenceId, msgInfos))
_ = c.sendPacket(c.buildDeleteOnlinePushPacket(uin, 0, nil, uint16(resp.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, unit{}, time.Second*30)
c.onlinePushCache.Add(k, "", time.Second*30)
// 0x2dc
if m.MsgType == 732 {
r := binary.NewReader(m.VMsg)
@ -53,17 +53,7 @@ func decodeOnlinePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
r.ReadBytes(6)
target := int64(uint32(r.ReadInt32()))
t := r.ReadInt32()
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{
c.dispatchGroupMuteEvent(&GroupMuteEvent{
GroupCode: groupCode,
OperatorUin: operator,
TargetUin: target,
@ -78,7 +68,7 @@ func decodeOnlinePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
if rm.MsgType == 2 {
continue
}
c.GroupMessageRecalledEvent.dispatch(c, &GroupMessageRecalledEvent{
c.dispatchGroupMessageRecalledEvent(&GroupMessageRecalledEvent{
GroupCode: groupCode,
OperatorUin: b.OptMsgRecall.Uin,
AuthorUin: rm.AuthorUin,
@ -92,7 +82,7 @@ func decodeOnlinePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
}
if b.OptMsgRedTips != nil {
if b.OptMsgRedTips.LuckyFlag == 1 { // 运气王提示
c.GroupNotifyEvent.dispatch(c, &GroupRedBagLuckyKingNotifyEvent{
c.dispatchGroupNotifyEvent(&GroupRedBagLuckyKingNotifyEvent{
GroupCode: groupCode,
Sender: int64(b.OptMsgRedTips.SenderUin),
LuckyKing: int64(b.OptMsgRedTips.LuckyUin),
@ -101,7 +91,7 @@ func decodeOnlinePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
}
if b.QqGroupDigestMsg != nil {
digest := b.QqGroupDigestMsg
c.GroupDigestEvent.dispatch(c, &GroupDigestEvent{
c.dispatchGroupDigestEvent(&GroupDigestEvent{
GroupCode: int64(digest.GroupCode),
MessageID: int32(digest.Seq),
InternalMessageID: int32(digest.Random),
@ -128,7 +118,7 @@ func decodeOnlinePushReqPacket(c *QQClient, pkt *network.Packet) (any, error) {
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))
}
}
}
@ -142,7 +132,7 @@ func msgType0x210Sub8ADecoder(c *QQClient, protobuf []byte) error {
}
for _, m := range s8a.MsgInfo {
if m.ToUin == c.Uin {
c.FriendMessageRecalledEvent.dispatch(c, &FriendMessageRecalledEvent{
c.dispatchFriendMessageRecalledEvent(&FriendMessageRecalledEvent{
FriendUin: m.FromUin,
MessageId: m.MsgSeq,
Time: m.MsgTime,
@ -162,7 +152,7 @@ func msgType0x210SubB3Decoder(c *QQClient, protobuf []byte) error {
Nickname: b3.MsgAddFrdNotify.Nick,
}
c.FriendList = append(c.FriendList, frd)
c.NewFriendEvent.dispatch(c, &NewFriendEvent{Friend: frd})
c.dispatchNewFriendEvent(&NewFriendEvent{Friend: frd})
return nil
}
@ -177,7 +167,7 @@ func msgType0x210SubD4Decoder(c *QQClient, protobuf []byte) error {
groupLeaveLock.Unlock()
return err
}
c.GroupLeaveEvent.dispatch(c, &GroupLeaveEvent{Group: g})
c.dispatchLeaveGroupEvent(&GroupLeaveEvent{Group: g})
}
groupLeaveLock.Unlock()
return nil
@ -191,15 +181,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.Field.Unwrap() == 1 {
if g := c.FindGroup(int64(m.ModGroupProfile.GroupCode.Unwrap())); g != nil {
if info.GetField() == 1 {
if g := c.FindGroup(int64(m.ModGroupProfile.GetGroupCode())); g != nil {
old := g.Name
g.Name = string(info.Value)
c.GroupNameUpdatedEvent.dispatch(c, &GroupNameUpdatedEvent{
g.Name = string(info.GetValue())
c.dispatchGroupNameUpdatedEvent(&GroupNameUpdatedEvent{
Group: g,
OldName: old,
NewName: g.Name,
OperatorUin: int64(m.ModGroupProfile.CmdUin.Unwrap()),
OperatorUin: int64(m.ModGroupProfile.GetCmdUin()),
})
}
}
@ -208,10 +198,6 @@ 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")
}
@ -235,10 +221,7 @@ func msgType0x210Sub122Decoder(c *QQClient, protobuf []byte) error {
if sender == 0 {
return nil
}
if receiver == 0 {
receiver = c.Uin
}
c.FriendNotifyEvent.dispatch(c, &FriendPokeNotifyEvent{
c.dispatchFriendNotifyEvent(&FriendPokeNotifyEvent{
Sender: sender,
Receiver: receiver,
})
@ -255,11 +238,11 @@ func msgType0x210Sub44Decoder(c *QQClient, protobuf []byte) error {
}
groupJoinLock.Lock()
defer groupJoinLock.Unlock()
if s44.GroupSyncMsg.GrpCode == 0 { // member sync
if s44.GroupSyncMsg.GetGrpCode() == 0 { // member sync
return errors.New("invalid group code")
}
c.debug("syncing members.")
if group := c.FindGroup(s44.GroupSyncMsg.GrpCode); group != nil {
c.Debug("syncing members.")
if group := c.FindGroup(s44.GroupSyncMsg.GetGrpCode()); group != nil {
group.lock.Lock()
defer group.lock.Unlock()
@ -274,7 +257,7 @@ func msgType0x210Sub44Decoder(c *QQClient, protobuf []byte) error {
group.Members = newMem
for _, m := range newMem {
if lastJoinTime < m.JoinTime {
c.GroupMemberJoinEvent.dispatch(c, &MemberJoinGroupEvent{
go c.dispatchNewMemberEvent(&MemberJoinGroupEvent{
Group: group,
Member: m,
})

View File

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

View File

@ -3,74 +3,259 @@
package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type ChannelUserInfo struct {
ClientIdentity *ClientIdentity `protobuf:"bytes,1,opt"`
MemberType proto.Option[uint32] `protobuf:"varint,2,opt"`
MemberType *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 proto.Option[bool] `protobuf:"varint,1,opt"`
AllowWriteFeed proto.Option[bool] `protobuf:"varint,2,opt"`
_ [0]func()
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
}
type ClientIdentity struct {
ClientId proto.Option[uint32] `protobuf:"varint,1,opt"`
Desc proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
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 ""
}
type BaseGuildInfo struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
JoinTime proto.Option[uint64] `protobuf:"varint,3,opt"`
_ [0]func()
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
}
type BaseRoleGroupInfo struct {
RoleId proto.Option[uint64] `protobuf:"varint,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
Color proto.Option[uint32] `protobuf:"varint,3,opt"`
_ [0]func()
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
}
type StChannelInfo struct {
Sign *StChannelSign `protobuf:"bytes,1,opt"`
Name proto.Option[string] `protobuf:"bytes,2,opt"`
IconUrl proto.Option[string] `protobuf:"bytes,3,opt"`
_ [0]func()
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 ""
}
type StChannelSign struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelId proto.Option[uint64] `protobuf:"varint,2,opt"`
_ [0]func()
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
}
type StEmotionReactionInfo struct {
Id proto.Option[string] `protobuf:"bytes,1,opt"`
EmojiReactionList []*EmojiReaction `protobuf:"bytes,2,rep"`
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
}
type StCommonExt struct {
MapInfo []*CommonEntry `protobuf:"bytes,1,rep"`
AttachInfo proto.Option[string] `protobuf:"bytes,2,opt"`
MapBytesInfo []*BytesEntry `protobuf:"bytes,3,rep"`
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
}
type BytesEntry struct {
Key proto.Option[string] `protobuf:"bytes,1,opt"`
Value []byte `protobuf:"bytes,2,opt"`
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
}
type CommonEntry struct {
Key proto.Option[string] `protobuf:"bytes,1,opt"`
Value proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
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 ""
}

View File

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

View File

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

View File

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

View File

@ -3,20 +3,79 @@
package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type StAlterFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
BusiReqData []byte `protobuf:"bytes,3,opt"`
MBitmap proto.Option[uint64] `protobuf:"varint,4,opt"`
From proto.Option[int32] `protobuf:"varint,5,opt"`
Src proto.Option[int32] `protobuf:"varint,6,opt"`
AlterFeedExtInfo []*CommonEntry `protobuf:"bytes,7,rep"`
JsonFeed proto.Option[string] `protobuf:"bytes,8,opt"`
ClientContent *StClientContent `protobuf:"bytes,9,opt"`
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
}
type StAlterFeedRsp struct {
@ -25,47 +84,211 @@ 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 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()
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 ""
}
type StClientVideoContent struct {
TaskId proto.Option[string] `protobuf:"bytes,1,opt"`
VideoId proto.Option[string] `protobuf:"bytes,2,opt"`
VideoUrl proto.Option[string] `protobuf:"bytes,3,opt"`
CoverUrl proto.Option[string] `protobuf:"bytes,4,opt"`
_ [0]func()
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 ""
}
type StDelFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
From proto.Option[int32] `protobuf:"varint,3,opt"`
Src proto.Option[int32] `protobuf:"varint,4,opt"`
_ [0]func()
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
}
type StDelFeedRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
_ [0]func()
}
func (x *StDelFeedRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
type StDoCommentReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
CommentType proto.Option[uint32] `protobuf:"varint,2,opt"`
Comment *StComment `protobuf:"bytes,3,opt"`
Feed *StFeed `protobuf:"bytes,4,opt"`
From proto.Option[int32] `protobuf:"varint,5,opt"`
BusiReqData []byte `protobuf:"bytes,6,opt"`
Src proto.Option[int32] `protobuf:"varint,7,opt"`
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
}
type StDoCommentRsp struct {
@ -74,19 +297,110 @@ 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 proto.Option[uint32] `protobuf:"varint,2,opt"`
LikeType *uint32 `protobuf:"varint,2,opt"`
Like *StLike `protobuf:"bytes,3,opt"`
Feed *StFeed `protobuf:"bytes,4,opt"`
BusiReqData []byte `protobuf:"bytes,5,opt"`
Comment *StComment `protobuf:"bytes,6,opt"`
Reply *StReply `protobuf:"bytes,7,opt"`
From proto.Option[int32] `protobuf:"varint,8,opt"`
Src proto.Option[int32] `protobuf:"varint,9,opt"`
From *int32 `protobuf:"varint,8,opt"`
Src *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"`
@ -94,15 +408,99 @@ 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 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"`
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
}
type StDoReplyRsp struct {
@ -111,28 +509,138 @@ 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 proto.Option[int32] `protobuf:"varint,6,opt"`
_ [0]func()
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
}
type StDoSecurityRsp struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
_ [0]func()
}
func (x *StDoSecurityRsp) GetExtInfo() *StCommonExt {
if x != nil {
return x.ExtInfo
}
return nil
}
type StModifyFeedReq struct {
ExtInfo *StCommonExt `protobuf:"bytes,1,opt"`
Feed *StFeed `protobuf:"bytes,2,opt"`
MBitmap proto.Option[uint64] `protobuf:"varint,3,opt"`
From proto.Option[int32] `protobuf:"varint,4,opt"`
Src proto.Option[int32] `protobuf:"varint,5,opt"`
ModifyFeedExtInfo []*CommonEntry `protobuf:"bytes,6,rep"`
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
}
type StModifyFeedRsp struct {
@ -141,15 +649,92 @@ 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 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"`
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
}
type StPublishFeedRsp struct {
@ -157,3 +742,24 @@ 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 = "github.com/Mrs4s/MiraiGo/client/pb/channel";
option go_package = "pb/channel;channel";
import "pb/channel/GuildFeedCloudMeta.proto";
import "pb/channel/GuildChannelBase.proto";

View File

@ -3,45 +3,145 @@
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 proto.Option[uint64] `protobuf:"varint,1,opt"`
Id []*MsgId `protobuf:"bytes,2,rep"`
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
}
type ChannelMsgInfo struct {
ChannelId proto.Option[uint64] `protobuf:"varint,1,opt"`
RespData []*MsgRespData `protobuf:"bytes,2,rep"`
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
}
type EmojiReaction struct {
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()
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
}
type GuildMsg struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelMsgList []*SvrChannelMsg `protobuf:"bytes,2,rep"`
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
}
type GuildMsgInfo struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
ChannelMsgInfoList []*ChannelMsgInfo `protobuf:"bytes,2,rep"`
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
}
type MsgCnt struct {
@ -49,13 +149,54 @@ 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 proto.Option[uint64] `protobuf:"varint,1,opt"`
Seq proto.Option[uint64] `protobuf:"varint,2,opt"`
_ [0]func()
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
}
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 = "github.com/Mrs4s/MiraiGo/client/pb/channel";
option go_package = "pb/channel;channel";
message BatchGetMsgRspCountReq {
repeated GuildMsg guildMsgList = 1;

View File

@ -5,77 +5,374 @@ package channel
import (
msg "github.com/Mrs4s/MiraiGo/client/pb/msg"
proto "github.com/RomiChan/protobuf/proto"
)
type ChannelContentHead struct {
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 *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 DirectMessageMember struct {
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"`
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
}
type ChannelEvent struct {
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 *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 ChannelExtInfo struct {
FromNick []byte `protobuf:"bytes,1,opt"`
GuildName []byte `protobuf:"bytes,2,opt"`
ChannelName []byte `protobuf:"bytes,3,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"`
Visibility *uint32 `protobuf:"varint,4,opt"`
NotifyType *uint32 `protobuf:"varint,5,opt"`
OfflineFlag *uint32 `protobuf:"varint,6,opt"`
NameType *uint32 `protobuf:"varint,7,opt"`
MemberName []byte `protobuf:"bytes,8,opt"`
Timestamp proto.Option[uint32] `protobuf:"varint,9,opt"`
EventVersion proto.Option[uint64] `protobuf:"varint,10,opt"`
Timestamp *uint32 `protobuf:"varint,9,opt"`
EventVersion *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 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()
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
}
type ChannelInfo struct {
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"`
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
}
type ChannelLoginSig struct {
Type proto.Option[uint32] `protobuf:"varint,1,opt"`
Sig []byte `protobuf:"bytes,2,opt"`
Appid proto.Option[uint32] `protobuf:"varint,3,opt"`
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 ChannelMeta struct {
FromUin proto.Option[uint64] `protobuf:"varint,1,opt"`
LoginSig *ChannelLoginSig `protobuf:"bytes,2,opt"`
_ [0]func()
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
}
type ChannelMsgContent struct {
@ -83,64 +380,303 @@ type ChannelMsgContent struct {
CtrlHead *ChannelMsgCtrlHead `protobuf:"bytes,2,opt"`
Body *msg.MessageBody `protobuf:"bytes,3,opt"`
ExtInfo *ChannelExtInfo `protobuf:"bytes,4,opt"`
_ [0]func()
}
func (x *ChannelMsgContent) GetHead() *ChannelMsgHead {
if x != nil {
return x.Head
}
return nil
}
func (x *ChannelMsgContent) GetCtrlHead() *ChannelMsgCtrlHead {
if x != nil {
return x.CtrlHead
}
return nil
}
func (x *ChannelMsgContent) GetBody() *msg.MessageBody {
if x != nil {
return x.Body
}
return nil
}
func (x *ChannelMsgContent) GetExtInfo() *ChannelExtInfo {
if x != nil {
return x.ExtInfo
}
return nil
}
type ChannelMsgCtrlHead struct {
IncludeUin [][]byte `protobuf:"bytes,1,rep"`
// repeated uint64 excludeUin = 2; // bytes?
// repeated uint64 featureid = 3;
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"`
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
}
type ChannelMsgHead struct {
RoutingHead *ChannelRoutingHead `protobuf:"bytes,1,opt"`
ContentHead *ChannelContentHead `protobuf:"bytes,2,opt"`
_ [0]func()
}
func (x *ChannelMsgHead) GetRoutingHead() *ChannelRoutingHead {
if x != nil {
return x.RoutingHead
}
return nil
}
func (x *ChannelMsgHead) GetContentHead() *ChannelContentHead {
if x != nil {
return x.ContentHead
}
return nil
}
type ChannelMsgMeta struct {
AtAllSeq proto.Option[uint64] `protobuf:"varint,1,opt"`
_ [0]func()
AtAllSeq *uint64 `protobuf:"varint,1,opt"`
}
func (x *ChannelMsgMeta) GetAtAllSeq() uint64 {
if x != nil && x.AtAllSeq != nil {
return *x.AtAllSeq
}
return 0
}
type ChannelMsgOpInfo struct {
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()
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
}
type PersonalLevel struct {
ToUin proto.Option[uint64] `protobuf:"varint,1,opt"`
Level proto.Option[uint64] `protobuf:"varint,2,opt"`
_ [0]func()
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
}
type ChannelRole struct {
Id proto.Option[uint64] `protobuf:"varint,1,opt"`
Info []byte `protobuf:"bytes,2,opt"`
Flag proto.Option[uint32] `protobuf:"varint,3,opt"`
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
}
type ChannelRoutingHead struct {
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()
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
}

View File

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

View File

@ -3,35 +3,129 @@
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 proto.Option[uint32] `protobuf:"varint,2,opt"`
NeedResp proto.Option[uint32] `protobuf:"varint,3,opt"`
GeneralFlag *uint32 `protobuf:"varint,2,opt"`
NeedResp *uint32 `protobuf:"varint,3,opt"`
ServerBuf []byte `protobuf:"bytes,4,opt"`
CompressFlag proto.Option[uint32] `protobuf:"varint,5,opt"`
CompressFlag *uint32 `protobuf:"varint,5,opt"`
CompressMsg []byte `protobuf:"bytes,6,opt"`
FocusInfo *FocusInfo `protobuf:"bytes,7,opt"`
HugeFlag proto.Option[uint32] `protobuf:"varint,8,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
}
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"`
}
type ServerBuf struct {
SvrIp proto.Option[uint32] `protobuf:"varint,1,opt"`
SvrPort proto.Option[uint32] `protobuf:"varint,2,opt"`
EchoKey []byte `protobuf:"bytes,3,opt"`
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
}

View File

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

View File

@ -5,28 +5,117 @@ 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"`
_ [0]func()
}
func (x *DF62ReqBody) GetMsg() *ChannelMsgContent {
if x != nil {
return x.Msg
}
return nil
}
type DF62RspBody struct {
Result proto.Option[uint32] `protobuf:"varint,1,opt"`
Result *uint32 `protobuf:"varint,1,opt"`
Errmsg []byte `protobuf:"bytes,2,opt"`
SendTime proto.Option[uint32] `protobuf:"varint,3,opt"`
SendTime *uint32 `protobuf:"varint,3,opt"`
Head *ChannelMsgHead `protobuf:"bytes,4,opt"`
ErrType proto.Option[uint32] `protobuf:"varint,5,opt"`
ErrType *uint32 `protobuf:"varint,5,opt"`
TransSvrInfo *TransSvrInfo `protobuf:"bytes,6,opt"`
FreqLimitInfo *ChannelFreqLimitInfo `protobuf:"bytes,7,opt"`
Body *msg.MessageBody `protobuf:"bytes,8,opt"`
}
type TransSvrInfo struct {
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"`
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
}

View File

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

View File

@ -3,136 +3,674 @@
package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
type ChannelMsg struct {
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"`
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"`
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 proto.Option[uint32] `protobuf:"varint,2,opt"`
DirectMessageFlag proto.Option[uint32] `protobuf:"varint,3,opt"`
_ [0]func()
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
}
type ChannelMsgRsp struct {
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"`
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
}
type ChannelNode struct {
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"`
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
}
type ChannelParam struct {
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"`
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
}
type DirectMessageSource struct {
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"`
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
}
type FirstViewMsg struct {
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"`
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
}
type FirstViewReq struct {
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()
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
}
type FirstViewRsp struct {
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"`
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
}
type GuildNode struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
GuildCode proto.Option[uint64] `protobuf:"varint,2,opt"`
GuildId *uint64 `protobuf:"varint,1,opt"`
GuildCode *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 proto.Option[uint64] `protobuf:"varint,1,opt"`
EventVersion proto.Option[uint64] `protobuf:"varint,2,opt"`
_ [0]func()
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
}
type MultiChannelMsg struct {
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"`
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
}
type MultiChannelMsgReq struct {
ChannelParams []*ChannelParam `protobuf:"bytes,1,rep"`
Seq proto.Option[uint32] `protobuf:"varint,2,opt"`
DirectMessageFlag proto.Option[uint32] `protobuf:"varint,3,opt"`
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
}
type MultiChannelMsgRsp struct {
Result proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrMsg []byte `protobuf:"bytes,2,opt"`
Seq proto.Option[uint32] `protobuf:"varint,3,opt"`
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
}
type ReqBody struct {
ChannelParam *ChannelParam `protobuf:"bytes,1,opt"`
DirectMessageFlag proto.Option[uint32] `protobuf:"varint,2,opt"`
_ [0]func()
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
}
type RspBody struct {
Result proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrMsg []byte `protobuf:"bytes,2,opt"`
ChannelMsg *ChannelMsg `protobuf:"bytes,3,opt"`
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
}

View File

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

View File

@ -3,170 +3,711 @@
package channel
import (
proto "github.com/RomiChan/protobuf/proto"
)
// see sub_37628C
type ChannelOidb0Xf5BRsp struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
GuildId *uint64 `protobuf:"varint,1,opt"`
Bots []*GuildMemberInfo `protobuf:"bytes,4,rep"`
Members []*GuildMemberInfo `protobuf:"bytes,5,rep"`
NextIndex proto.Option[uint32] `protobuf:"varint,10,opt"`
Finished proto.Option[uint32] `protobuf:"varint,9,opt"`
NextQueryParam proto.Option[string] `protobuf:"bytes,24,opt"`
NextIndex *uint32 `protobuf:"varint,10,opt"`
Finished *uint32 `protobuf:"varint,9,opt"`
NextQueryParam *string `protobuf:"bytes,24,opt"`
MemberWithRoles []*GuildGroupMembersInfo `protobuf:"bytes,25,rep"`
NextRoleIdIndex proto.Option[uint64] `protobuf:"varint,26,opt"`
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
}
type ChannelOidb0Xf88Rsp struct {
Profile *GuildUserProfile `protobuf:"bytes,1,opt"`
_ [0]func()
}
func (x *ChannelOidb0Xf88Rsp) GetProfile() *GuildUserProfile {
if x != nil {
return x.Profile
}
return nil
}
type ChannelOidb0Xfc9Rsp struct {
Profile *GuildUserProfile `protobuf:"bytes,1,opt"`
_ [0]func()
}
func (x *ChannelOidb0Xfc9Rsp) GetProfile() *GuildUserProfile {
if x != nil {
return x.Profile
}
return nil
}
type ChannelOidb0Xf57Rsp struct {
Rsp *GuildMetaRsp `protobuf:"bytes,1,opt"`
_ [0]func()
}
func (x *ChannelOidb0Xf57Rsp) GetRsp() *GuildMetaRsp {
if x != nil {
return x.Rsp
}
return nil
}
type ChannelOidb0Xf55Rsp struct {
Info *GuildChannelInfo `protobuf:"bytes,1,opt"`
_ [0]func()
}
func (x *ChannelOidb0Xf55Rsp) GetInfo() *GuildChannelInfo {
if x != nil {
return x.Info
}
return nil
}
type ChannelOidb0Xf5DRsp struct {
Rsp *ChannelListRsp `protobuf:"bytes,1,opt"`
_ [0]func()
}
func (x *ChannelOidb0Xf5DRsp) GetRsp() *ChannelListRsp {
if x != nil {
return x.Rsp
}
return nil
}
type ChannelOidb0X1017Rsp struct {
P1 *P10X1017 `protobuf:"bytes,1,opt"`
_ [0]func()
}
func (x *ChannelOidb0X1017Rsp) GetP1() *P10X1017 {
if x != nil {
return x.P1
}
return nil
}
type P10X1017 struct {
TinyId proto.Option[uint64] `protobuf:"varint,1,opt"`
Roles []*GuildUserRole `protobuf:"bytes,3,rep"`
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
}
type ChannelOidb0X1019Rsp struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Roles []*GuildRole `protobuf:"bytes,2,rep"`
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
}
type ChannelOidb0X1016Rsp struct {
RoleId proto.Option[uint64] `protobuf:"varint,2,opt"`
_ [0]func()
RoleId *uint64 `protobuf:"varint,2,opt"`
}
func (x *ChannelOidb0X1016Rsp) GetRoleId() uint64 {
if x != nil && x.RoleId != nil {
return *x.RoleId
}
return 0
}
type GuildMetaRsp struct {
GuildId proto.Option[uint64] `protobuf:"varint,3,opt"`
Meta *GuildMeta `protobuf:"bytes,4,opt"`
_ [0]func()
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
}
type ChannelListRsp struct {
GuildId proto.Option[uint64] `protobuf:"varint,1,opt"`
Channels []*GuildChannelInfo `protobuf:"bytes,2,rep"` // 5: Category infos
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
}
type GuildGroupMembersInfo struct {
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"`
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
}
// see sub_374334
type GuildMemberInfo struct {
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()
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
}
// 频道系统用户资料
type GuildUserProfile struct {
TinyId proto.Option[uint64] `protobuf:"varint,2,opt"`
Nickname proto.Option[string] `protobuf:"bytes,3,opt"`
AvatarUrl proto.Option[string] `protobuf:"bytes,6,opt"`
TinyId *uint64 `protobuf:"varint,2,opt"`
Nickname *string `protobuf:"bytes,3,opt"`
AvatarUrl *string `protobuf:"bytes,6,opt"`
// 15: avatar url info
JoinTime proto.Option[int64] `protobuf:"varint,16,opt"` // uncertainty
_ [0]func()
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
}
type GuildRole struct {
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()
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
}
type GuildUserRole struct {
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()
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
}
type GuildMeta struct {
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()
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
}
type GuildChannelInfo struct {
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"`
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"`
// 11 - 14 : MsgInfo
CreatorTinyId proto.Option[uint64] `protobuf:"varint,15,opt"`
CreatorTinyId *uint64 `protobuf:"varint,15,opt"`
// 16: Member info ?
VisibleType proto.Option[int32] `protobuf:"varint,22,opt"`
VisibleType *int32 `protobuf:"varint,22,opt"`
TopMsg *GuildChannelTopMsgInfo `protobuf:"bytes,28,opt"`
CurrentSlowModeKey proto.Option[int32] `protobuf:"varint,31,opt"`
CurrentSlowModeKey *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 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()
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 ""
}
type GuildChannelTopMsgInfo struct {
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()
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
}

View File

@ -3,7 +3,7 @@ syntax = "proto2";
package channel;
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/channel";
option go_package = "pb/channel;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 = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x346";
option go_package = "./;cmd0x346";
message ApplyCleanTrafficRsp {
int32 retCode = 10;
@ -197,8 +197,6 @@ message ApplyUploadReqV3 {
string localFilepath = 70;
int32 dangerLevel = 80;
int64 totalSpace = 90;
bytes md5 = 110;
bytes _3Sha = 120;
}
message ApplyUploadRsp {
int32 retCode = 10;
@ -250,7 +248,6 @@ message ApplyUploadRspV3 {
string uploadHttpsDomain = 150;
string uploadDns = 160;
string uploadLanip = 170;
bytes mediaPlateformUploadKey = 220;
}
message DelMessageReq {
int64 uinSender = 1;
@ -364,69 +361,61 @@ 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;
uint32 flagSupportMediaplatform = 200;
//ApplyCopyToReq applyCopyToReq = 90000;
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,77 +1,430 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/cmd0x352/cmd0x352.proto
// source: cmd0x352.proto
package cmd0x352
import (
proto "github.com/RomiChan/protobuf/proto"
)
type ReqBody struct {
Subcmd proto.Option[uint32] `protobuf:"varint,1,opt"`
TryupImgReq []*D352TryUpImgReq `protobuf:"bytes,2,rep"`
Subcmd *uint32 `protobuf:"varint,1,opt"`
TryupImgReq []*D352TryUpImgReq `protobuf:"bytes,2,rep"`
// repeated GetImgUrlReq getimgUrlReq = 3;
// repeated DelImgReq delImgReq = 4;
NetType proto.Option[uint32] `protobuf:"varint,10,opt"`
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
}
type RspBody struct {
Subcmd proto.Option[uint32] `protobuf:"varint,1,opt"`
TryupImgRsp []*TryUpImgRsp `protobuf:"bytes,2,rep"`
Subcmd *uint32 `protobuf:"varint,1,opt"`
TryupImgRsp []*TryUpImgRsp `protobuf:"bytes,2,rep"`
// repeated GetImgUrlRsp getimgUrlRsp = 3;
NewBigchan proto.Option[bool] `protobuf:"varint,4,opt"`
NewBigchan *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 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"`
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
}
type TryUpImgRsp struct {
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"`
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"`
// 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 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"`
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"`
// 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 proto.Option[uint32] `protobuf:"varint,66,opt"` // optional TryUpInfo4Busi info4Busi = 1001;
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
}

View File

@ -1,6 +1,6 @@
syntax = "proto2";
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x352";
option go_package = "./;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 = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x388";
option go_package = "./;cmd0x388";
message DelImgReq {
optional uint64 srcUin = 1;

View File

@ -1,83 +1,446 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/cmd0x3f6/cmd0x3f6.proto
// source: cmd0x3f6.proto
package cmd0x3f6
import (
proto "github.com/RomiChan/protobuf/proto"
)
type C3F6ReqBody struct {
SubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
SubCmd *uint32 `protobuf:"varint,1,opt"`
CrmCommonHead *C3F6CRMMsgHead `protobuf:"bytes,2,opt"`
SubcmdLoginProcessCompleteReqBody *QDUserLoginProcessCompleteReqBody `protobuf:"bytes,42,opt"`
_ [0]func()
}
func (x *C3F6ReqBody) GetSubCmd() uint32 {
if x != nil && x.SubCmd != nil {
return *x.SubCmd
}
return 0
}
func (x *C3F6ReqBody) GetCrmCommonHead() *C3F6CRMMsgHead {
if x != nil {
return x.CrmCommonHead
}
return nil
}
func (x *C3F6ReqBody) GetSubcmdLoginProcessCompleteReqBody() *QDUserLoginProcessCompleteReqBody {
if x != nil {
return x.SubcmdLoginProcessCompleteReqBody
}
return nil
}
type C3F6RspBody struct {
SubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
SubCmd *uint32 `protobuf:"varint,1,opt"`
CrmCommonHead *C3F6CRMMsgHead `protobuf:"bytes,2,opt"`
SubcmdLoginProcessCompleteRspBody *QDUserLoginProcessCompleteRspBody `protobuf:"bytes,42,opt"`
_ [0]func()
}
func (x *C3F6RspBody) GetSubCmd() uint32 {
if x != nil && x.SubCmd != nil {
return *x.SubCmd
}
return 0
}
func (x *C3F6RspBody) GetCrmCommonHead() *C3F6CRMMsgHead {
if x != nil {
return x.CrmCommonHead
}
return nil
}
func (x *C3F6RspBody) GetSubcmdLoginProcessCompleteRspBody() *QDUserLoginProcessCompleteRspBody {
if x != nil {
return x.SubcmdLoginProcessCompleteRspBody
}
return nil
}
type QDUserLoginProcessCompleteReqBody struct {
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"`
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
}
type QDUserLoginProcessCompleteRspBody struct {
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"`
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
}
type RetInfo struct {
RetCode proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrorMsg proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
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 ""
}
type C3F6CRMMsgHead struct {
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()
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
}

View File

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

View File

@ -1,131 +1,667 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/cmd0x6ff/smbcmd0x519.proto
// source: smbcmd0x519.proto
package cmd0x6ff
import (
proto "github.com/RomiChan/protobuf/proto"
)
type C519CRMMsgHead struct {
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()
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
}
type GetNavigationMenuReqBody struct {
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()
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
}
type GetNavigationMenuRspBody struct {
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()
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
}
type C519ReqBody struct {
SubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
SubCmd *uint32 `protobuf:"varint,1,opt"`
CrmCommonHead *C519CRMMsgHead `protobuf:"bytes,2,opt"`
GetAddressDetailListReqBody *GetAddressDetailListReqBody `protobuf:"bytes,33,opt"` // optional GetNavigationMenuReqBody getNavigationMenuReq = 35;
_ [0]func()
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
}
type C519RetInfo struct {
RetCode proto.Option[uint32] `protobuf:"varint,1,opt"`
ErrorMsg proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
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 ""
}
type C519RspBody struct {
SubCmd proto.Option[uint32] `protobuf:"varint,1,opt"`
// optional C519CRMMsgHead crmCommonHead = 2;
GetAddressDetailListRspBody *GetAddressDetailListRspBody `protobuf:"bytes,33,opt"` //optional GetNavigationMenuRspBody getNavigationMenuRsp = 35;
_ [0]func()
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
}
type GetAddressDetailListReqBody struct {
Timestamp proto.Option[uint32] `protobuf:"fixed32,1,opt"`
Timestamp2 proto.Option[uint64] `protobuf:"fixed64,2,opt"`
_ [0]func()
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
}
type GetAddressDetailListRspBody struct {
// 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"`
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
}
type AddressDetail struct {
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"`
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
}
type AddressMobileInfo struct {
Index proto.Option[uint32] `protobuf:"varint,1,opt"`
Account []byte `protobuf:"bytes,2,opt"`
FormattedAccount []byte `protobuf:"bytes,5,opt"`
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
}
type AddressQQinfo struct {
Index proto.Option[uint32] `protobuf:"varint,1,opt"`
Account proto.Option[uint64] `protobuf:"varint,2,opt"`
_ [0]func()
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
}
type NewBizClientRegion struct {
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()
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 ""
}
type NewBizClientRegionCode struct {
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()
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
}

View File

@ -1,5 +1,5 @@
syntax = "proto2";
option go_package = "github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff";
option go_package = "./;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,31 +1,95 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/cmd0x6ff/subcmd0x501.proto
// source: subcmd0x501.proto
package cmd0x6ff
import (
proto "github.com/RomiChan/protobuf/proto"
)
type C501ReqBody struct {
ReqBody *SubCmd0X501ReqBody `protobuf:"bytes,1281,opt"`
_ [0]func()
}
func (x *C501ReqBody) GetReqBody() *SubCmd0X501ReqBody {
if x != nil {
return x.ReqBody
}
return nil
}
type C501RspBody struct {
RspBody *SubCmd0X501RspBody `protobuf:"bytes,1281,opt"`
_ [0]func()
}
func (x *C501RspBody) GetRspBody() *SubCmd0X501RspBody {
if x != nil {
return x.RspBody
}
return nil
}
type SubCmd0X501ReqBody struct {
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"`
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
}
type SubCmd0X501RspBody struct {
@ -34,15 +98,77 @@ 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 proto.Option[uint32] `protobuf:"varint,1,opt"`
Addrs []*IpAddr `protobuf:"bytes,2,rep"`
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
}
type IpAddr struct {
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()
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
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,6 @@
syntax = "proto3";
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;
}
option go_package = "./;pb";
message DeviceInfo {
string bootloader = 1;

View File

@ -1,72 +1,263 @@
// Code generated by protoc-gen-golite. DO NOT EDIT.
// source: pb/exciting/group.proto
// source: group.proto
package exciting
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()
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"`
}
type FileUploadEntry struct {
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 {
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"`
_ [0]func()
}
func (x *GroupFileUploadEntry) GetBusiBuff() *ExcitingBusiInfo {
if x != nil {
return x.BusiBuff
}
return nil
}
func (x *GroupFileUploadEntry) GetFileEntry() *ExcitingFileEntry {
if x != nil {
return x.FileEntry
}
return nil
}
func (x *GroupFileUploadEntry) GetClientInfo() *ExcitingClientInfo {
if x != nil {
return x.ClientInfo
}
return nil
}
func (x *GroupFileUploadEntry) GetFileNameInfo() *ExcitingFileNameInfo {
if x != nil {
return x.FileNameInfo
}
return nil
}
func (x *GroupFileUploadEntry) GetHost() *ExcitingHostConfig {
if x != nil {
return x.Host
}
return nil
}
type ExcitingBusiInfo struct {
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()
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
}
type ExcitingFileEntry struct {
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"`
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
}
type ExcitingClientInfo struct {
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()
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
}
type ExcitingFileNameInfo struct {
FileName proto.Option[string] `protobuf:"bytes,100,opt"`
_ [0]func()
FileName *string `protobuf:"bytes,100,opt"`
}
func (x *ExcitingFileNameInfo) GetFileName() string {
if x != nil && x.FileName != nil {
return *x.FileName
}
return ""
}
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 proto.Option[int32] `protobuf:"varint,2,opt"`
_ [0]func()
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
}
type ExcitingUrlInfo struct {
Unknown proto.Option[int32] `protobuf:"varint,1,opt"` // not https?
Host proto.Option[string] `protobuf:"bytes,2,opt"`
_ [0]func()
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 ""
}

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