mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 11:07:40 +08:00
first commit.
This commit is contained in:
commit
ddfd670dac
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
416
binary/jce/reader.go
Normal file
416
binary/jce/reader.go
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
package jce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JceReader struct {
|
||||||
|
buf *bytes.Reader
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeadData struct {
|
||||||
|
Type byte
|
||||||
|
Tag int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJceReader(data []byte) *JceReader {
|
||||||
|
buf := bytes.NewReader(data)
|
||||||
|
return &JceReader{buf: buf, data: data}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readHead() (hd *HeadData, l int32) {
|
||||||
|
hd = &HeadData{}
|
||||||
|
b, _ := r.buf.ReadByte()
|
||||||
|
hd.Type = b & 0xF
|
||||||
|
hd.Tag = (int(b) & 0xF0) >> 4
|
||||||
|
if hd.Tag == 15 {
|
||||||
|
b, _ = r.buf.ReadByte()
|
||||||
|
hd.Tag = int(b) & 0xFF
|
||||||
|
return hd, 2
|
||||||
|
}
|
||||||
|
return hd, 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) peakHead() (hd *HeadData, l int32) {
|
||||||
|
offset := r.buf.Size() - int64(r.buf.Len())
|
||||||
|
n := NewJceReader(r.data[offset:])
|
||||||
|
return n.readHead()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) skip(l int) {
|
||||||
|
r.readBytes(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) skipField(t byte) {
|
||||||
|
switch t {
|
||||||
|
case 0:
|
||||||
|
r.skip(1)
|
||||||
|
case 1:
|
||||||
|
r.skip(2)
|
||||||
|
case 2, 4:
|
||||||
|
r.skip(4)
|
||||||
|
case 3, 5:
|
||||||
|
r.skip(8)
|
||||||
|
case 6:
|
||||||
|
b, _ := r.buf.ReadByte()
|
||||||
|
r.skip(int(b))
|
||||||
|
case 7:
|
||||||
|
r.skip(int(r.readInt32()))
|
||||||
|
case 8:
|
||||||
|
s := r.ReadInt32(0)
|
||||||
|
for i := 0; i < int(s)*2; i++ {
|
||||||
|
r.skipNextField()
|
||||||
|
}
|
||||||
|
case 9:
|
||||||
|
s := r.ReadInt32(0)
|
||||||
|
for i := 0; i < int(s); i++ {
|
||||||
|
r.skipNextField()
|
||||||
|
}
|
||||||
|
case 13:
|
||||||
|
r.readHead()
|
||||||
|
s := r.ReadInt32(0)
|
||||||
|
r.skip(int(s))
|
||||||
|
case 10:
|
||||||
|
r.skipToStructEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) skipNextField() {
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
r.skipField(hd.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) SkipField(c int) {
|
||||||
|
for i := 0; i < c; i++ {
|
||||||
|
r.skipNextField()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readBytes(len int) []byte {
|
||||||
|
b := make([]byte, len)
|
||||||
|
_, err := r.buf.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readByte() byte {
|
||||||
|
return r.readBytes(1)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readUInt16() uint16 {
|
||||||
|
f, _ := r.buf.ReadByte()
|
||||||
|
s, err := r.buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return uint16((int32(f) << 8) + int32(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readInt32() int32 {
|
||||||
|
b := r.readBytes(4)
|
||||||
|
return (int32(b[0]) << 24) | (int32(b[1]) << 16) | (int32(b[2]) << 8) | int32(b[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readInt64() int64 {
|
||||||
|
b := r.readBytes(8)
|
||||||
|
return (int64(b[0]) << 56) | (int64(b[1]) << 48) | (int64(b[2]) << 40) | (int64(b[3]) << 32) | (int64(b[4]) << 24) | (int64(b[5]) << 16) | (int64(b[6]) << 8) | int64(b[7])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readFloat32() float32 {
|
||||||
|
b := r.readInt32()
|
||||||
|
return math.Float32frombits(uint32(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readFloat64() float64 {
|
||||||
|
b := r.readInt64()
|
||||||
|
return math.Float64frombits(uint64(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) skipToTag(tag int) bool {
|
||||||
|
for {
|
||||||
|
hd, l := r.peakHead()
|
||||||
|
if tag <= hd.Tag || hd.Type == 11 {
|
||||||
|
return tag == hd.Tag
|
||||||
|
}
|
||||||
|
r.skip(int(l))
|
||||||
|
r.skipField(hd.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) skipToStructEnd() {
|
||||||
|
for {
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
r.skipField(hd.Type)
|
||||||
|
if hd.Type == 11 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadByte(tag int) byte {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
switch hd.Type {
|
||||||
|
case 12:
|
||||||
|
return 0
|
||||||
|
case 0:
|
||||||
|
return r.readByte()
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadBool(tag int) bool {
|
||||||
|
return r.ReadByte(tag) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadInt16(tag int) int16 {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
switch hd.Type {
|
||||||
|
case 12:
|
||||||
|
return 0
|
||||||
|
case 0:
|
||||||
|
return int16(r.readByte())
|
||||||
|
case 1:
|
||||||
|
return int16(r.readUInt16())
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadInt32(tag int) int32 {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
switch hd.Type {
|
||||||
|
case 12:
|
||||||
|
return 0
|
||||||
|
case 0:
|
||||||
|
return int32(r.readByte())
|
||||||
|
case 1:
|
||||||
|
return int32(r.readUInt16())
|
||||||
|
case 2:
|
||||||
|
return r.readInt32()
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadInt64(tag int) int64 {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
switch hd.Type {
|
||||||
|
case 12:
|
||||||
|
return 0
|
||||||
|
case 0:
|
||||||
|
return int64(r.readByte())
|
||||||
|
case 1:
|
||||||
|
return int64(int16(r.readUInt16()))
|
||||||
|
case 2:
|
||||||
|
return int64(r.readInt32())
|
||||||
|
case 3:
|
||||||
|
return r.readInt64()
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadFloat32(tag int) float32 {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
switch hd.Type {
|
||||||
|
case 12:
|
||||||
|
return 0
|
||||||
|
case 4:
|
||||||
|
return r.readFloat32()
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadFloat64(tag int) float64 {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
switch hd.Type {
|
||||||
|
case 12:
|
||||||
|
return 0
|
||||||
|
case 4:
|
||||||
|
return float64(r.readFloat32())
|
||||||
|
case 5:
|
||||||
|
return r.readFloat64()
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadString(tag int) string {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
switch hd.Type {
|
||||||
|
case 6:
|
||||||
|
return string(r.readBytes(int(r.readByte())))
|
||||||
|
case 7:
|
||||||
|
return string(r.readBytes(int(r.readInt32())))
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAny Read any type via tag, unsupported JceStruct
|
||||||
|
func (r *JceReader) ReadAny(tag int) interface{} {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
switch hd.Type {
|
||||||
|
case 0:
|
||||||
|
return r.readByte()
|
||||||
|
case 1:
|
||||||
|
return r.readUInt16()
|
||||||
|
case 2:
|
||||||
|
return r.readInt32()
|
||||||
|
case 3:
|
||||||
|
return r.readInt64()
|
||||||
|
case 4:
|
||||||
|
return r.readFloat32()
|
||||||
|
case 5:
|
||||||
|
return r.readFloat64()
|
||||||
|
case 6:
|
||||||
|
return string(r.readBytes(int(r.readByte())))
|
||||||
|
case 7:
|
||||||
|
return string(r.readBytes(int(r.readInt32())))
|
||||||
|
case 8:
|
||||||
|
s := r.ReadInt32(0)
|
||||||
|
m := make(map[interface{}]interface{})
|
||||||
|
for i := 0; i < int(s); i++ {
|
||||||
|
m[r.ReadAny(0)] = r.ReadAny(1)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
case 9:
|
||||||
|
var sl []interface{}
|
||||||
|
s := r.ReadInt32(0)
|
||||||
|
for i := 0; i < int(s); i++ {
|
||||||
|
sl = append(sl, r.ReadAny(0))
|
||||||
|
}
|
||||||
|
return sl
|
||||||
|
case 12:
|
||||||
|
return 0
|
||||||
|
case 13:
|
||||||
|
r.readHead()
|
||||||
|
return r.readBytes(int(r.ReadInt32(0)))
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadMapF(tag int, f func(interface{}, interface{})) {
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.readHead()
|
||||||
|
s := r.ReadInt32(0)
|
||||||
|
for i := 0; i < int(s); i++ {
|
||||||
|
k := r.ReadAny(0)
|
||||||
|
v := r.ReadAny(1)
|
||||||
|
if k != nil {
|
||||||
|
f(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) readObject(t reflect.Type, tag int) reflect.Value {
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
var i int64
|
||||||
|
r.ReadObject(&i, tag)
|
||||||
|
return reflect.ValueOf(i)
|
||||||
|
case reflect.String:
|
||||||
|
var s string
|
||||||
|
r.ReadObject(&s, tag)
|
||||||
|
return reflect.ValueOf(s)
|
||||||
|
case reflect.Slice:
|
||||||
|
s := reflect.New(t.Elem()).Interface().(IJecStruct)
|
||||||
|
r.readHead()
|
||||||
|
s.ReadFrom(r)
|
||||||
|
r.skipToStructEnd()
|
||||||
|
return reflect.ValueOf(s).Elem()
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadSlice(i interface{}, tag int) {
|
||||||
|
t := reflect.TypeOf(i)
|
||||||
|
v := reflect.ValueOf(i).Elem()
|
||||||
|
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Slice {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !r.skipToTag(tag) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hd, _ := r.readHead()
|
||||||
|
if hd.Type == 9 {
|
||||||
|
s := r.ReadInt32(0)
|
||||||
|
for i := 0; i < int(s); i++ {
|
||||||
|
val := r.readObject(t.Elem(), 0)
|
||||||
|
v.Set(reflect.Append(v, val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hd.Type == 13 {
|
||||||
|
r.readHead()
|
||||||
|
arr := r.readBytes(int(r.ReadInt32(0)))
|
||||||
|
for _, b := range arr {
|
||||||
|
v.Set(reflect.Append(v, reflect.ValueOf(b)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *JceReader) ReadObject(i interface{}, tag int) {
|
||||||
|
va := reflect.ValueOf(i)
|
||||||
|
if va.Kind() != reflect.Ptr || va.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch o := i.(type) {
|
||||||
|
case *byte:
|
||||||
|
*o = r.ReadByte(tag)
|
||||||
|
case *bool:
|
||||||
|
*o = r.ReadBool(tag)
|
||||||
|
case *int16:
|
||||||
|
*o = r.ReadInt16(tag)
|
||||||
|
case *int:
|
||||||
|
*o = int(r.ReadInt32(tag))
|
||||||
|
case *int32:
|
||||||
|
*o = r.ReadInt32(tag)
|
||||||
|
case *int64:
|
||||||
|
*o = r.ReadInt64(tag)
|
||||||
|
case *float32:
|
||||||
|
*o = r.ReadFloat32(tag)
|
||||||
|
case *float64:
|
||||||
|
*o = r.ReadFloat64(tag)
|
||||||
|
case *string:
|
||||||
|
*o = r.ReadString(tag)
|
||||||
|
case IJecStruct:
|
||||||
|
o.ReadFrom(r)
|
||||||
|
}
|
||||||
|
}
|
397
binary/jce/structs.go
Normal file
397
binary/jce/structs.go
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
package jce
|
||||||
|
|
||||||
|
type IJecStruct interface {
|
||||||
|
ToBytes() []byte
|
||||||
|
ReadFrom(*JceReader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: code gen
|
||||||
|
type (
|
||||||
|
RequestPacket struct {
|
||||||
|
IVersion int16 `jceId:"1"`
|
||||||
|
CPacketType byte `jceId:"2"`
|
||||||
|
IMessageType int32 `jceId:"3"`
|
||||||
|
IRequestId int32 `jceId:"4"`
|
||||||
|
SServantName string `jceId:"5"`
|
||||||
|
SFuncName string `jceId:"6"`
|
||||||
|
SBuffer []byte `jceId:"7"`
|
||||||
|
ITimeout int32 `jceId:"8"`
|
||||||
|
Context map[string]string `jceId:"9"`
|
||||||
|
Status map[string]string `jceId:"10"`
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestDataVersion3 struct {
|
||||||
|
Map map[string][]byte `jceId:"0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestDataVersion2 struct {
|
||||||
|
Map map[string]map[string][]byte `jceId:"0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
SvcReqRegister struct {
|
||||||
|
Uin int64 `jceId:"0"`
|
||||||
|
Bid int64 `jceId:"1"`
|
||||||
|
ConnType byte `jceId:"2"`
|
||||||
|
Other string `jceId:"3"`
|
||||||
|
Status int32 `jceId:"4"`
|
||||||
|
OnlinePush byte `jceId:"5"`
|
||||||
|
IsOnline byte `jceId:"6"`
|
||||||
|
IsShowOnline byte `jceId:"7"`
|
||||||
|
KickPC byte `jceId:"8"`
|
||||||
|
KickWeak byte `jceId:"9"`
|
||||||
|
Timestamp int64 `jceId:"10"`
|
||||||
|
IOSVersion int64 `jceId:"11"`
|
||||||
|
NetType byte `jceId:"12"`
|
||||||
|
BuildVer string `jceId:"13"`
|
||||||
|
RegType byte `jceId:"14"`
|
||||||
|
DevParam []byte `jceId:"15"`
|
||||||
|
Guid []byte `jceId:"16"`
|
||||||
|
LocaleId int32 `jceId:"17"`
|
||||||
|
SilentPush byte `jceId:"18"`
|
||||||
|
DevName string `jceId:"19"`
|
||||||
|
DevType string `jceId:"20"`
|
||||||
|
OSVer string `jceId:"21"`
|
||||||
|
OpenPush byte `jceId:"22"`
|
||||||
|
LargeSeq int64 `jceId:"23"`
|
||||||
|
LastWatchStartTime int64 `jceId:"24"`
|
||||||
|
OldSSOIp int64 `jceId:"26"`
|
||||||
|
NewSSOIp int64 `jceId:"27"`
|
||||||
|
ChannelNo string `jceId:"28"`
|
||||||
|
CPID int64 `jceId:"29"`
|
||||||
|
VendorName string `jceId:"30"`
|
||||||
|
VendorOSName string `jceId:"31"`
|
||||||
|
IOSIdfa string `jceId:"32"`
|
||||||
|
B769 []byte `jceId:"33"`
|
||||||
|
IsSetStatus byte `jceId:"34"`
|
||||||
|
ServerBuf []byte `jceId:"35"`
|
||||||
|
SetMute byte `jceId:"36"`
|
||||||
|
}
|
||||||
|
|
||||||
|
FriendListRequest struct {
|
||||||
|
Reqtype int32 `jceId:"0"`
|
||||||
|
IfReflush byte `jceId:"1"`
|
||||||
|
Uin int64 `jceId:"2"`
|
||||||
|
StartIndex int16 `jceId:"3"`
|
||||||
|
FriendCount int16 `jceId:"4"`
|
||||||
|
GroupId byte `jceId:"5"`
|
||||||
|
IfGetGroupInfo byte `jceId:"6"`
|
||||||
|
GroupStartIndex byte `jceId:"7"`
|
||||||
|
GroupCount byte `jceId:"8"`
|
||||||
|
IfGetMSFGroup byte `jceId:"9"`
|
||||||
|
IfShowTermType byte `jceId:"10"`
|
||||||
|
Version int64 `jceId:"11"`
|
||||||
|
UinList []int64 `jceId:"12"`
|
||||||
|
AppType int32 `jceId:"13"`
|
||||||
|
IfGetDOVId byte `jceId:"14"`
|
||||||
|
IfGetBothFlag byte `jceId:"15"`
|
||||||
|
D50 []byte `jceId:"16"`
|
||||||
|
D6B []byte `jceId:"17"`
|
||||||
|
SnsTypeList []int64 `jceId:"18"`
|
||||||
|
}
|
||||||
|
|
||||||
|
FriendInfo struct {
|
||||||
|
FriendUin int64 `jceId:"0"`
|
||||||
|
GroupId byte `jceId:"1"`
|
||||||
|
FaceId int16 `jceId:"2"`
|
||||||
|
Remark string `jceId:"3"`
|
||||||
|
QQType byte `jceId:"4"`
|
||||||
|
Status byte `jceId:"5"`
|
||||||
|
MemberLevel byte `jceId:"6"`
|
||||||
|
IsMqqOnLine byte `jceId:"7"`
|
||||||
|
QQOnlineState byte `jceId:"8"`
|
||||||
|
IsIphoneOnline byte `jceId:"9"`
|
||||||
|
DetailStatusFlag byte `jceId:"10"`
|
||||||
|
QQOnlineStateV2 byte `jceId:"11"`
|
||||||
|
ShowName string `jceId:"12"`
|
||||||
|
IsRemark byte `jceId:"13"`
|
||||||
|
Nick string `jceId:"14"`
|
||||||
|
SpecialFlag byte `jceId:"15"`
|
||||||
|
IMGroupID []byte `jceId:"16"`
|
||||||
|
MSFGroupID []byte `jceId:"17"`
|
||||||
|
TermType int32 `jceId:"18"`
|
||||||
|
Network byte `jceId:"20"`
|
||||||
|
Ring []byte `jceId:"21"`
|
||||||
|
AbiFlag int64 `jceId:"22"`
|
||||||
|
FaceAddonId int64 `jceId:"23"`
|
||||||
|
NetworkType int32 `jceId:"24"`
|
||||||
|
VipFont int64 `jceId:"25"`
|
||||||
|
IconType int32 `jceId:"26"`
|
||||||
|
TermDesc string `jceId:"27"`
|
||||||
|
ColorRing int64 `jceId:"28"`
|
||||||
|
ApolloFlag byte `jceId:"29"`
|
||||||
|
ApolloTimestamp int64 `jceId:"30"`
|
||||||
|
Sex byte `jceId:"31"`
|
||||||
|
FounderFont int64 `jceId:"32"`
|
||||||
|
EimId string `jceId:"33"`
|
||||||
|
EimMobile string `jceId:"34"`
|
||||||
|
OlympicTorch byte `jceId:"35"`
|
||||||
|
ApolloSignTime int64 `jceId:"36"`
|
||||||
|
LaviUin int64 `jceId:"37"`
|
||||||
|
TagUpdateTime int64 `jceId:"38"`
|
||||||
|
GameLastLoginTime int64 `jceId:"39"`
|
||||||
|
GameAppId int64 `jceId:"40"`
|
||||||
|
CardID []byte `jceId:"41"`
|
||||||
|
BitSet int64 `jceId:"42"`
|
||||||
|
KingOfGloryFlag byte `jceId:"43"`
|
||||||
|
KingOfGloryRank int64 `jceId:"44"`
|
||||||
|
MasterUin string `jceId:"45"`
|
||||||
|
LastMedalUpdateTime int64 `jceId:"46"`
|
||||||
|
FaceStoreId int64 `jceId:"47"`
|
||||||
|
FontEffect int64 `jceId:"48"`
|
||||||
|
DOVId string `jceId:"49"`
|
||||||
|
BothFlag int64 `jceId:"50"`
|
||||||
|
CentiShow3DFlag byte `jceId:"51"`
|
||||||
|
IntimateInfo []byte `jceId:"52"`
|
||||||
|
ShowNameplate byte `jceId:"53"`
|
||||||
|
NewLoverDiamondFlag byte `jceId:"54"`
|
||||||
|
ExtSnsFrdData []byte `jceId:"55"`
|
||||||
|
MutualMarkData []byte `jceId:"56"`
|
||||||
|
}
|
||||||
|
|
||||||
|
TroopListRequest struct {
|
||||||
|
Uin int64 `jceId:"0"`
|
||||||
|
GetMSFMsgFlag byte `jceId:"1"`
|
||||||
|
Cookies []byte `jceId:"2"`
|
||||||
|
GroupInfo []int64 `jceId:"3"`
|
||||||
|
GroupFlagExt byte `jceId:"4"`
|
||||||
|
Version int32 `jceId:"5"`
|
||||||
|
CompanyId int64 `jceId:"6"`
|
||||||
|
VersionNum int64 `jceId:"7"`
|
||||||
|
GetLongGroupName byte `jceId:"8"`
|
||||||
|
}
|
||||||
|
|
||||||
|
TroopNumber struct {
|
||||||
|
GroupUin int64 `jceId:"0"`
|
||||||
|
GroupCode int64 `jceId:"1"`
|
||||||
|
Flag byte `jceId:"2"`
|
||||||
|
GroupInfoSeq int64 `jceId:"3"`
|
||||||
|
GroupName string `jceId:"4"`
|
||||||
|
GroupMemo string `jceId:"5"`
|
||||||
|
GroupFlagExt int64 `jceId:"6"`
|
||||||
|
GroupRankSeq int64 `jceId:"7"`
|
||||||
|
CertificationType int64 `jceId:"8"`
|
||||||
|
ShutUpTimestamp int64 `jceId:"9"`
|
||||||
|
MyShutUpTimestamp int64 `jceId:"10"`
|
||||||
|
CmdUinUinFlag int64 `jceId:"11"`
|
||||||
|
AdditionalFlag int64 `jceId:"12"`
|
||||||
|
GroupTypeFlag int64 `jceId:"13"`
|
||||||
|
GroupSecType int64 `jceId:"14"`
|
||||||
|
GroupSecTypeInfo int64 `jceId:"15"`
|
||||||
|
GroupClassExt int64 `jceId:"16"`
|
||||||
|
AppPrivilegeFlag int64 `jceId:"17"`
|
||||||
|
SubscriptionUin int64 `jceId:"18"`
|
||||||
|
MemberNum int64 `jceId:"19"`
|
||||||
|
MemberNumSeq int64 `jceId:"20"`
|
||||||
|
MemberCardSeq int64 `jceId:"21"`
|
||||||
|
GroupFlagExt3 int64 `jceId:"22"`
|
||||||
|
GroupOwnerUin int64 `jceId:"23"`
|
||||||
|
IsConfGroup byte `jceId:"24"`
|
||||||
|
IsModifyConfGroupFace byte `jceId:"25"`
|
||||||
|
IsModifyConfGroupName byte `jceId:"26"`
|
||||||
|
CmdUinJoinTime int64 `jceId:"27"`
|
||||||
|
CompanyId int64 `jceId:"28"`
|
||||||
|
MaxGroupMemberNum int64 `jceId:"29"`
|
||||||
|
CmdUinGroupMask int64 `jceId:"30"`
|
||||||
|
GuildAppId int64 `jceId:"31"`
|
||||||
|
GuildSubType int64 `jceId:"32"`
|
||||||
|
CmdUinRingtoneID int64 `jceId:"33"`
|
||||||
|
CmdUinFlagEx2 int64 `jceId:"34"`
|
||||||
|
}
|
||||||
|
|
||||||
|
TroopMemberListRequest struct {
|
||||||
|
Uin int64 `jceId:"0"`
|
||||||
|
GroupCode int64 `jceId:"1"`
|
||||||
|
NextUin int64 `jceId:"2"`
|
||||||
|
GroupUin int64 `jceId:"3"`
|
||||||
|
Version int64 `jceId:"4"`
|
||||||
|
ReqType int64 `jceId:"5"`
|
||||||
|
GetListAppointTime int64 `jceId:"6"`
|
||||||
|
RichCardNameVer byte `jceId:"7"`
|
||||||
|
}
|
||||||
|
|
||||||
|
TroopMemberInfo struct {
|
||||||
|
MemberUin int64 `jceId:"0"`
|
||||||
|
FaceId int16 `jceId:"1"`
|
||||||
|
Age byte `jceId:"2"`
|
||||||
|
Gender byte `jceId:"3"`
|
||||||
|
Nick string `jceId:"4"`
|
||||||
|
Status byte `jceId:"5"`
|
||||||
|
ShowName string `jceId:"6"`
|
||||||
|
Name string `jceId:"8"`
|
||||||
|
Memo string `jceId:"12"`
|
||||||
|
AutoRemark string `jceId:"13"`
|
||||||
|
MemberLevel int64 `jceId:"14"`
|
||||||
|
JoinTime int64 `jceId:"15"`
|
||||||
|
LastSpeakTime int64 `jceId:"16"`
|
||||||
|
CreditLevel int64 `jceId:"17"`
|
||||||
|
Flag int64 `jceId:"18"`
|
||||||
|
FlagExt int64 `jceId:"19"`
|
||||||
|
Point int64 `jceId:"20"`
|
||||||
|
Concerned byte `jceId:"21"`
|
||||||
|
Shielded byte `jceId:"22"`
|
||||||
|
SpecialTitle string `jceId:"23"`
|
||||||
|
SpecialTitleExpireTime int64 `jceId:"24"`
|
||||||
|
Job string `jceId:"25"`
|
||||||
|
ApolloFlag byte `jceId:"26"`
|
||||||
|
ApolloTimestamp int64 `jceId:"27"`
|
||||||
|
GlobalGroupLevel int64 `jceId:"28"`
|
||||||
|
TitleId int64 `jceId:"29"`
|
||||||
|
ShutUpTimestap int64 `jceId:"30"`
|
||||||
|
GlobalGroupPoint int64 `jceId:"31"`
|
||||||
|
RichCardNameVer byte `jceId:"33"`
|
||||||
|
VipType int64 `jceId:"34"`
|
||||||
|
VipLevel int64 `jceId:"35"`
|
||||||
|
BigClubLevel int64 `jceId:"36"`
|
||||||
|
BigClubFlag int64 `jceId:"37"`
|
||||||
|
Nameplate int64 `jceId:"38"`
|
||||||
|
GroupHonor []byte `jceId:"39"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (pkt *RequestPacket) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *RequestPacket) ReadFrom(r *JceReader) {
|
||||||
|
pkt.SBuffer = []byte{}
|
||||||
|
pkt.Context = make(map[string]string)
|
||||||
|
pkt.Status = make(map[string]string)
|
||||||
|
pkt.IVersion = r.ReadInt16(1)
|
||||||
|
pkt.CPacketType = r.ReadByte(2)
|
||||||
|
pkt.IMessageType = r.ReadInt32(3)
|
||||||
|
pkt.IRequestId = r.ReadInt32(4)
|
||||||
|
pkt.SServantName = r.ReadString(5)
|
||||||
|
pkt.SFuncName = r.ReadString(6)
|
||||||
|
r.ReadSlice(&pkt.SBuffer, 7)
|
||||||
|
pkt.ITimeout = r.ReadInt32(8)
|
||||||
|
r.ReadMapF(9, func(k interface{}, v interface{}) { pkt.Context[k.(string)] = v.(string) })
|
||||||
|
r.ReadMapF(10, func(k interface{}, v interface{}) { pkt.Status[k.(string)] = v.(string) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *RequestDataVersion3) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *RequestDataVersion3) ReadFrom(r *JceReader) {
|
||||||
|
pkt.Map = make(map[string][]byte)
|
||||||
|
r.ReadMapF(0, func(k interface{}, v interface{}) {
|
||||||
|
pkt.Map[k.(string)] = v.([]byte)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *RequestDataVersion2) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *RequestDataVersion2) ReadFrom(r *JceReader) {
|
||||||
|
pkt.Map = make(map[string]map[string][]byte)
|
||||||
|
r.ReadMapF(0, func(k interface{}, v interface{}) {
|
||||||
|
pkt.Map[k.(string)] = make(map[string][]byte)
|
||||||
|
for k2, v := range v.(map[interface{}]interface{}) {
|
||||||
|
pkt.Map[k.(string)][k2.(string)] = v.([]byte)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *SvcReqRegister) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *SvcReqRegister) ReadFrom(r *JceReader) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *FriendListRequest) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *FriendListRequest) ReadFrom(r *JceReader) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *FriendInfo) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *FriendInfo) ReadFrom(r *JceReader) {
|
||||||
|
pkt.FriendUin = r.ReadInt64(0)
|
||||||
|
pkt.GroupId = r.ReadByte(1)
|
||||||
|
pkt.FaceId = r.ReadInt16(2)
|
||||||
|
pkt.Remark = r.ReadString(3)
|
||||||
|
pkt.Status = r.ReadByte(5)
|
||||||
|
pkt.MemberLevel = r.ReadByte(6)
|
||||||
|
pkt.Nick = r.ReadString(14)
|
||||||
|
pkt.Network = r.ReadByte(20)
|
||||||
|
pkt.NetworkType = r.ReadInt32(24)
|
||||||
|
pkt.CardID = []byte{}
|
||||||
|
r.ReadObject(&pkt.CardID, 41)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *TroopListRequest) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *TroopListRequest) ReadFrom(r *JceReader) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *TroopNumber) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *TroopNumber) ReadFrom(r *JceReader) {
|
||||||
|
pkt.GroupUin = r.ReadInt64(0)
|
||||||
|
pkt.GroupCode = r.ReadInt64(1)
|
||||||
|
pkt.GroupName = r.ReadString(4)
|
||||||
|
pkt.GroupMemo = r.ReadString(5)
|
||||||
|
pkt.MemberNum = r.ReadInt64(19)
|
||||||
|
pkt.GroupOwnerUin = r.ReadInt64(23)
|
||||||
|
pkt.MaxGroupMemberNum = r.ReadInt64(29)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *TroopMemberListRequest) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *TroopMemberListRequest) ReadFrom(r *JceReader) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *TroopMemberInfo) ToBytes() []byte {
|
||||||
|
w := NewJceWriter()
|
||||||
|
w.WriteJceStructRaw(pkt)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *TroopMemberInfo) ReadFrom(r *JceReader) {
|
||||||
|
pkt.MemberUin = r.ReadInt64(0)
|
||||||
|
pkt.FaceId = r.ReadInt16(1)
|
||||||
|
pkt.Nick = r.ReadString(4)
|
||||||
|
pkt.ShowName = r.ReadString(6)
|
||||||
|
pkt.Name = r.ReadString(8)
|
||||||
|
pkt.AutoRemark = r.ReadString(13)
|
||||||
|
pkt.MemberLevel = r.ReadInt64(14)
|
||||||
|
pkt.JoinTime = r.ReadInt64(15)
|
||||||
|
pkt.LastSpeakTime = r.ReadInt64(16)
|
||||||
|
pkt.SpecialTitle = r.ReadString(23)
|
||||||
|
pkt.SpecialTitleExpireTime = r.ReadInt64(24)
|
||||||
|
pkt.Job = r.ReadString(25)
|
||||||
|
}
|
192
binary/jce/writer.go
Normal file
192
binary/jce/writer.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package jce
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
goBinary "encoding/binary"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JceWriter struct {
|
||||||
|
buf *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJceWriter() *JceWriter {
|
||||||
|
return &JceWriter{buf: new(bytes.Buffer)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) writeHead(t byte, tag int) {
|
||||||
|
if tag < 15 {
|
||||||
|
b := byte(tag<<4) | t
|
||||||
|
w.buf.WriteByte(b)
|
||||||
|
} else if tag < 256 {
|
||||||
|
b := 0xF0 | t
|
||||||
|
w.buf.WriteByte(b)
|
||||||
|
w.buf.WriteByte(byte(tag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteByte(b byte, tag int) {
|
||||||
|
if b == 0 {
|
||||||
|
w.writeHead(12, tag)
|
||||||
|
} else {
|
||||||
|
w.writeHead(0, tag)
|
||||||
|
w.buf.WriteByte(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteBool(b bool, tag int) {
|
||||||
|
var by byte = 0
|
||||||
|
if b {
|
||||||
|
by = 1
|
||||||
|
}
|
||||||
|
w.WriteByte(by, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteInt16(n int16, tag int) {
|
||||||
|
if n >= -128 && n <= 127 {
|
||||||
|
w.WriteByte(byte(n), tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.writeHead(1, tag)
|
||||||
|
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteInt32(n int32, tag int) {
|
||||||
|
if n >= -32768 && n <= 32767 { // ? if ((n >= 32768) && (n <= 32767))
|
||||||
|
w.WriteInt16(int16(n), tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.writeHead(2, tag)
|
||||||
|
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteInt64(n int64, tag int) {
|
||||||
|
if n >= -2147483648 && n <= 2147483647 {
|
||||||
|
w.WriteInt32(int32(n), tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.writeHead(3, tag)
|
||||||
|
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteFloat32(n float32, tag int) {
|
||||||
|
w.writeHead(4, tag)
|
||||||
|
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteFloat64(n float64, tag int) {
|
||||||
|
w.writeHead(5, tag)
|
||||||
|
_ = goBinary.Write(w.buf, goBinary.BigEndian, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteString(s string, tag int) {
|
||||||
|
by := []byte(s)
|
||||||
|
if len(by) > 255 {
|
||||||
|
w.writeHead(7, tag)
|
||||||
|
_ = goBinary.Write(w.buf, goBinary.BigEndian, len(by))
|
||||||
|
w.buf.Write(by)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.writeHead(6, tag)
|
||||||
|
w.buf.WriteByte(byte(len(by)))
|
||||||
|
w.buf.Write(by)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteBytes(l []byte, tag int) {
|
||||||
|
w.writeHead(13, tag)
|
||||||
|
w.writeHead(0, 0)
|
||||||
|
w.WriteInt32(int32(len(l)), 0)
|
||||||
|
w.buf.Write(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteInt64Slice(l []int64, tag int) {
|
||||||
|
w.writeHead(9, tag)
|
||||||
|
if len(l) == 0 {
|
||||||
|
w.WriteInt32(0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteInt32(int32(len(l)), 0)
|
||||||
|
for _, v := range l {
|
||||||
|
w.WriteInt64(v, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteMap(m interface{}, tag int) {
|
||||||
|
if m == nil {
|
||||||
|
w.writeHead(8, tag)
|
||||||
|
w.WriteInt32(0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
va := reflect.ValueOf(m)
|
||||||
|
if va.Kind() != reflect.Map {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.writeHead(8, tag)
|
||||||
|
w.WriteInt32(int32(len(va.MapKeys())), 0)
|
||||||
|
for _, k := range va.MapKeys() {
|
||||||
|
v := va.MapIndex(k)
|
||||||
|
w.WriteObject(k.Interface(), 0)
|
||||||
|
w.WriteObject(v.Interface(), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteObject(i interface{}, tag int) {
|
||||||
|
t := reflect.TypeOf(i)
|
||||||
|
if t.Kind() == reflect.Map {
|
||||||
|
w.WriteMap(i, tag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch o := i.(type) {
|
||||||
|
case byte:
|
||||||
|
w.WriteByte(o, tag)
|
||||||
|
case bool:
|
||||||
|
w.WriteBool(o, tag)
|
||||||
|
case int16:
|
||||||
|
w.WriteInt16(o, tag)
|
||||||
|
case int32:
|
||||||
|
w.WriteInt32(o, tag)
|
||||||
|
case int64:
|
||||||
|
w.WriteInt64(o, tag)
|
||||||
|
case float32:
|
||||||
|
w.WriteFloat32(o, tag)
|
||||||
|
case float64:
|
||||||
|
w.WriteFloat64(o, tag)
|
||||||
|
case string:
|
||||||
|
w.WriteString(o, tag)
|
||||||
|
case []byte:
|
||||||
|
w.WriteBytes(o, tag)
|
||||||
|
case []int64:
|
||||||
|
w.WriteInt64Slice(o, tag)
|
||||||
|
case IJecStruct:
|
||||||
|
w.WriteJceStruct(o, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteJceStructRaw(s IJecStruct) {
|
||||||
|
var (
|
||||||
|
t = reflect.TypeOf(s).Elem()
|
||||||
|
v = reflect.ValueOf(s).Elem()
|
||||||
|
)
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
strId := t.Field(i).Tag.Get("jceId")
|
||||||
|
if strId == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
id, err := strconv.Atoi(strId)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
w.WriteObject(v.Field(i).Interface(), id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) WriteJceStruct(s IJecStruct, tag int) {
|
||||||
|
w.writeHead(10, tag)
|
||||||
|
w.WriteJceStructRaw(s)
|
||||||
|
w.writeHead(11, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *JceWriter) Bytes() []byte {
|
||||||
|
return w.buf.Bytes()
|
||||||
|
}
|
146
binary/reader.go
Normal file
146
binary/reader.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package binary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reader struct {
|
||||||
|
buf *bytes.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkReader struct {
|
||||||
|
conn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
type TlvMap map[uint16][]byte
|
||||||
|
|
||||||
|
// --- ByteStream reader ---
|
||||||
|
|
||||||
|
func NewReader(data []byte) *Reader {
|
||||||
|
buf := bytes.NewReader(data)
|
||||||
|
return &Reader{
|
||||||
|
buf: buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadByte() byte {
|
||||||
|
b, err := r.buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadBytes(len int) []byte {
|
||||||
|
b := make([]byte, len)
|
||||||
|
_, err := r.buf.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadBytesShort() []byte {
|
||||||
|
return r.ReadBytes(int(r.ReadUInt16()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadUInt16() uint16 {
|
||||||
|
f, _ := r.buf.ReadByte()
|
||||||
|
s, err := r.buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return uint16((int32(f) << 8) + int32(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadInt32() int32 {
|
||||||
|
b := r.ReadBytes(4)
|
||||||
|
return (int32(b[0]) << 24) | (int32(b[1]) << 16) | (int32(b[2]) << 8) | int32(b[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadString() string {
|
||||||
|
data := r.ReadBytes(int(r.ReadInt32() - 4))
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadStringShort() string {
|
||||||
|
data := r.ReadBytes(int(r.ReadUInt16()))
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) ReadStringLimit(limit int) string {
|
||||||
|
data := r.ReadBytes(limit)
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if tagSize == 1 {
|
||||||
|
k = uint16(r.ReadByte())
|
||||||
|
} else if tagSize == 2 {
|
||||||
|
k = r.ReadUInt16()
|
||||||
|
} else if tagSize == 4 {
|
||||||
|
k = uint16(r.ReadInt32())
|
||||||
|
}
|
||||||
|
if k == 255 {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
m[k] = r.ReadBytes(int(r.ReadUInt16()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Len() int {
|
||||||
|
return r.buf.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tlv TlvMap) Exists(key uint16) bool {
|
||||||
|
if _, ok := tlv[key]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Network reader ---
|
||||||
|
|
||||||
|
func NewNetworkReader(conn net.Conn) *NetworkReader {
|
||||||
|
return &NetworkReader{conn: conn}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *NetworkReader) ReadByte() byte {
|
||||||
|
buf := make([]byte, 1)
|
||||||
|
n, err := r.conn.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
return r.ReadByte()
|
||||||
|
}
|
||||||
|
return buf[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *NetworkReader) ReadBytes(len int) []byte {
|
||||||
|
buf := make([]byte, len)
|
||||||
|
for i := 0; i < len; i++ {
|
||||||
|
buf[i] = r.ReadByte()
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *NetworkReader) ReadInt32() int32 {
|
||||||
|
return (int32(r.ReadByte()) << 24) | (int32(r.ReadByte()) << 16) | (int32(r.ReadByte()) << 8) | int32(r.ReadByte())
|
||||||
|
}
|
143
binary/tea.go
Normal file
143
binary/tea.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package binary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
delta = uint32(0x9E3779B9)
|
||||||
|
fillnor = 0xF8
|
||||||
|
)
|
||||||
|
|
||||||
|
type teaCipher struct {
|
||||||
|
keys [4]uint32
|
||||||
|
value []byte
|
||||||
|
byte8 [8]byte
|
||||||
|
ubyte32 [2]uint32
|
||||||
|
xor [8]byte //xor
|
||||||
|
fxor [8]byte //first xor
|
||||||
|
lxor [8]byte //last xor
|
||||||
|
nxor [8]byte //new xor Decrypt add
|
||||||
|
balebuff *bytes.Buffer
|
||||||
|
seedrand *rand.Rand
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTeaCipher(key []byte) *teaCipher {
|
||||||
|
if len(key) != 16 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cipher := &teaCipher{
|
||||||
|
balebuff: bytes.NewBuffer(nil),
|
||||||
|
}
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
cipher.keys[i] = binary.BigEndian.Uint32(key[i*4:])
|
||||||
|
}
|
||||||
|
cipher.seedrand = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
return cipher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *teaCipher) Encrypt(value []byte) []byte {
|
||||||
|
c.balebuff.Reset()
|
||||||
|
vl := len(value)
|
||||||
|
filln := (8 - (vl + 2)) % 8
|
||||||
|
if filln < 0 {
|
||||||
|
filln += 2 + 8
|
||||||
|
} else {
|
||||||
|
filln += 2
|
||||||
|
}
|
||||||
|
bindex := filln + 1
|
||||||
|
if bindex <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rands := make([]byte, bindex)
|
||||||
|
for i := 1; i < bindex; i++ {
|
||||||
|
rands[i] = byte((c.seedrand.Intn(236) + 1))
|
||||||
|
}
|
||||||
|
rands[0] = byte((filln - 2) | fillnor)
|
||||||
|
c.balebuff.Write(rands)
|
||||||
|
c.balebuff.Write(value)
|
||||||
|
c.balebuff.Write([]byte{00, 00, 00, 00, 00, 00, 00})
|
||||||
|
vl = c.balebuff.Len()
|
||||||
|
c.value = c.balebuff.Bytes()
|
||||||
|
c.balebuff.Reset()
|
||||||
|
for i := 0; i < vl; i += 8 {
|
||||||
|
c.xor = xor(c.value[i:i+8], c.fxor[0:8])
|
||||||
|
c.ubyte32[0] = binary.BigEndian.Uint32(c.xor[0:4])
|
||||||
|
c.ubyte32[1] = binary.BigEndian.Uint32(c.xor[4:8])
|
||||||
|
c.encipher()
|
||||||
|
c.fxor = xor(c.byte8[0:8], c.lxor[0:8])
|
||||||
|
c.balebuff.Write(c.fxor[0:8])
|
||||||
|
c.lxor = c.xor
|
||||||
|
|
||||||
|
}
|
||||||
|
return c.balebuff.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *teaCipher) Decrypt(value []byte) []byte {
|
||||||
|
vl := len(value)
|
||||||
|
if vl <= 0 || (vl%8) != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.balebuff.Reset()
|
||||||
|
c.ubyte32[0] = binary.BigEndian.Uint32(value[0:4])
|
||||||
|
c.ubyte32[1] = binary.BigEndian.Uint32(value[4:8])
|
||||||
|
c.decipher()
|
||||||
|
copy(c.lxor[0:8], value[0:8])
|
||||||
|
c.fxor = c.byte8
|
||||||
|
pos := int((c.byte8[0] & 0x7) + 2)
|
||||||
|
c.balebuff.Write(c.byte8[0:8])
|
||||||
|
for i := 8; i < vl; i += 8 {
|
||||||
|
c.xor = xor(value[i:i+8], c.fxor[0:8])
|
||||||
|
c.ubyte32[0] = binary.BigEndian.Uint32(c.xor[0:4])
|
||||||
|
c.ubyte32[1] = binary.BigEndian.Uint32(c.xor[4:8])
|
||||||
|
c.decipher()
|
||||||
|
c.nxor = xor(c.byte8[0:8], c.lxor[0:8])
|
||||||
|
c.balebuff.Write(c.nxor[0:8])
|
||||||
|
c.fxor = xor(c.nxor[0:8], c.lxor[0:8])
|
||||||
|
copy(c.lxor[0:8], value[i:i+8])
|
||||||
|
}
|
||||||
|
pos++
|
||||||
|
c.value = c.balebuff.Bytes()
|
||||||
|
nl := c.balebuff.Len()
|
||||||
|
if pos >= c.balebuff.Len() || (nl-7) <= pos {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.value[pos : nl-7]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *teaCipher) encipher() {
|
||||||
|
sum := delta
|
||||||
|
for i := 0x10; i > 0; i-- {
|
||||||
|
c.ubyte32[0] += ((c.ubyte32[1] << 4 & 0xFFFFFFF0) + c.keys[0]) ^ (c.ubyte32[1] + sum) ^ ((c.ubyte32[1] >> 5 & 0x07ffffff) + c.keys[1])
|
||||||
|
c.ubyte32[1] += ((c.ubyte32[0] << 4 & 0xFFFFFFF0) + c.keys[2]) ^ (c.ubyte32[0] + sum) ^ ((c.ubyte32[0] >> 5 & 0x07ffffff) + c.keys[3])
|
||||||
|
sum += delta
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint32(c.byte8[0:4], c.ubyte32[0])
|
||||||
|
binary.BigEndian.PutUint32(c.byte8[4:8], c.ubyte32[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *teaCipher) decipher() {
|
||||||
|
sum := delta
|
||||||
|
sum = (sum << 4) & 0xffffffff
|
||||||
|
|
||||||
|
for i := 0x10; i > 0; i-- {
|
||||||
|
c.ubyte32[1] -= (((c.ubyte32[0] << 4 & 0xFFFFFFF0) + c.keys[2]) ^ (c.ubyte32[0] + sum) ^ ((c.ubyte32[0] >> 5 & 0x07ffffff) + c.keys[3]))
|
||||||
|
c.ubyte32[1] &= 0xffffffff
|
||||||
|
c.ubyte32[0] -= (((c.ubyte32[1] << 4 & 0xFFFFFFF0) + c.keys[0]) ^ (c.ubyte32[1] + sum) ^ ((c.ubyte32[1] >> 5 & 0x07ffffff) + c.keys[1]))
|
||||||
|
c.ubyte32[0] &= 0xffffffff
|
||||||
|
sum -= delta
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint32(c.byte8[0:4], c.ubyte32[0])
|
||||||
|
binary.BigEndian.PutUint32(c.byte8[4:8], c.ubyte32[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func xor(a, b []byte) (bts [8]byte) {
|
||||||
|
l := len(a)
|
||||||
|
for i := 0; i < l; i += 4 {
|
||||||
|
binary.BigEndian.PutUint32(bts[i:i+4], binary.BigEndian.Uint32(a[i:i+4])^binary.BigEndian.Uint32(b[i:i+4]))
|
||||||
|
}
|
||||||
|
return bts
|
||||||
|
}
|
77
binary/utils.go
Normal file
77
binary/utils.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package binary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
|
"crypto/rand"
|
||||||
|
binary2 "encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ZlibUncompress(src []byte) []byte {
|
||||||
|
b := bytes.NewReader(src)
|
||||||
|
var out bytes.Buffer
|
||||||
|
r, _ := zlib.NewReader(b)
|
||||||
|
io.Copy(&out, r)
|
||||||
|
return out.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomString(len int) string {
|
||||||
|
var res string
|
||||||
|
var str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||||
|
b := bytes.NewBufferString(str)
|
||||||
|
length := b.Len()
|
||||||
|
bigInt := big.NewInt(int64(length))
|
||||||
|
for i := 0; i < len; i++ {
|
||||||
|
randomInt, _ := rand.Int(rand.Reader, bigInt)
|
||||||
|
res += string(str[randomInt.Int64()])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalculateImageResourceId(md5 []byte) string {
|
||||||
|
return strings.ToUpper(fmt.Sprintf(
|
||||||
|
"{%s-%s-%s-%s-%s}.png",
|
||||||
|
hex.EncodeToString(md5[0:4]), hex.EncodeToString(md5[4:6]), hex.EncodeToString(md5[6:8]),
|
||||||
|
hex.EncodeToString(md5[8:10]), hex.EncodeToString(md5[10:]),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToIPV4Address(arr []byte) string {
|
||||||
|
if len(arr) != 4 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d", arr[0], arr[1], arr[2], arr[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
func UInt32ToIPV4Address(i uint32) string {
|
||||||
|
addr := make([]byte, 4)
|
||||||
|
binary2.LittleEndian.PutUint32(addr, i)
|
||||||
|
return ToIPV4Address(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
case int16:
|
||||||
|
w.WriteUInt16(uint16(t))
|
||||||
|
case int32:
|
||||||
|
w.WriteUInt32(uint32(t))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
112
binary/writer.go
Normal file
112
binary/writer.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package binary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
buf *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter() *Writer {
|
||||||
|
return &Writer{buf: new(bytes.Buffer)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriterF(f func(writer *Writer)) []byte {
|
||||||
|
w := NewWriter()
|
||||||
|
f(w)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Write(b []byte) {
|
||||||
|
w.buf.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteByte(b byte) {
|
||||||
|
w.buf.WriteByte(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteUInt16(v uint16) {
|
||||||
|
b := make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(b, v)
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteUInt32(v uint32) {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(b, v)
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteUInt64(v uint64) {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(b, v)
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteString(v string) {
|
||||||
|
payload := []byte(v)
|
||||||
|
w.WriteUInt32(uint32(len(payload) + 4))
|
||||||
|
w.Write(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteStringShort(v string) {
|
||||||
|
w.WriteTlv([]byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteBool(b bool) {
|
||||||
|
if b {
|
||||||
|
w.WriteByte(0x01)
|
||||||
|
} else {
|
||||||
|
w.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) EncryptAndWrite(key []byte, data []byte) {
|
||||||
|
tea := NewTeaCipher(key)
|
||||||
|
ed := tea.Encrypt(data)
|
||||||
|
w.Write(ed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteIntLvPacket(offset int, f func(writer *Writer)) {
|
||||||
|
t := NewWriter()
|
||||||
|
f(t)
|
||||||
|
data := t.Bytes()
|
||||||
|
w.WriteUInt32(uint32(len(data) + offset))
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteUniPacket(commandName string, sessionId, extraData, body []byte) {
|
||||||
|
w.WriteIntLvPacket(4, func(w *Writer) {
|
||||||
|
w.WriteString(commandName)
|
||||||
|
w.WriteUInt32(8)
|
||||||
|
w.Write(sessionId)
|
||||||
|
if len(extraData) == 0 {
|
||||||
|
w.WriteUInt32(0x04)
|
||||||
|
} else {
|
||||||
|
w.WriteUInt32(uint32(len(extraData) + 4))
|
||||||
|
w.Write(extraData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
w.WriteIntLvPacket(4, func(w *Writer) {
|
||||||
|
w.Write(body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteTlv(data []byte) {
|
||||||
|
w.WriteUInt16(uint16(len(data)))
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) WriteTlvLimitedSize(data []byte, limit int) {
|
||||||
|
if len(data) <= limit {
|
||||||
|
w.WriteTlv(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteTlv(data[:limit])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Bytes() []byte {
|
||||||
|
return w.buf.Bytes()
|
||||||
|
}
|
418
client/builders.go
Normal file
418
client/builders.go
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary/jce"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
|
"github.com/Mrs4s/MiraiGo/protocol/crypto"
|
||||||
|
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
||||||
|
"github.com/Mrs4s/MiraiGo/protocol/tlv"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *QQClient) buildLoginPacket() (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := packets.BuildOicqRequestPacket(c.Uin, 0x0810, crypto.ECDH, c.RandomKey, func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(9)
|
||||||
|
w.WriteUInt16(17)
|
||||||
|
|
||||||
|
w.Write(tlv.T18(16, uint32(c.Uin)))
|
||||||
|
w.Write(tlv.T1(uint32(c.Uin), SystemDeviceInfo.IpAddress))
|
||||||
|
w.Write(tlv.T106(uint32(c.Uin), 0, c.PasswordMd5, true, SystemDeviceInfo.Guid, SystemDeviceInfo.TgtgtKey))
|
||||||
|
w.Write(tlv.T116(184024956, 0x10400))
|
||||||
|
w.Write(tlv.T100())
|
||||||
|
w.Write(tlv.T107(0))
|
||||||
|
w.Write(tlv.T142("com.tencent.mobileqq"))
|
||||||
|
w.Write(tlv.T144(
|
||||||
|
SystemDeviceInfo.AndroidId,
|
||||||
|
SystemDeviceInfo.GenDeviceInfoData(),
|
||||||
|
SystemDeviceInfo.OSType,
|
||||||
|
SystemDeviceInfo.Version.Release,
|
||||||
|
SystemDeviceInfo.SimInfo,
|
||||||
|
SystemDeviceInfo.APN,
|
||||||
|
false, true, false, tlv.GuidFlag(),
|
||||||
|
SystemDeviceInfo.Model,
|
||||||
|
SystemDeviceInfo.Guid,
|
||||||
|
SystemDeviceInfo.Brand,
|
||||||
|
SystemDeviceInfo.TgtgtKey,
|
||||||
|
))
|
||||||
|
|
||||||
|
w.Write(tlv.T145(SystemDeviceInfo.Guid))
|
||||||
|
w.Write(tlv.T147(16, []byte("8.2.7"), []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D}))
|
||||||
|
/*
|
||||||
|
if (miscBitMap & 0x80) != 0{
|
||||||
|
w.Write(tlv.T166(1))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
w.Write(tlv.T154(seq))
|
||||||
|
w.Write(tlv.T141(SystemDeviceInfo.SimInfo, SystemDeviceInfo.APN))
|
||||||
|
w.Write(tlv.T8(2052))
|
||||||
|
w.Write(tlv.T511([]string{
|
||||||
|
"tenpay.com", "openmobile.qq.com", "docs.qq.com", "connect.qq.com",
|
||||||
|
"qzone.qq.com", "vip.qq.com", "qun.qq.com", "game.qq.com", "qqweb.qq.com",
|
||||||
|
"office.qq.com", "ti.qq.com", "mail.qq.com", "qzone.com", "mma.qq.com",
|
||||||
|
}))
|
||||||
|
|
||||||
|
w.Write(tlv.T187(SystemDeviceInfo.MacAddress))
|
||||||
|
w.Write(tlv.T188(SystemDeviceInfo.AndroidId))
|
||||||
|
if len(SystemDeviceInfo.IMSIMd5) != 0 {
|
||||||
|
w.Write(tlv.T194(SystemDeviceInfo.IMSIMd5))
|
||||||
|
}
|
||||||
|
w.Write(tlv.T191(0x82))
|
||||||
|
if len(SystemDeviceInfo.WifiBSSID) != 0 && len(SystemDeviceInfo.WifiSSID) != 0 {
|
||||||
|
w.Write(tlv.T202(SystemDeviceInfo.WifiBSSID, SystemDeviceInfo.WifiSSID))
|
||||||
|
}
|
||||||
|
w.Write(tlv.T177())
|
||||||
|
w.Write(tlv.T516())
|
||||||
|
w.Write(tlv.T521())
|
||||||
|
w.Write(tlv.T525(tlv.T536([]byte{0x01, 0x00})))
|
||||||
|
})
|
||||||
|
sso := packets.BuildSsoPacket(seq, "wtlogin.login", SystemDeviceInfo.IMEI, []byte{}, c.OutGoingPacketSessionId, req, c.ksid)
|
||||||
|
packet := packets.BuildLoginPacket(c.Uin, 2, make([]byte, 16), sso, []byte{})
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildCaptchaPacket(result string, sign []byte) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := packets.BuildOicqRequestPacket(c.Uin, 0x810, crypto.ECDH, c.RandomKey, func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(2) // sub command
|
||||||
|
w.WriteUInt16(4)
|
||||||
|
w.Write(tlv.T2(result, sign))
|
||||||
|
w.Write(tlv.T8(2052))
|
||||||
|
w.Write(tlv.T104(c.t104))
|
||||||
|
w.Write(tlv.T116(150470524, 66560))
|
||||||
|
})
|
||||||
|
sso := packets.BuildSsoPacket(seq, "wtlogin.login", SystemDeviceInfo.IMEI, []byte{}, c.OutGoingPacketSessionId, req, c.ksid)
|
||||||
|
packet := packets.BuildLoginPacket(c.Uin, 2, make([]byte, 16), sso, []byte{})
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildClientRegisterPacket() (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
svc := &jce.SvcReqRegister{
|
||||||
|
ConnType: 0,
|
||||||
|
Uin: c.Uin,
|
||||||
|
Bid: 1 | 2 | 4,
|
||||||
|
Status: 11,
|
||||||
|
KickPC: 0,
|
||||||
|
KickWeak: 0,
|
||||||
|
IOSVersion: int64(SystemDeviceInfo.Version.Sdk),
|
||||||
|
NetType: 1,
|
||||||
|
RegType: 0,
|
||||||
|
Guid: SystemDeviceInfo.Guid,
|
||||||
|
IsSetStatus: 0,
|
||||||
|
LocaleId: 2052,
|
||||||
|
DevName: string(SystemDeviceInfo.Model),
|
||||||
|
DevType: string(SystemDeviceInfo.Model),
|
||||||
|
OSVer: string(SystemDeviceInfo.Version.Release),
|
||||||
|
OpenPush: 1,
|
||||||
|
LargeSeq: 1551,
|
||||||
|
OldSSOIp: 0,
|
||||||
|
NewSSOIp: 31806887127679168,
|
||||||
|
ChannelNo: "",
|
||||||
|
CPID: 0,
|
||||||
|
VendorName: "MIUI",
|
||||||
|
VendorOSName: "ONEPLUS A5000_23_17",
|
||||||
|
B769: []byte{0x0A, 0x04, 0x08, 0x2E, 0x10, 0x00, 0x0A, 0x05, 0x08, 0x9B, 0x02, 0x10, 0x00},
|
||||||
|
SetMute: 0,
|
||||||
|
}
|
||||||
|
b := append([]byte{0x0A}, svc.ToBytes()...)
|
||||||
|
b = append(b, 0x0B)
|
||||||
|
buf := &jce.RequestDataVersion3{
|
||||||
|
Map: map[string][]byte{"SvcReqRegister": b},
|
||||||
|
}
|
||||||
|
pkt := &jce.RequestPacket{
|
||||||
|
IVersion: 3,
|
||||||
|
SServantName: "PushService",
|
||||||
|
SFuncName: "SvcReqRegister",
|
||||||
|
SBuffer: buf.ToBytes(),
|
||||||
|
Context: make(map[string]string),
|
||||||
|
Status: make(map[string]string),
|
||||||
|
}
|
||||||
|
sso := packets.BuildSsoPacket(seq, "StatSvc.register", SystemDeviceInfo.IMEI, c.sigInfo.tgt, c.OutGoingPacketSessionId, pkt.ToBytes(), c.ksid)
|
||||||
|
packet := packets.BuildLoginPacket(c.Uin, 1, c.sigInfo.d2Key, sso, c.sigInfo.d2)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildPushResponsePacket(t int32, pktSeq int64, jceBuf []byte) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := jce.NewJceWriter()
|
||||||
|
req.WriteInt32(t, 1)
|
||||||
|
req.WriteInt64(pktSeq, 2)
|
||||||
|
req.WriteBytes(jceBuf, 3)
|
||||||
|
b := append([]byte{0x0A}, req.Bytes()...)
|
||||||
|
b = append(b, 0x0B)
|
||||||
|
buf := &jce.RequestDataVersion3{
|
||||||
|
Map: map[string][]byte{"PushResp": b},
|
||||||
|
}
|
||||||
|
pkt := &jce.RequestPacket{
|
||||||
|
IVersion: 3,
|
||||||
|
SServantName: "QQService.ConfigPushSvc.MainServant",
|
||||||
|
SFuncName: "PushResp",
|
||||||
|
SBuffer: buf.ToBytes(),
|
||||||
|
Context: make(map[string]string),
|
||||||
|
Status: make(map[string]string),
|
||||||
|
}
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "ConfigPushSvc.PushResp", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes())
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildFriendGroupListRequestPacket(friendStartIndex, friendListCount, groupStartIndex, groupListCount int16) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
d50, _ := proto.Marshal(&pb.D50ReqBody{
|
||||||
|
Appid: 1002,
|
||||||
|
ReqMusicSwitch: 1,
|
||||||
|
ReqMutualmarkAlienation: 1,
|
||||||
|
ReqKsingSwitch: 1,
|
||||||
|
ReqMutualmarkLbsshare: 1,
|
||||||
|
})
|
||||||
|
req := &jce.FriendListRequest{
|
||||||
|
Reqtype: 3,
|
||||||
|
IfReflush: func() byte { // fuck golang
|
||||||
|
if friendStartIndex <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}(),
|
||||||
|
Uin: int64(c.Uin),
|
||||||
|
StartIndex: friendStartIndex,
|
||||||
|
FriendCount: friendListCount,
|
||||||
|
GroupId: 0,
|
||||||
|
IfGetGroupInfo: func() byte {
|
||||||
|
if groupListCount <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}(),
|
||||||
|
GroupStartIndex: byte(groupStartIndex),
|
||||||
|
GroupCount: byte(groupListCount),
|
||||||
|
IfGetMSFGroup: 0,
|
||||||
|
IfShowTermType: 1,
|
||||||
|
Version: 27,
|
||||||
|
UinList: nil,
|
||||||
|
AppType: 0,
|
||||||
|
IfGetDOVId: 0,
|
||||||
|
IfGetBothFlag: 0,
|
||||||
|
D50: d50,
|
||||||
|
D6B: []byte{},
|
||||||
|
SnsTypeList: []int64{13580, 13581, 13582},
|
||||||
|
}
|
||||||
|
b := append([]byte{0x0A}, req.ToBytes()...)
|
||||||
|
b = append(b, 0x0B)
|
||||||
|
buf := &jce.RequestDataVersion3{
|
||||||
|
Map: map[string][]byte{"FL": b},
|
||||||
|
}
|
||||||
|
pkt := &jce.RequestPacket{
|
||||||
|
IVersion: 3,
|
||||||
|
CPacketType: 0x003,
|
||||||
|
IRequestId: 1921334514,
|
||||||
|
SServantName: "mqq.IMService.FriendListServiceServantObj",
|
||||||
|
SFuncName: "GetFriendListReq",
|
||||||
|
SBuffer: buf.ToBytes(),
|
||||||
|
Context: make(map[string]string),
|
||||||
|
Status: make(map[string]string),
|
||||||
|
}
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "friendlist.getFriendGroupList", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes())
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildGroupListRequestPacket() (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &jce.TroopListRequest{
|
||||||
|
Uin: c.Uin,
|
||||||
|
GetMSFMsgFlag: 1,
|
||||||
|
Cookies: []byte{},
|
||||||
|
GroupInfo: []int64{},
|
||||||
|
GroupFlagExt: 1,
|
||||||
|
Version: 7,
|
||||||
|
CompanyId: 0,
|
||||||
|
VersionNum: 1,
|
||||||
|
GetLongGroupName: 1,
|
||||||
|
}
|
||||||
|
b := append([]byte{0x0A}, req.ToBytes()...)
|
||||||
|
b = append(b, 0x0B)
|
||||||
|
buf := &jce.RequestDataVersion3{
|
||||||
|
Map: map[string][]byte{"GetTroopListReqV2Simplify": b},
|
||||||
|
}
|
||||||
|
pkt := &jce.RequestPacket{
|
||||||
|
IVersion: 3,
|
||||||
|
CPacketType: 0x00,
|
||||||
|
IRequestId: c.nextPacketSeq(),
|
||||||
|
SServantName: "mqq.IMService.FriendListServiceServantObj",
|
||||||
|
SFuncName: "GetTroopListReqV2Simplify",
|
||||||
|
SBuffer: buf.ToBytes(),
|
||||||
|
Context: make(map[string]string),
|
||||||
|
Status: make(map[string]string),
|
||||||
|
}
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "friendlist.GetTroopListReqV2", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes())
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildGroupMemberListRequestPacket(groupUin, groupCode, nextUin int64) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &jce.TroopMemberListRequest{
|
||||||
|
Uin: c.Uin,
|
||||||
|
GroupCode: groupCode,
|
||||||
|
NextUin: nextUin,
|
||||||
|
GroupUin: groupUin,
|
||||||
|
Version: 2,
|
||||||
|
}
|
||||||
|
b := append([]byte{0x0A}, req.ToBytes()...)
|
||||||
|
b = append(b, 0x0B)
|
||||||
|
buf := &jce.RequestDataVersion3{
|
||||||
|
Map: map[string][]byte{"GTML": b},
|
||||||
|
}
|
||||||
|
pkt := &jce.RequestPacket{
|
||||||
|
IVersion: 3,
|
||||||
|
IRequestId: c.nextPacketSeq(),
|
||||||
|
SServantName: "mqq.IMService.FriendListServiceServantObj",
|
||||||
|
SFuncName: "GetTroopMemberListReq",
|
||||||
|
SBuffer: buf.ToBytes(),
|
||||||
|
Context: make(map[string]string),
|
||||||
|
Status: make(map[string]string),
|
||||||
|
}
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "friendlist.GetTroopMemberListReq", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, pkt.ToBytes())
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildGetMessageRequestPacket(flag msg.SyncFlag, msgTime int64) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
cook := c.syncCookie
|
||||||
|
if cook == nil {
|
||||||
|
cook, _ = proto.Marshal(&msg.SyncCookie{
|
||||||
|
Time: msgTime,
|
||||||
|
Ran1: 758330138,
|
||||||
|
Ran2: 2480149246,
|
||||||
|
Const1: 1167238020,
|
||||||
|
Const2: 3913056418,
|
||||||
|
Const3: 0x1D,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
req := &msg.GetMessageRequest{
|
||||||
|
SyncFlag: flag,
|
||||||
|
SyncCookie: cook,
|
||||||
|
LatestRambleNumber: 20,
|
||||||
|
OtherRambleNumber: 3,
|
||||||
|
OnlineSyncFlag: 1,
|
||||||
|
ContextFlag: 1,
|
||||||
|
MsgReqType: 1,
|
||||||
|
PubaccountCookie: []byte{},
|
||||||
|
MsgCtrlBuf: []byte{},
|
||||||
|
ServerBuf: []byte{},
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbGetMsg", 1, c.OutGoingPacketSessionId, []byte{}, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildStopGetMessagePacket(msgTime int64) []byte {
|
||||||
|
_, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_STOP, msgTime)
|
||||||
|
return pkt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildDeleteMessageRequestPacket(msg []*pb.MessageItem) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &pb.DeleteMessageRequest{Items: msg}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbDeleteMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildGroupSendingPacket(groupCode int64, m *message.SendingMessage) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
req := &msg.SendMessageRequest{
|
||||||
|
RoutingHead: &msg.RoutingHead{Grp: &msg.Grp{GroupCode: groupCode}},
|
||||||
|
ContentHead: &msg.ContentHead{PkgNum: 1},
|
||||||
|
MsgBody: &msg.MessageBody{
|
||||||
|
RichText: &msg.RichText{
|
||||||
|
Elems: message.ToProtoElems(m.Elements),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MsgSeq: c.nextMessageSeq(),
|
||||||
|
MsgRand: int32(rand.Uint32()),
|
||||||
|
SyncCookie: EmptyBytes,
|
||||||
|
MsgVia: 1,
|
||||||
|
MsgCtrl: nil,
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "MessageSvc.PbSendMsg", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildGroupImageStorePacket(groupCode int64, md5 [16]byte, size int32) (uint16, []byte) {
|
||||||
|
seq := c.nextSeq()
|
||||||
|
name := binary.RandomString(16) + ".gif"
|
||||||
|
req := &pb.D388ReqBody{
|
||||||
|
NetType: 3,
|
||||||
|
Subcmd: 1,
|
||||||
|
MsgTryupImgReq: []*pb.TryUpImgReq{
|
||||||
|
{
|
||||||
|
GroupCode: groupCode,
|
||||||
|
SrcUin: c.Uin,
|
||||||
|
FileMd5: md5[:],
|
||||||
|
FileSize: int64(size),
|
||||||
|
FileName: name,
|
||||||
|
SrcTerm: 5,
|
||||||
|
PlatformType: 9,
|
||||||
|
BuType: 1,
|
||||||
|
PicType: 1000,
|
||||||
|
BuildVer: "8.2.7.4410",
|
||||||
|
AppPicType: 1006,
|
||||||
|
FileIndex: EmptyBytes,
|
||||||
|
TransferUrl: EmptyBytes,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Extension: EmptyBytes,
|
||||||
|
}
|
||||||
|
payload, _ := proto.Marshal(req)
|
||||||
|
packet := packets.BuildUniPacket(c.Uin, seq, "ImgStore.GroupPicUp", 1, c.OutGoingPacketSessionId, EmptyBytes, c.sigInfo.d2Key, payload)
|
||||||
|
return seq, packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) buildImageUploadPacket(data, updKey []byte, commandId int32, fmd5 [16]byte) (r [][]byte) {
|
||||||
|
offset := 0
|
||||||
|
binary.ToChunkedBytesF(data, 8192*8, func(chunked []byte) {
|
||||||
|
w := binary.NewWriter()
|
||||||
|
cmd5 := md5.Sum(chunked)
|
||||||
|
head, _ := proto.Marshal(&pb.ReqDataHighwayHead{
|
||||||
|
MsgBasehead: &pb.DataHighwayHead{
|
||||||
|
Version: 1,
|
||||||
|
Uin: strconv.FormatInt(c.Uin, 10),
|
||||||
|
Command: "PicUp.DataUp",
|
||||||
|
Seq: func() int32 {
|
||||||
|
if commandId == 2 {
|
||||||
|
return c.nextGroupDataTransSeq()
|
||||||
|
}
|
||||||
|
return c.nextGroupDataTransSeq()
|
||||||
|
}(),
|
||||||
|
Appid: 537062409,
|
||||||
|
Dataflag: 4096,
|
||||||
|
CommandId: commandId,
|
||||||
|
LocaleId: 2052,
|
||||||
|
},
|
||||||
|
MsgSeghead: &pb.SegHead{
|
||||||
|
Filesize: int64(len(data)),
|
||||||
|
Dataoffset: int64(offset),
|
||||||
|
Datalength: int32(len(chunked)),
|
||||||
|
Serviceticket: updKey,
|
||||||
|
Md5: cmd5[:],
|
||||||
|
FileMd5: fmd5[:],
|
||||||
|
},
|
||||||
|
ReqExtendinfo: EmptyBytes,
|
||||||
|
})
|
||||||
|
offset += len(chunked)
|
||||||
|
w.WriteByte(40)
|
||||||
|
w.WriteUInt32(uint32(len(head)))
|
||||||
|
w.WriteUInt32(uint32(len(chunked)))
|
||||||
|
w.Write(head)
|
||||||
|
w.Write(chunked)
|
||||||
|
w.WriteByte(41)
|
||||||
|
r = append(r, w.Bytes())
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
409
client/client.go
Normal file
409
client/client.go
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||||
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
|
"github.com/Mrs4s/MiraiGo/protocol/packets"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QQClient struct {
|
||||||
|
Uin int64
|
||||||
|
PasswordMd5 [16]byte
|
||||||
|
|
||||||
|
Nickname string
|
||||||
|
Age uint16
|
||||||
|
Gender uint16
|
||||||
|
FriendList []*FriendInfo
|
||||||
|
GroupList []*GroupInfo
|
||||||
|
|
||||||
|
SequenceId uint16
|
||||||
|
OutGoingPacketSessionId []byte
|
||||||
|
RandomKey []byte
|
||||||
|
Conn net.Conn
|
||||||
|
|
||||||
|
decoders map[string]func(*QQClient, []byte) (interface{}, error)
|
||||||
|
handlers map[uint16]func(interface{}, error)
|
||||||
|
|
||||||
|
syncCookie []byte
|
||||||
|
pubAccountCookie []byte
|
||||||
|
msgCtrlBuf []byte
|
||||||
|
ksid []byte
|
||||||
|
t104 []byte
|
||||||
|
t150 []byte
|
||||||
|
t149 []byte
|
||||||
|
t528 []byte
|
||||||
|
t530 []byte
|
||||||
|
rollbackSig []byte
|
||||||
|
timeDiff int64
|
||||||
|
sigInfo *loginSigInfo
|
||||||
|
pwdFlag bool
|
||||||
|
running bool
|
||||||
|
|
||||||
|
lastMessageSeq int32
|
||||||
|
requestPacketRequestId int32
|
||||||
|
messageSeq int32
|
||||||
|
groupDataTransSeq int32
|
||||||
|
|
||||||
|
privateMessageHandlers []func(*QQClient, *message.PrivateMessage)
|
||||||
|
groupMessageHandlers []func(*QQClient, *message.GroupMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
type loginSigInfo struct {
|
||||||
|
loginBitmap uint64
|
||||||
|
tgt []byte
|
||||||
|
tgtKey []byte
|
||||||
|
|
||||||
|
userStKey []byte
|
||||||
|
userStWebSig []byte
|
||||||
|
sKey []byte
|
||||||
|
d2 []byte
|
||||||
|
d2Key []byte
|
||||||
|
wtSessionTicketKey []byte
|
||||||
|
deviceToken []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient create new qq client
|
||||||
|
func NewClient(uin int64, password string) *QQClient {
|
||||||
|
cli := &QQClient{
|
||||||
|
Uin: uin,
|
||||||
|
PasswordMd5: md5.Sum([]byte(password)),
|
||||||
|
SequenceId: 0x3635,
|
||||||
|
RandomKey: make([]byte, 16),
|
||||||
|
OutGoingPacketSessionId: []byte{0x02, 0xB0, 0x5B, 0x8B},
|
||||||
|
decoders: map[string]func(*QQClient, []byte) (interface{}, error){
|
||||||
|
"wtlogin.login": decodeLoginResponse,
|
||||||
|
"StatSvc.register": decodeClientRegisterResponse,
|
||||||
|
"MessageSvc.PushNotify": decodeSvcNotify,
|
||||||
|
"OnlinePush.PbPushGroupMsg": decodeGroupMessagePacket,
|
||||||
|
"ConfigPushSvc.PushReq": decodePushReqPacket,
|
||||||
|
"MessageSvc.PbGetMsg": decodeMessageSvcPacket,
|
||||||
|
"friendlist.getFriendGroupList": decodeFriendGroupListResponse,
|
||||||
|
"friendlist.GetTroopListReqV2": decodeGroupListResponse,
|
||||||
|
"friendlist.GetTroopMemberListReq": decodeGroupMemberListResponse,
|
||||||
|
"ImgStore.GroupPicUp": decodeGroupImageStoreResponse,
|
||||||
|
},
|
||||||
|
handlers: map[uint16]func(interface{}, error){},
|
||||||
|
sigInfo: &loginSigInfo{},
|
||||||
|
requestPacketRequestId: 1921334513,
|
||||||
|
messageSeq: 22911,
|
||||||
|
ksid: []byte("|454001228437590|A8.2.7.27f6ea96"),
|
||||||
|
}
|
||||||
|
rand.Read(cli.RandomKey)
|
||||||
|
return cli
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login send login request
|
||||||
|
func (c *QQClient) Login() (*LoginResponse, error) {
|
||||||
|
if c.running {
|
||||||
|
return nil, ErrAlreadyRunning
|
||||||
|
}
|
||||||
|
conn, err := net.Dial("tcp", "125.94.60.146:80") //TODO: more servers
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.Conn = conn
|
||||||
|
c.running = true
|
||||||
|
go c.loop()
|
||||||
|
seq, packet := c.buildLoginPacket()
|
||||||
|
rsp, err := c.sendAndWait(seq, packet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l := rsp.(LoginResponse)
|
||||||
|
if l.Success {
|
||||||
|
c.registerClient()
|
||||||
|
go c.heartbeat()
|
||||||
|
}
|
||||||
|
return &l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l := rsp.(LoginResponse)
|
||||||
|
if l.Success {
|
||||||
|
c.registerClient()
|
||||||
|
go c.heartbeat()
|
||||||
|
}
|
||||||
|
return &l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadFriendList refresh QQClient.FriendList field via GetFriendList()
|
||||||
|
func (c *QQClient) ReloadFriendList() error {
|
||||||
|
rsp, err := c.GetFriendList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.FriendList = rsp.List
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFriendList request friend list
|
||||||
|
func (c *QQClient) GetFriendList() (*FriendListResponse, error) {
|
||||||
|
var curFriendCount = 0
|
||||||
|
r := &FriendListResponse{}
|
||||||
|
for {
|
||||||
|
rsp, err := c.sendAndWait(c.buildFriendGroupListRequestPacket(int16(curFriendCount), 150, 0, 0))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list := rsp.(FriendListResponse)
|
||||||
|
r.TotalCount = list.TotalCount
|
||||||
|
r.List = append(r.List, list.List...)
|
||||||
|
curFriendCount += len(list.List)
|
||||||
|
if int32(curFriendCount) >= r.TotalCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) SendGroupMessage(groupCode int64, m *message.SendingMessage) {
|
||||||
|
_, pkt := c.buildGroupSendingPacket(groupCode, m)
|
||||||
|
c.send(pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) UploadGroupImage(groupCode int64, img []byte) (*message.GroupImageElement, error) {
|
||||||
|
h := md5.Sum(img)
|
||||||
|
seq, pkt := c.buildGroupImageStorePacket(groupCode, h, int32(len(img)))
|
||||||
|
r, err := c.sendAndWait(seq, pkt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rsp := r.(groupImageUploadResponse)
|
||||||
|
if rsp.ResultCode != 0 {
|
||||||
|
return nil, errors.New(rsp.Message)
|
||||||
|
}
|
||||||
|
if rsp.IsExists {
|
||||||
|
return message.NewGroupImage(binary.CalculateImageResourceId(h[:]), h[:]), nil
|
||||||
|
}
|
||||||
|
for i, ip := range rsp.UploadIp {
|
||||||
|
updServer := binary.UInt32ToIPV4Address(uint32(ip))
|
||||||
|
conn, err := net.DialTimeout("tcp", updServer+":"+strconv.FormatInt(int64(rsp.UploadPort[i]), 10), time.Second*5)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if conn.SetDeadline(time.Now().Add(time.Second*10)) != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pkt := c.buildImageUploadPacket(img, rsp.UploadKey, 2, h)
|
||||||
|
for _, p := range pkt {
|
||||||
|
_, err = conn.Write(p)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r := binary.NewNetworkReader(conn)
|
||||||
|
r.ReadByte()
|
||||||
|
hl := r.ReadInt32()
|
||||||
|
r.ReadBytes(4)
|
||||||
|
payload := r.ReadBytes(int(hl))
|
||||||
|
_ = conn.Close()
|
||||||
|
rsp := pb.RspDataHighwayHead{}
|
||||||
|
if proto.Unmarshal(payload, &rsp) != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rsp.ErrorCode != 0 {
|
||||||
|
return nil, errors.New("upload failed")
|
||||||
|
}
|
||||||
|
return message.NewGroupImage(binary.CalculateImageResourceId(h[:]), h[:]), nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("upload failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) ReloadGroupList() error {
|
||||||
|
list, err := c.GetGroupList()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.GroupList = list
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) GetGroupList() ([]*GroupInfo, error) {
|
||||||
|
rsp, err := c.sendAndWait(c.buildGroupListRequestPacket())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := rsp.([]*GroupInfo)
|
||||||
|
for _, group := range r {
|
||||||
|
m, err := c.GetGroupMembers(group)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
group.Members = m
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) GetGroupMembers(group *GroupInfo) ([]GroupMemberInfo, error) {
|
||||||
|
var nextUin int64
|
||||||
|
var list []GroupMemberInfo
|
||||||
|
for {
|
||||||
|
data, err := c.sendAndWait(c.buildGroupMemberListRequestPacket(group.Uin, group.Code, nextUin))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rsp := data.(groupMemberListResponse)
|
||||||
|
nextUin = rsp.NextUin
|
||||||
|
list = append(list, rsp.list...)
|
||||||
|
if nextUin == 0 {
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) FindFriend(uin int64) *FriendInfo {
|
||||||
|
for _, t := range c.FriendList {
|
||||||
|
f := t
|
||||||
|
if f.Uin == uin {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) FindGroup(uin int64) *GroupInfo {
|
||||||
|
for _, g := range c.GroupList {
|
||||||
|
f := g
|
||||||
|
if f.Uin == uin {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GroupInfo) FindMember(uin int64) *GroupMemberInfo {
|
||||||
|
for _, m := range g.Members {
|
||||||
|
f := m
|
||||||
|
if f.Uin == uin {
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) registerClient() {
|
||||||
|
seq, packet := c.buildClientRegisterPacket()
|
||||||
|
_, _ = c.sendAndWait(seq, packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) nextSeq() uint16 {
|
||||||
|
c.SequenceId++
|
||||||
|
c.SequenceId &= 0x7FFF
|
||||||
|
if c.SequenceId == 0 {
|
||||||
|
c.SequenceId++
|
||||||
|
}
|
||||||
|
return c.SequenceId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) nextPacketSeq() int32 {
|
||||||
|
s := atomic.LoadInt32(&c.requestPacketRequestId)
|
||||||
|
atomic.AddInt32(&c.requestPacketRequestId, 2)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) nextMessageSeq() int32 {
|
||||||
|
s := atomic.LoadInt32(&c.messageSeq)
|
||||||
|
atomic.AddInt32(&c.messageSeq, 2)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) nextGroupDataTransSeq() int32 {
|
||||||
|
s := atomic.LoadInt32(&c.groupDataTransSeq)
|
||||||
|
atomic.AddInt32(&c.groupDataTransSeq, 2)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) send(pkt []byte) error {
|
||||||
|
_, err := c.Conn.Write(pkt)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) sendAndWait(seq uint16, pkt []byte) (interface{}, error) {
|
||||||
|
type T struct {
|
||||||
|
Response interface{}
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
_, err := c.Conn.Write(pkt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ch := make(chan T)
|
||||||
|
c.handlers[seq] = func(i interface{}, err error) {
|
||||||
|
ch <- T{
|
||||||
|
Response: i,
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rsp := <-ch
|
||||||
|
return rsp.Response, rsp.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) loop() {
|
||||||
|
reader := binary.NewNetworkReader(c.Conn)
|
||||||
|
for c.running {
|
||||||
|
data := reader.ReadBytes(int(reader.ReadInt32()) - 4)
|
||||||
|
pkt, err := packets.ParseIncomingPacket(data, c.sigInfo.d2Key)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("parse incoming packet error: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
payload := pkt.Payload
|
||||||
|
if pkt.Flag2 == 2 {
|
||||||
|
payload, err = pkt.DecryptPayload(c.RandomKey)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println(pkt.CommandName)
|
||||||
|
go func() {
|
||||||
|
decoder, ok := c.decoders[pkt.CommandName]
|
||||||
|
if !ok {
|
||||||
|
if f, ok := c.handlers[pkt.SequenceId]; ok {
|
||||||
|
delete(c.handlers, pkt.SequenceId)
|
||||||
|
f(nil, nil)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rsp, err := decoder(c, payload)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("decode", pkt.CommandName, "error:", err)
|
||||||
|
}
|
||||||
|
if f, ok := c.handlers[pkt.SequenceId]; ok {
|
||||||
|
delete(c.handlers, pkt.SequenceId)
|
||||||
|
f(rsp, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) heartbeat() {
|
||||||
|
for c.running {
|
||||||
|
time.Sleep(time.Second * 30)
|
||||||
|
seq := c.nextSeq()
|
||||||
|
sso := packets.BuildSsoPacket(seq, "Heartbeat.Alive", SystemDeviceInfo.IMEI, []byte{}, c.OutGoingPacketSessionId, []byte{}, c.ksid)
|
||||||
|
packet := packets.BuildLoginPacket(c.Uin, 0, []byte{}, sso, []byte{})
|
||||||
|
_, _ = c.sendAndWait(seq, packet)
|
||||||
|
}
|
||||||
|
}
|
282
client/decoders.go
Normal file
282
client/decoders.go
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary/jce"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func decodeLoginResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
reader := binary.NewReader(payload)
|
||||||
|
reader.ReadUInt16() // sub command
|
||||||
|
t := reader.ReadByte()
|
||||||
|
reader.ReadUInt16()
|
||||||
|
m := reader.ReadTlvMap(2)
|
||||||
|
if t == 0 { // login success
|
||||||
|
if t150, ok := m[0x150]; ok {
|
||||||
|
c.t150 = t150
|
||||||
|
}
|
||||||
|
if t161, ok := m[0x161]; ok {
|
||||||
|
c.decodeT161(t161)
|
||||||
|
}
|
||||||
|
c.decodeT119(m[0x119])
|
||||||
|
return LoginResponse{
|
||||||
|
Success: true,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if t == 2 {
|
||||||
|
c.t104, _ = m[0x104]
|
||||||
|
if m.Exists(0x192) { // slider, not supported yet
|
||||||
|
return LoginResponse{
|
||||||
|
Success: false,
|
||||||
|
Error: UnknownLoginError,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if m.Exists(0x165) { // image
|
||||||
|
imgData := binary.NewReader(m[0x105])
|
||||||
|
signLen := imgData.ReadUInt16()
|
||||||
|
imgData.ReadUInt16()
|
||||||
|
sign := imgData.ReadBytes(int(signLen))
|
||||||
|
return LoginResponse{
|
||||||
|
Success: false,
|
||||||
|
Error: NeedCaptcha,
|
||||||
|
CaptchaImage: imgData.ReadAvailable(),
|
||||||
|
CaptchaSign: sign,
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
return LoginResponse{
|
||||||
|
Success: false,
|
||||||
|
Error: UnknownLoginError,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
} // need captcha
|
||||||
|
|
||||||
|
if t == 160 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if t == 204 {
|
||||||
|
return LoginResponse{
|
||||||
|
Success: false,
|
||||||
|
Error: DeviceLockError,
|
||||||
|
}, nil
|
||||||
|
} // drive lock
|
||||||
|
|
||||||
|
if t149, ok := m[0x149]; ok {
|
||||||
|
t149r := binary.NewReader(t149)
|
||||||
|
t149r.ReadBytes(2)
|
||||||
|
t149r.ReadStringShort() // title
|
||||||
|
return LoginResponse{
|
||||||
|
Success: false,
|
||||||
|
Error: OtherLoginError,
|
||||||
|
ErrorMessage: t149r.ReadStringShort(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t146, ok := m[0x146]; ok {
|
||||||
|
t146r := binary.NewReader(t146)
|
||||||
|
t146r.ReadBytes(4) // ver and code
|
||||||
|
t146r.ReadStringShort() // title
|
||||||
|
return LoginResponse{
|
||||||
|
Success: false,
|
||||||
|
Error: OtherLoginError,
|
||||||
|
ErrorMessage: t146r.ReadStringShort(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil // ?
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeClientRegisterResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
request := &jce.RequestPacket{}
|
||||||
|
request.ReadFrom(jce.NewJceReader(payload))
|
||||||
|
data := &jce.RequestDataVersion2{}
|
||||||
|
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePushReqPacket(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
request := &jce.RequestPacket{}
|
||||||
|
request.ReadFrom(jce.NewJceReader(payload))
|
||||||
|
data := &jce.RequestDataVersion2{}
|
||||||
|
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||||
|
r := jce.NewJceReader(data.Map["PushReq"]["ConfigPush.PushReq"][1:])
|
||||||
|
jceBuf := []byte{}
|
||||||
|
t := r.ReadInt32(1)
|
||||||
|
r.ReadSlice(&jceBuf, 2)
|
||||||
|
seq := r.ReadInt64(3)
|
||||||
|
_, pkt := c.buildPushResponsePacket(t, seq, jceBuf)
|
||||||
|
return nil, c.send(pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMessageSvcPacket(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
rsp := msg.GetMessageResponse{}
|
||||||
|
err := proto.Unmarshal(payload, &rsp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rsp.Result != 0 {
|
||||||
|
return nil, errors.New("message svc result unsuccessful")
|
||||||
|
}
|
||||||
|
c.syncCookie = rsp.SyncCookie
|
||||||
|
c.pubAccountCookie = rsp.PubAccountCookie
|
||||||
|
c.msgCtrlBuf = rsp.MsgCtrlBuf
|
||||||
|
if rsp.UinPairMsgs == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var delItems []*pb.MessageItem
|
||||||
|
for _, pairMsg := range rsp.UinPairMsgs {
|
||||||
|
for _, message := range pairMsg.Messages {
|
||||||
|
// delete message
|
||||||
|
delItem := &pb.MessageItem{
|
||||||
|
FromUin: message.Head.FromUin,
|
||||||
|
ToUin: message.Head.ToUin,
|
||||||
|
MsgType: 187,
|
||||||
|
MsgSeq: message.Head.MsgSeq,
|
||||||
|
MsgUid: message.Head.MsgUid,
|
||||||
|
}
|
||||||
|
delItems = append(delItems, delItem)
|
||||||
|
|
||||||
|
if message.Head.ToUin != c.Uin {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if message.Body.RichText == nil || message.Body.RichText.Elems == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c.lastMessageSeq >= message.Head.MsgSeq {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.lastMessageSeq = message.Head.MsgSeq
|
||||||
|
|
||||||
|
c.dispatchFriendMessage(c.parsePrivateMessage(message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, _ = c.sendAndWait(c.buildDeleteMessageRequestPacket(delItems))
|
||||||
|
if rsp.SyncFlag != msg.SyncFlag_STOP {
|
||||||
|
seq, pkt := c.buildGetMessageRequestPacket(rsp.SyncFlag, time.Now().Unix())
|
||||||
|
_, _ = c.sendAndWait(seq, pkt)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeGroupMessagePacket(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
pkt := msg.PushMessagePacket{}
|
||||||
|
err := proto.Unmarshal(payload, &pkt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if pkt.Message.Head.FromUin == c.Uin {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
c.dispatchGroupMessage(c.parseGroupMessage(pkt.Message))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSvcNotify(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
_, pkt := c.buildGetMessageRequestPacket(msg.SyncFlag_START, time.Now().Unix())
|
||||||
|
return nil, c.send(pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeFriendGroupListResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
request := &jce.RequestPacket{}
|
||||||
|
request.ReadFrom(jce.NewJceReader(payload))
|
||||||
|
data := &jce.RequestDataVersion3{}
|
||||||
|
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||||
|
r := jce.NewJceReader(data.Map["FLRESP"][1:])
|
||||||
|
totalFriendCount := r.ReadInt16(5)
|
||||||
|
friends := []jce.FriendInfo{}
|
||||||
|
r.ReadSlice(&friends, 7)
|
||||||
|
var l []*FriendInfo
|
||||||
|
for _, f := range friends {
|
||||||
|
l = append(l, &FriendInfo{
|
||||||
|
Uin: f.FriendUin,
|
||||||
|
Nickname: f.Nick,
|
||||||
|
Remark: f.Remark,
|
||||||
|
FaceId: f.FaceId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
rsp := FriendListResponse{
|
||||||
|
TotalCount: int32(totalFriendCount),
|
||||||
|
List: l,
|
||||||
|
}
|
||||||
|
return rsp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeGroupListResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
request := &jce.RequestPacket{}
|
||||||
|
request.ReadFrom(jce.NewJceReader(payload))
|
||||||
|
data := &jce.RequestDataVersion3{}
|
||||||
|
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||||
|
r := jce.NewJceReader(data.Map["GetTroopListRespV2"][1:])
|
||||||
|
groups := []jce.TroopNumber{}
|
||||||
|
r.ReadSlice(&groups, 5)
|
||||||
|
var l []*GroupInfo
|
||||||
|
for _, g := range groups {
|
||||||
|
l = append(l, &GroupInfo{
|
||||||
|
Uin: g.GroupUin,
|
||||||
|
Code: g.GroupCode,
|
||||||
|
Name: g.GroupName,
|
||||||
|
Memo: g.GroupMemo,
|
||||||
|
OwnerUin: uint32(g.GroupOwnerUin),
|
||||||
|
MemberCount: uint16(g.MemberNum),
|
||||||
|
MaxMemberCount: uint16(g.MaxGroupMemberNum),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeGroupMemberListResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
request := &jce.RequestPacket{}
|
||||||
|
request.ReadFrom(jce.NewJceReader(payload))
|
||||||
|
data := &jce.RequestDataVersion3{}
|
||||||
|
data.ReadFrom(jce.NewJceReader(request.SBuffer))
|
||||||
|
r := jce.NewJceReader(data.Map["GTMLRESP"][1:])
|
||||||
|
members := []jce.TroopMemberInfo{}
|
||||||
|
r.ReadSlice(&members, 3)
|
||||||
|
next := r.ReadInt64(4)
|
||||||
|
var l []GroupMemberInfo
|
||||||
|
for _, m := range members {
|
||||||
|
l = append(l, GroupMemberInfo{
|
||||||
|
Uin: m.MemberUin,
|
||||||
|
Nickname: m.Nick,
|
||||||
|
CardName: m.Name,
|
||||||
|
Level: uint16(m.MemberLevel),
|
||||||
|
JoinTime: m.JoinTime,
|
||||||
|
LastSpeakTime: m.LastSpeakTime,
|
||||||
|
SpecialTitle: m.SpecialTitle,
|
||||||
|
SpecialTitleExpireTime: m.SpecialTitleExpireTime,
|
||||||
|
Job: m.Job,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return groupMemberListResponse{
|
||||||
|
NextUin: next,
|
||||||
|
list: l,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeGroupImageStoreResponse(c *QQClient, payload []byte) (interface{}, error) {
|
||||||
|
pkt := pb.D388RespBody{}
|
||||||
|
err := proto.Unmarshal(payload, &pkt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rsp := pkt.MsgTryupImgRsp[0]
|
||||||
|
if rsp.Result != 0 {
|
||||||
|
return groupImageUploadResponse{
|
||||||
|
ResultCode: rsp.Result,
|
||||||
|
Message: rsp.FailMsg,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if rsp.BoolFileExit {
|
||||||
|
return groupImageUploadResponse{IsExists: true}, nil
|
||||||
|
}
|
||||||
|
return groupImageUploadResponse{
|
||||||
|
UploadKey: rsp.UpUkey,
|
||||||
|
UploadIp: rsp.Uint32UpIp,
|
||||||
|
UploadPort: rsp.Uint32UpPort,
|
||||||
|
}, nil
|
||||||
|
}
|
80
client/entities.go
Normal file
80
client/entities.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAlreadyRunning = errors.New("already running")
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
LoginError int
|
||||||
|
LoginResponse struct {
|
||||||
|
Success bool
|
||||||
|
Error LoginError
|
||||||
|
|
||||||
|
// Captcha info
|
||||||
|
CaptchaImage []byte
|
||||||
|
CaptchaSign []byte
|
||||||
|
|
||||||
|
// other error
|
||||||
|
ErrorMessage string
|
||||||
|
}
|
||||||
|
|
||||||
|
FriendInfo struct {
|
||||||
|
Uin int64
|
||||||
|
Nickname string
|
||||||
|
Remark string
|
||||||
|
FaceId int16
|
||||||
|
}
|
||||||
|
|
||||||
|
FriendListResponse struct {
|
||||||
|
TotalCount int32
|
||||||
|
List []*FriendInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupInfo struct {
|
||||||
|
Uin int64
|
||||||
|
Code int64
|
||||||
|
Name string
|
||||||
|
Memo string
|
||||||
|
OwnerUin uint32
|
||||||
|
MemberCount uint16
|
||||||
|
MaxMemberCount uint16
|
||||||
|
Members []GroupMemberInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupMemberInfo struct {
|
||||||
|
Uin int64
|
||||||
|
Nickname string
|
||||||
|
CardName string
|
||||||
|
Level uint16
|
||||||
|
JoinTime int64
|
||||||
|
LastSpeakTime int64
|
||||||
|
SpecialTitle string
|
||||||
|
SpecialTitleExpireTime int64
|
||||||
|
Job string
|
||||||
|
}
|
||||||
|
|
||||||
|
groupMemberListResponse struct {
|
||||||
|
NextUin int64
|
||||||
|
list []GroupMemberInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
groupImageUploadResponse struct {
|
||||||
|
ResultCode int32
|
||||||
|
Message string
|
||||||
|
|
||||||
|
IsExists bool
|
||||||
|
|
||||||
|
UploadKey []byte
|
||||||
|
UploadIp []int32
|
||||||
|
UploadPort []int32
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NeedCaptcha LoginError = 1
|
||||||
|
DeviceLockError = 2
|
||||||
|
OtherLoginError = 3
|
||||||
|
UnknownLoginError = -1
|
||||||
|
)
|
57
client/events.go
Normal file
57
client/events.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/message"
|
||||||
|
|
||||||
|
func (c *QQClient) OnPrivateMessage(f func(*QQClient, *message.PrivateMessage)) {
|
||||||
|
c.privateMessageHandlers = append(c.privateMessageHandlers, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) OnPrivateMessageF(filter func(*message.PrivateMessage) bool, f func(*QQClient, *message.PrivateMessage)) {
|
||||||
|
c.privateMessageHandlers = append(c.privateMessageHandlers, func(client *QQClient, msg *message.PrivateMessage) {
|
||||||
|
if filter(msg) {
|
||||||
|
f(client, msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) OnGroupMessage(f func(*QQClient, *message.GroupMessage)) {
|
||||||
|
c.groupMessageHandlers = append(c.groupMessageHandlers, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUinFilterPrivate(uin int64) func(*message.PrivateMessage) bool {
|
||||||
|
return func(msg *message.PrivateMessage) bool {
|
||||||
|
return msg.Sender.Uin == uin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) dispatchFriendMessage(msg *message.PrivateMessage) {
|
||||||
|
if msg == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, f := range c.privateMessageHandlers {
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if pan := recover(); pan != nil {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
f(c, msg)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) dispatchGroupMessage(msg *message.GroupMessage) {
|
||||||
|
if msg == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, f := range c.groupMessageHandlers {
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if pan := recover(); pan != nil {
|
||||||
|
// TODO: logger
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
f(c, msg)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
201
client/global.go
Normal file
201
client/global.go
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
devinfo "github.com/Mrs4s/MiraiGo/client/pb"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
|
"github.com/Mrs4s/MiraiGo/message"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceInfo struct {
|
||||||
|
Display []byte
|
||||||
|
Product []byte
|
||||||
|
Device []byte
|
||||||
|
Board []byte
|
||||||
|
Brand []byte
|
||||||
|
Model []byte
|
||||||
|
Bootloader []byte
|
||||||
|
FingerPrint []byte
|
||||||
|
BootId []byte
|
||||||
|
ProcVersion []byte
|
||||||
|
BaseBand []byte
|
||||||
|
SimInfo []byte
|
||||||
|
OSType []byte
|
||||||
|
MacAddress []byte
|
||||||
|
IpAddress []byte
|
||||||
|
WifiBSSID []byte
|
||||||
|
WifiSSID []byte
|
||||||
|
IMSIMd5 []byte
|
||||||
|
IMEI string
|
||||||
|
AndroidId []byte
|
||||||
|
APN []byte
|
||||||
|
Guid []byte
|
||||||
|
TgtgtKey []byte
|
||||||
|
Version *Version
|
||||||
|
}
|
||||||
|
|
||||||
|
type Version struct {
|
||||||
|
Incremental []byte
|
||||||
|
Release []byte
|
||||||
|
CodeName []byte
|
||||||
|
Sdk uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
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: []byte{},
|
||||||
|
SimInfo: []byte("T-Mobile"),
|
||||||
|
OSType: []byte("android"),
|
||||||
|
MacAddress: []byte("00:50:56:C0:00:08"),
|
||||||
|
IpAddress: []byte{10, 0, 1, 3}, // 10.0.1.3
|
||||||
|
WifiBSSID: []byte("00:50:56:C0:00:08"),
|
||||||
|
WifiSSID: []byte("<unknown ssid>"),
|
||||||
|
IMEI: "468356291846738",
|
||||||
|
AndroidId: []byte("MIRAI.123456.001"),
|
||||||
|
APN: []byte("wifi"),
|
||||||
|
Version: &Version{
|
||||||
|
Incremental: []byte("5891938"),
|
||||||
|
Release: []byte("10"),
|
||||||
|
CodeName: []byte("REL"),
|
||||||
|
Sdk: 29,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var EmptyBytes = []byte{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
r := make([]byte, 16)
|
||||||
|
rand.Read(r)
|
||||||
|
t := md5.Sum(r)
|
||||||
|
SystemDeviceInfo.IMSIMd5 = t[:]
|
||||||
|
SystemDeviceInfo.GenNewGuid()
|
||||||
|
SystemDeviceInfo.GenNewTgtgtKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *DeviceInfo) GenNewGuid() {
|
||||||
|
t := md5.Sum(append(info.AndroidId, info.MacAddress...))
|
||||||
|
info.Guid = t[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *DeviceInfo) GenNewTgtgtKey() {
|
||||||
|
r := make([]byte, 16)
|
||||||
|
rand.Read(r)
|
||||||
|
t := md5.Sum(append(r, info.Guid...))
|
||||||
|
info.TgtgtKey = t[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *DeviceInfo) GenDeviceInfoData() []byte {
|
||||||
|
msg := &devinfo.DeviceInfo{
|
||||||
|
Bootloader: string(info.Bootloader),
|
||||||
|
ProcVersion: string(info.ProcVersion),
|
||||||
|
Codename: string(info.Version.CodeName),
|
||||||
|
Incremental: string(info.Version.Incremental),
|
||||||
|
Fingerprint: string(info.FingerPrint),
|
||||||
|
BootId: string(info.BootId),
|
||||||
|
AndroidId: string(info.AndroidId),
|
||||||
|
BaseBand: string(info.BaseBand),
|
||||||
|
InnerVersion: string(info.Version.Incremental),
|
||||||
|
}
|
||||||
|
data, err := proto.Marshal(msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage {
|
||||||
|
switch msg.Head.MsgType {
|
||||||
|
case 166:
|
||||||
|
friend := c.FindFriend(msg.Head.FromUin)
|
||||||
|
if friend == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &message.PrivateMessage{
|
||||||
|
Id: msg.Head.MsgSeq,
|
||||||
|
Sender: &message.Sender{
|
||||||
|
Uin: friend.Uin,
|
||||||
|
Nickname: friend.Nickname,
|
||||||
|
},
|
||||||
|
Elements: parseMessageElems(msg.Body.RichText.Elems),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage {
|
||||||
|
group := c.FindGroup(m.Head.GroupInfo.GroupCode)
|
||||||
|
if group == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var anonInfo *msg.AnonymousGroupMessage
|
||||||
|
for _, e := range m.Body.RichText.Elems {
|
||||||
|
if e.AnonGroupMsg != nil {
|
||||||
|
anonInfo = e.AnonGroupMsg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var sender *message.Sender
|
||||||
|
if anonInfo != nil {
|
||||||
|
sender = &message.Sender{
|
||||||
|
Uin: 80000000,
|
||||||
|
Nickname: string(anonInfo.AnonNick),
|
||||||
|
IsFriend: false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mem := group.FindMember(m.Head.FromUin)
|
||||||
|
if mem == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sender = &message.Sender{
|
||||||
|
Uin: mem.Uin,
|
||||||
|
Nickname: mem.Nickname,
|
||||||
|
CardName: mem.CardName,
|
||||||
|
IsFriend: c.FindFriend(mem.Uin) != nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g := &message.GroupMessage{
|
||||||
|
Id: m.Head.MsgSeq,
|
||||||
|
GroupUin: m.Head.GroupInfo.GroupCode,
|
||||||
|
GroupName: string(m.Head.GroupInfo.GroupName),
|
||||||
|
Sender: sender,
|
||||||
|
Elements: parseMessageElems(m.Body.RichText.Elems),
|
||||||
|
IsAdministrator: false,
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMessageElems(elems []*msg.Elem) []message.IMessageElement {
|
||||||
|
var res []message.IMessageElement
|
||||||
|
for _, elem := range elems {
|
||||||
|
if elem.Text != nil {
|
||||||
|
res = append(res, message.NewText(elem.Text.Str))
|
||||||
|
}
|
||||||
|
if elem.CustomFace != nil {
|
||||||
|
res = append(res, message.NewNetImage(elem.CustomFace.FilePath, "http://gchat.qpic.cn/"+elem.CustomFace.OrigUrl))
|
||||||
|
}
|
||||||
|
if elem.NotOnlineImage != nil {
|
||||||
|
var img string
|
||||||
|
if elem.NotOnlineImage.OrigUrl != "" {
|
||||||
|
img = "http://c2cpicdw.qpic.cn" + elem.NotOnlineImage.OrigUrl
|
||||||
|
} else {
|
||||||
|
img = "http://c2cpicdw.qpic.cn/offpic_new/0/" + elem.NotOnlineImage.ResId + "/0?term=2"
|
||||||
|
}
|
||||||
|
res = append(res, message.NewNetImage(elem.NotOnlineImage.FilePath, img))
|
||||||
|
}
|
||||||
|
if elem.Face != nil {
|
||||||
|
res = append(res, message.NewFace(elem.Face.Index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
1959
client/pb/data.pb.go
Normal file
1959
client/pb/data.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
157
client/pb/data.proto
Normal file
157
client/pb/data.proto
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option go_package = ".;pb";
|
||||||
|
|
||||||
|
message DeviceInfo {
|
||||||
|
string bootloader = 1;
|
||||||
|
string procVersion = 2;
|
||||||
|
string codename = 3;
|
||||||
|
string incremental = 4;
|
||||||
|
string fingerprint = 5;
|
||||||
|
string bootId = 6;
|
||||||
|
string androidId = 7;
|
||||||
|
string baseBand = 8;
|
||||||
|
string innerVersion = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequestBody {
|
||||||
|
repeated ConfigSeq rpt_config_list = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConfigSeq {
|
||||||
|
int32 type = 1;
|
||||||
|
int32 version = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message D50ReqBody {
|
||||||
|
int64 appid = 1;
|
||||||
|
int32 maxPkgSize = 2;
|
||||||
|
int32 startTime = 3;
|
||||||
|
int32 startIndex = 4;
|
||||||
|
int32 reqNum = 5;
|
||||||
|
repeated int64 uinList = 6;
|
||||||
|
int32 reqMusicSwitch = 91001;
|
||||||
|
int32 reqMutualmarkAlienation = 101001;
|
||||||
|
int32 reqMutualmarkScore = 141001;
|
||||||
|
int32 reqKsingSwitch = 151001;
|
||||||
|
int32 reqMutualmarkLbsshare = 181001;
|
||||||
|
}
|
||||||
|
|
||||||
|
message D388ReqBody {
|
||||||
|
int32 netType = 1;
|
||||||
|
int32 subcmd = 2;
|
||||||
|
repeated TryUpImgReq msgTryupImgReq = 3;
|
||||||
|
int32 commandId = 7;
|
||||||
|
bytes extension = 1001;
|
||||||
|
}
|
||||||
|
|
||||||
|
message D388RespBody {
|
||||||
|
int32 clientIp = 1;
|
||||||
|
int32 subCmd = 2;
|
||||||
|
repeated TryUpImgResp msgTryupImgRsp = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ReqDataHighwayHead {
|
||||||
|
DataHighwayHead msgBasehead = 1;
|
||||||
|
SegHead msgSeghead = 2;
|
||||||
|
bytes reqExtendinfo = 3;
|
||||||
|
int64 timestamp = 4;
|
||||||
|
//LoginSigHead? msgLoginSigHead = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RspDataHighwayHead {
|
||||||
|
DataHighwayHead msgBasehead = 1;
|
||||||
|
SegHead msgSeghead = 2;
|
||||||
|
int32 errorCode = 3;
|
||||||
|
int32 allowRetry = 4;
|
||||||
|
int32 cachecost = 5;
|
||||||
|
int32 htcost = 6;
|
||||||
|
bytes rspExtendinfo = 7;
|
||||||
|
int64 timestamp = 8;
|
||||||
|
int64 range = 9;
|
||||||
|
int32 isReset = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DataHighwayHead {
|
||||||
|
int32 version = 1;
|
||||||
|
string uin = 2;
|
||||||
|
string command = 3;
|
||||||
|
int32 seq = 4;
|
||||||
|
int32 retryTimes = 5;
|
||||||
|
int32 appid = 6;
|
||||||
|
int32 dataflag = 7;
|
||||||
|
int32 commandId = 8;
|
||||||
|
string buildVer = 9;
|
||||||
|
int32 localeId = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SegHead {
|
||||||
|
int32 serviceid = 1;
|
||||||
|
int64 filesize = 2;
|
||||||
|
int64 dataoffset = 3;
|
||||||
|
int32 datalength = 4;
|
||||||
|
int32 rtcode = 5;
|
||||||
|
bytes serviceticket = 6;
|
||||||
|
int32 flag = 7;
|
||||||
|
bytes md5 = 8;
|
||||||
|
bytes fileMd5 = 9;
|
||||||
|
int32 cacheAddr = 10;
|
||||||
|
int32 queryTimes = 11;
|
||||||
|
int32 updateCacheip = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TryUpImgReq {
|
||||||
|
int64 groupCode = 1;
|
||||||
|
int64 srcUin = 2;
|
||||||
|
int64 fileId = 3;
|
||||||
|
bytes fileMd5 = 4;
|
||||||
|
int64 fileSize = 5;
|
||||||
|
string fileName = 6;
|
||||||
|
int32 srcTerm = 7;
|
||||||
|
int32 platformType = 8;
|
||||||
|
int32 buType = 9;
|
||||||
|
int32 picWidth = 10;
|
||||||
|
int32 picHeight = 11;
|
||||||
|
int32 picType = 12;
|
||||||
|
string buildVer = 13;
|
||||||
|
int32 innerIp = 14;
|
||||||
|
int32 appPicType = 15;
|
||||||
|
int32 originalPic = 16;
|
||||||
|
bytes fileIndex = 17;
|
||||||
|
int64 dstUin = 18;
|
||||||
|
int32 srvUpload = 19;
|
||||||
|
bytes transferUrl = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TryUpImgResp {
|
||||||
|
int64 fileId = 1;
|
||||||
|
int32 result = 2;
|
||||||
|
string failMsg = 3;
|
||||||
|
bool boolFileExit = 4;
|
||||||
|
ImgInfo msgImgInfo = 5;
|
||||||
|
repeated int32 uint32UpIp = 6;
|
||||||
|
repeated int32 uint32UpPort = 7;
|
||||||
|
bytes upUkey = 8;
|
||||||
|
int64 fid = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ImgInfo {
|
||||||
|
bytes fileMd5 = 1;
|
||||||
|
int32 fileType = 2;
|
||||||
|
int64 fileSize = 3;
|
||||||
|
int32 fileWidth = 4;
|
||||||
|
int32 fileHeight = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteMessageRequest {
|
||||||
|
repeated MessageItem items = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageItem {
|
||||||
|
int64 fromUin = 1;
|
||||||
|
int64 toUin = 2;
|
||||||
|
int32 msgType = 3;
|
||||||
|
int32 msgSeq = 4;
|
||||||
|
int64 msgUid = 5;
|
||||||
|
bytes sig = 7;
|
||||||
|
}
|
5402
client/pb/msg/msg.pb.go
Normal file
5402
client/pb/msg/msg.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
519
client/pb/msg/msg.proto
Normal file
519
client/pb/msg/msg.proto
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option go_package = ".;msg";
|
||||||
|
|
||||||
|
message GetMessageRequest {
|
||||||
|
SyncFlag syncFlag = 1;
|
||||||
|
bytes syncCookie = 2;
|
||||||
|
int32 rambleFlag = 3;
|
||||||
|
int32 latestRambleNumber = 4;
|
||||||
|
int32 otherRambleNumber = 5;
|
||||||
|
int32 onlineSyncFlag = 6;
|
||||||
|
int32 contextFlag = 7;
|
||||||
|
int32 whisperSessionId = 8;
|
||||||
|
int32 msgReqType = 9;
|
||||||
|
bytes pubaccountCookie = 10;
|
||||||
|
bytes msgCtrlBuf = 11;
|
||||||
|
bytes serverBuf = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SendMessageRequest {
|
||||||
|
RoutingHead routingHead = 1;
|
||||||
|
ContentHead contentHead = 2;
|
||||||
|
MessageBody msgBody = 3;
|
||||||
|
int32 msgSeq = 4;
|
||||||
|
int32 msgRand = 5;
|
||||||
|
bytes syncCookie = 6;
|
||||||
|
//MsgComm.AppShareInfo? appShare = 7;
|
||||||
|
int32 msgVia = 8;
|
||||||
|
int32 dataStatist = 9;
|
||||||
|
//MultiMsgAssist? multiMsgAssist = 10;
|
||||||
|
//PbInputNotifyInfo? inputNotifyInfo = 11;
|
||||||
|
MsgCtrl msgCtrl = 12;
|
||||||
|
//ImReceipt.ReceiptReq? receiptReq = 13;
|
||||||
|
int32 multiSendSeq = 14;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message RoutingHead {
|
||||||
|
C2C c2c = 1;
|
||||||
|
Grp grp = 2;
|
||||||
|
GrpTmp grpTmp = 3;
|
||||||
|
/*
|
||||||
|
Dis dis = 4;
|
||||||
|
DisTmp disTmp = 5;
|
||||||
|
WPATmp? wpaTmp = 6;
|
||||||
|
SecretFileHead? secretFile = 7;
|
||||||
|
PublicPlat? publicPlat = 8;
|
||||||
|
TransMsg? transMsg = 9;
|
||||||
|
AddressListTmp? addressList = 10;
|
||||||
|
RichStatusTmp? richStatusTmp = 11;
|
||||||
|
TransCmd? transCmd = 12;
|
||||||
|
AccostTmp? accostTmp = 13;
|
||||||
|
PubGroupTmp? pubGroupTmp = 14;
|
||||||
|
Trans0x211? trans0x211 = 15;
|
||||||
|
BusinessWPATmp? businessWpaTmp = 16;
|
||||||
|
AuthTmp? authTmp = 17;
|
||||||
|
BsnsTmp? bsnsTmp = 18;
|
||||||
|
QQQueryBusinessTmp? qqQuerybusinessTmp = 19;
|
||||||
|
NearByDatingTmp? nearbyDatingTmp = 20;
|
||||||
|
NearByAssistantTmp? nearbyAssistantTmp = 21;
|
||||||
|
CommTmp? commTmp = 22;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
message C2C {
|
||||||
|
int64 toUin = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Grp {
|
||||||
|
int64 groupCode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GrpTmp {
|
||||||
|
int64 groupUin = 1;
|
||||||
|
int64 toUin = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MsgCtrl {
|
||||||
|
int32 msgFlag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetMessageResponse {
|
||||||
|
int32 result = 1;
|
||||||
|
string errorMessage = 2;
|
||||||
|
bytes syncCookie = 3;
|
||||||
|
SyncFlag syncFlag = 4;
|
||||||
|
repeated UinPairMessage uinPairMsgs = 5;
|
||||||
|
int64 bindUin = 6;
|
||||||
|
int32 msgRspType = 7;
|
||||||
|
bytes pubAccountCookie = 8;
|
||||||
|
bool isPartialSync = 9;
|
||||||
|
bytes msgCtrlBuf = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PushMessagePacket {
|
||||||
|
Message message = 1;
|
||||||
|
int32 svrip = 2;
|
||||||
|
bytes pushToken = 3;
|
||||||
|
int32 pingFLag = 4;
|
||||||
|
int32 generalFlag = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UinPairMessage {
|
||||||
|
int32 lastReadTime = 1;
|
||||||
|
int64 peerUin = 2;
|
||||||
|
int32 msgCompleted = 3;
|
||||||
|
repeated Message messages = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Message {
|
||||||
|
MessageHead head = 1;
|
||||||
|
MessageBody body = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageBody {
|
||||||
|
RichText richText = 1;
|
||||||
|
bytes msgContent = 2;
|
||||||
|
bytes msgEncryptContent = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RichText {
|
||||||
|
Attr attr = 1;
|
||||||
|
repeated Elem elems = 2;
|
||||||
|
NotOnlineFile notOnlineFile = 3;
|
||||||
|
Ptt ptt = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Elem {
|
||||||
|
Text text = 1;
|
||||||
|
Face face = 2;
|
||||||
|
OnlineImage onlineImage = 3;
|
||||||
|
NotOnlineImage notOnlineImage = 4;
|
||||||
|
//TransElem transElemInfo = 5;
|
||||||
|
//MarketFace marketFace = 6;
|
||||||
|
//ElemFlags elemFlags = 7;
|
||||||
|
CustomFace customFace = 8;
|
||||||
|
//ElemFlags2 elemFlags2 = 9;
|
||||||
|
//FunFace funFace = 10;
|
||||||
|
//SecretFileMsg secretFile = 11;
|
||||||
|
//RichMsg richMsg = 12;
|
||||||
|
GroupFile groupFile = 13;
|
||||||
|
//PubGroup pubGroup = 14;
|
||||||
|
//MarketTrans marketTrans = 15;
|
||||||
|
ExtraInfo extraInfo = 16;
|
||||||
|
//ShakeWindow? shakeWindow = 17;
|
||||||
|
//PubAccount? pubAccount = 18;
|
||||||
|
VideoFile videoFile = 19;
|
||||||
|
//TipsInfo? tipsInfo = 20;
|
||||||
|
AnonymousGroupMessage anonGroupMsg = 21;
|
||||||
|
//QQLiveOld? qqLiveOld = 22;
|
||||||
|
//LifeOnlineAccount? lifeOnline = 23;
|
||||||
|
//QQWalletMsg? qqwalletMsg = 24;
|
||||||
|
//CrmElem? crmElem = 25;
|
||||||
|
//ConferenceTipsInfo? conferenceTipsInfo = 26;
|
||||||
|
//RedBagInfo? redbagInfo = 27;
|
||||||
|
//LowVersionTips? lowVersionTips = 28;
|
||||||
|
//bytes bankcodeCtrlInfo = 29;
|
||||||
|
//NearByMessageType? nearByMsg = 30;
|
||||||
|
CustomElem customElem = 31;
|
||||||
|
//LocationInfo? locationInfo = 32;
|
||||||
|
//PubAccInfo? pubAccInfo = 33;
|
||||||
|
//SmallEmoji? smallEmoji = 34;
|
||||||
|
//FSJMessageElem? fsjMsgElem = 35;
|
||||||
|
//ArkAppElem? arkApp = 36;
|
||||||
|
//GeneralFlags? generalFlags = 37;
|
||||||
|
//CustomFace? hcFlashPic = 38;
|
||||||
|
//DeliverGiftMsg? deliverGiftMsg = 39;
|
||||||
|
//BitAppMsg? bitappMsg = 40;
|
||||||
|
//OpenQQData? openQqData = 41;
|
||||||
|
//ApolloActMsg? apolloMsg = 42;
|
||||||
|
//GroupPubAccountInfo? groupPubAccInfo = 43;
|
||||||
|
//BlessingMessage? blessMsg = 44;
|
||||||
|
//SourceMsg? srcMsg = 45;
|
||||||
|
//LolaMsg? lolaMsg = 46;
|
||||||
|
//GroupBusinessMsg? groupBusinessMsg = 47;
|
||||||
|
//WorkflowNotifyMsg? msgWorkflowNotify = 48;
|
||||||
|
//PatsElem? patElem = 49;
|
||||||
|
//GroupPostElem? groupPostElem = 50;
|
||||||
|
//LightAppElem? lightApp = 51;
|
||||||
|
//EIMInfo? eimInfo = 52;
|
||||||
|
//CommonElem? commonElem = 53;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CustomElem {
|
||||||
|
bytes desc = 1;
|
||||||
|
bytes data = 2;
|
||||||
|
int32 enumType = 3;
|
||||||
|
bytes ext = 4;
|
||||||
|
bytes sound = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Text {
|
||||||
|
string str = 1;
|
||||||
|
string link = 2;
|
||||||
|
bytes attr6Buf = 3;
|
||||||
|
bytes attr7Buf = 4;
|
||||||
|
bytes buf = 11;
|
||||||
|
bytes pbReserve = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Attr {
|
||||||
|
int32 codePage = 1;
|
||||||
|
int32 time = 2;
|
||||||
|
int32 random = 3;
|
||||||
|
int32 color = 4;
|
||||||
|
int32 size = 5;
|
||||||
|
int32 effect = 6;
|
||||||
|
int32 charSet = 7;
|
||||||
|
int32 pitchAndFamily = 8;
|
||||||
|
string fontName = 9;
|
||||||
|
bytes reserveData = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Ptt {
|
||||||
|
int32 fileType = 1;
|
||||||
|
int64 srcUin = 2;
|
||||||
|
bytes fileUuid = 3;
|
||||||
|
bytes fileMd5 = 4;
|
||||||
|
bytes fileName = 5;
|
||||||
|
int32 fileSize = 6;
|
||||||
|
bytes reserve = 7;
|
||||||
|
int32 fileId = 8;
|
||||||
|
int32 serverIp = 9;
|
||||||
|
int32 serverPort = 10;
|
||||||
|
bool boolValid = 11;
|
||||||
|
bytes signature = 12;
|
||||||
|
bytes shortcut = 13;
|
||||||
|
bytes fileKey = 14;
|
||||||
|
int32 magicPttIndex = 15;
|
||||||
|
int32 voiceSwitch = 16;
|
||||||
|
bytes pttUrl = 17;
|
||||||
|
bytes groupFileKey = 18;
|
||||||
|
int32 time = 19;
|
||||||
|
bytes downPara = 20;
|
||||||
|
int32 format = 29;
|
||||||
|
bytes pbReserve = 30;
|
||||||
|
repeated bytes bytesPttUrls = 31;
|
||||||
|
int32 downloadFlag = 32;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message OnlineImage {
|
||||||
|
bytes guid = 1;
|
||||||
|
bytes filePath = 2;
|
||||||
|
bytes oldVerSendFile = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NotOnlineImage {
|
||||||
|
string filePath = 1;
|
||||||
|
int32 fileLen = 2;
|
||||||
|
string downloadPath = 3;
|
||||||
|
bytes oldVerSendFile = 4;
|
||||||
|
int32 imgType = 5;
|
||||||
|
bytes previewsImage = 6;
|
||||||
|
bytes picMd5 = 7;
|
||||||
|
int32 picHeight = 8;
|
||||||
|
int32 picWidth = 9;
|
||||||
|
string resId = 10;
|
||||||
|
bytes flag = 11;
|
||||||
|
string thumbUrl = 12;
|
||||||
|
int32 original = 13;
|
||||||
|
string bigUrl = 14;
|
||||||
|
string origUrl = 15;
|
||||||
|
int32 bizType = 16;
|
||||||
|
int32 result = 17;
|
||||||
|
int32 index = 18;
|
||||||
|
bytes opFaceBuf = 19;
|
||||||
|
bool oldPicMd5 = 20;
|
||||||
|
int32 thumbWidth = 21;
|
||||||
|
int32 thumbHeight = 22;
|
||||||
|
int32 fileId = 23;
|
||||||
|
int32 showLen = 24;
|
||||||
|
int32 downloadLen = 25;
|
||||||
|
bytes pbReserve = 29;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NotOnlineFile {
|
||||||
|
int32 fileType = 1;
|
||||||
|
bytes sig = 2;
|
||||||
|
bytes fileUuid = 3;
|
||||||
|
bytes fileMd5 = 4;
|
||||||
|
bytes fileName = 5;
|
||||||
|
int64 fileSize = 6;
|
||||||
|
bytes note = 7;
|
||||||
|
int32 reserved = 8;
|
||||||
|
int32 subcmd = 9;
|
||||||
|
int32 microCloud = 10;
|
||||||
|
repeated bytes bytesFileUrls = 11;
|
||||||
|
int32 downloadFlag = 12;
|
||||||
|
int32 dangerEvel = 50;
|
||||||
|
int32 lifeTime = 51;
|
||||||
|
int32 uploadTime = 52;
|
||||||
|
int32 absFileType = 53;
|
||||||
|
int32 clientType = 54;
|
||||||
|
int32 expireTime = 55;
|
||||||
|
bytes pbReserve = 56;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtraInfo {
|
||||||
|
bytes nick = 1;
|
||||||
|
bytes groupCard = 2;
|
||||||
|
int32 level = 3;
|
||||||
|
int32 flags = 4;
|
||||||
|
int32 groupMask = 5;
|
||||||
|
int32 msgTailId = 6;
|
||||||
|
bytes senderTitle = 7;
|
||||||
|
bytes apnsTips = 8;
|
||||||
|
int64 uin = 9;
|
||||||
|
int32 msgStateFlag = 10;
|
||||||
|
int32 apnsSoundType = 11;
|
||||||
|
int32 newGroupFlag = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GroupFile {
|
||||||
|
bytes filename = 1;
|
||||||
|
int64 fileSize = 2;
|
||||||
|
bytes fileId = 3;
|
||||||
|
bytes batchId = 4;
|
||||||
|
bytes fileKey = 5;
|
||||||
|
bytes mark = 6;
|
||||||
|
int64 sequence = 7;
|
||||||
|
bytes batchItemId = 8;
|
||||||
|
int32 feedMsgTime = 9;
|
||||||
|
bytes pbReserve = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AnonymousGroupMessage {
|
||||||
|
int32 flags = 1;
|
||||||
|
bytes anonId = 2;
|
||||||
|
bytes anonNick = 3;
|
||||||
|
int32 headPortrait = 4;
|
||||||
|
int32 expireTime = 5;
|
||||||
|
int32 bubbleId = 6;
|
||||||
|
bytes rankColor = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VideoFile {
|
||||||
|
bytes fileUuid = 1;
|
||||||
|
bytes fileMd5 = 2;
|
||||||
|
bytes fileName = 3;
|
||||||
|
int32 fileFormat = 4;
|
||||||
|
int32 fileTime = 5;
|
||||||
|
int32 fileSize = 6;
|
||||||
|
int32 thumbWidth = 7;
|
||||||
|
int32 thumbHeight = 8;
|
||||||
|
bytes thumbFileMd5 = 9;
|
||||||
|
bytes source = 10;
|
||||||
|
int32 thumbFileSize = 11;
|
||||||
|
int32 busiType = 12;
|
||||||
|
int32 fromChatType = 13;
|
||||||
|
int32 toChatType = 14;
|
||||||
|
bool boolSupportProgressive = 15;
|
||||||
|
int32 fileWidth = 16;
|
||||||
|
int32 fileHeight = 17;
|
||||||
|
int32 subBusiType = 18;
|
||||||
|
int32 videoAttr = 19;
|
||||||
|
repeated bytes bytesThumbFileUrls = 20;
|
||||||
|
repeated bytes bytesVideoFileUrls = 21;
|
||||||
|
int32 thumbDownloadFlag = 22;
|
||||||
|
int32 videoDownloadFlag = 23;
|
||||||
|
bytes pbReserve = 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Face {
|
||||||
|
int32 index = 1;
|
||||||
|
bytes old = 2;
|
||||||
|
bytes buf = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CustomFace {
|
||||||
|
bytes guid = 1;
|
||||||
|
string filePath = 2;
|
||||||
|
string shortcut = 3;
|
||||||
|
bytes buffer = 4;
|
||||||
|
bytes flag = 5;
|
||||||
|
bytes oldData = 6;
|
||||||
|
int32 fileId = 7;
|
||||||
|
int32 serverIp = 8;
|
||||||
|
int32 serverPort = 9;
|
||||||
|
int32 fileType = 10;
|
||||||
|
bytes signature = 11;
|
||||||
|
int32 useful = 12;
|
||||||
|
bytes md5 = 13;
|
||||||
|
string thumbUrl = 14;
|
||||||
|
string bigUrl = 15;
|
||||||
|
string origUrl = 16;
|
||||||
|
int32 bizType = 17;
|
||||||
|
int32 repeatIndex = 18;
|
||||||
|
int32 repeatImage = 19;
|
||||||
|
int32 imageType = 20;
|
||||||
|
int32 index = 21;
|
||||||
|
int32 width = 22;
|
||||||
|
int32 height = 23;
|
||||||
|
int32 source = 24;
|
||||||
|
int32 size = 25;
|
||||||
|
int32 origin = 26;
|
||||||
|
int32 thumbWidth = 27;
|
||||||
|
int32 thumbHeight = 28;
|
||||||
|
int32 showLen = 29;
|
||||||
|
int32 downloadLen = 30;
|
||||||
|
string _400Url = 31;
|
||||||
|
int32 _400Width = 32;
|
||||||
|
int32 _400Height = 33;
|
||||||
|
bytes pbReserve = 34;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ContentHead {
|
||||||
|
int32 pkgNum = 1;
|
||||||
|
int32 pkgIndex = 2;
|
||||||
|
int32 divSeq = 3;
|
||||||
|
int32 autoReply = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageHead {
|
||||||
|
int64 fromUin = 1;
|
||||||
|
int64 toUin = 2;
|
||||||
|
int32 msgType = 3;
|
||||||
|
int32 c2cCmd = 4;
|
||||||
|
int32 msgSeq = 5;
|
||||||
|
int32 msgTime = 6;
|
||||||
|
int64 msgUid = 7;
|
||||||
|
C2CTempMessageHead c2cTmpMsgHead = 8;
|
||||||
|
GroupInfo groupInfo = 9;
|
||||||
|
int32 fromAppid = 10;
|
||||||
|
int32 fromInstid = 11;
|
||||||
|
int32 userActive = 12;
|
||||||
|
DiscussInfo discussInfo = 13;
|
||||||
|
string fromNick = 14;
|
||||||
|
int64 authUin = 15;
|
||||||
|
string authNick = 16;
|
||||||
|
int32 msgFlag = 17;
|
||||||
|
string authRemark = 18;
|
||||||
|
string groupName = 19;
|
||||||
|
MutilTransHead mutiltransHead = 20;
|
||||||
|
InstCtrl msgInstCtrl = 21;
|
||||||
|
int32 publicAccountGroupSendFlag = 22;
|
||||||
|
int32 wseqInC2cMsghead = 23;
|
||||||
|
int64 cpid = 24;
|
||||||
|
ExtGroupKeyInfo extGroupKeyInfo = 25;
|
||||||
|
string multiCompatibleText = 26;
|
||||||
|
int32 authSex = 27;
|
||||||
|
bool isSrcMsg = 28;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message GroupInfo {
|
||||||
|
int64 groupCode = 1;
|
||||||
|
int32 groupType = 2;
|
||||||
|
int64 groupInfoSeq = 3;
|
||||||
|
string groupCard = 4;
|
||||||
|
bytes groupRank = 5;
|
||||||
|
int32 groupLevel = 6;
|
||||||
|
int32 groupCardType = 7;
|
||||||
|
bytes groupName = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DiscussInfo {
|
||||||
|
int64 discussUin = 1;
|
||||||
|
int32 discussType = 2;
|
||||||
|
int64 discussInfoSeq = 3;
|
||||||
|
bytes discussRemark = 4;
|
||||||
|
bytes discussName = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MutilTransHead{
|
||||||
|
int32 status = 1;
|
||||||
|
int32 msgId = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message C2CTempMessageHead {
|
||||||
|
int32 c2cType = 1;
|
||||||
|
int32 serviceType = 2;
|
||||||
|
int64 groupUin = 3;
|
||||||
|
int64 groupCode = 4;
|
||||||
|
bytes sig = 5;
|
||||||
|
int32 sigType = 6;
|
||||||
|
string fromPhone = 7;
|
||||||
|
string toPhone = 8;
|
||||||
|
int32 lockDisplay = 9;
|
||||||
|
int32 directionFlag = 10;
|
||||||
|
bytes reserved = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InstCtrl {
|
||||||
|
repeated InstInfo msgSendToInst = 1;
|
||||||
|
repeated InstInfo msgExcludeInst = 2;
|
||||||
|
InstInfo msgFromInst = 3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message InstInfo {
|
||||||
|
int32 apppid = 1;
|
||||||
|
int32 instid = 2;
|
||||||
|
int32 platform = 3;
|
||||||
|
int32 enumDeviceType = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExtGroupKeyInfo {
|
||||||
|
int32 curMaxSeq = 1;
|
||||||
|
int64 curTime = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SyncCookie {
|
||||||
|
int64 time1 = 1;
|
||||||
|
int64 time = 2;
|
||||||
|
int64 ran1 = 3;
|
||||||
|
int64 ran2 = 4;
|
||||||
|
int64 const1 = 5;
|
||||||
|
int64 const2 = 11;
|
||||||
|
int64 const3 = 12;
|
||||||
|
int64 lastSyncTime = 13;
|
||||||
|
int64 const4 = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SyncFlag {
|
||||||
|
START = 0;
|
||||||
|
CONTINUME = 1;
|
||||||
|
STOP = 2;
|
||||||
|
}
|
153
client/tlv_decoders.go
Normal file
153
client/tlv_decoders.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- tlv decoders for qq client ---
|
||||||
|
|
||||||
|
func (c *QQClient) decodeT161(data []byte) {
|
||||||
|
reader := binary.NewReader(data)
|
||||||
|
reader.ReadBytes(2)
|
||||||
|
t := reader.ReadTlvMap(2)
|
||||||
|
if t172, ok := t[0x172]; ok {
|
||||||
|
c.rollbackSig = t172
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) decodeT119(data []byte) {
|
||||||
|
tea := binary.NewTeaCipher(SystemDeviceInfo.TgtgtKey)
|
||||||
|
reader := binary.NewReader(tea.Decrypt(data))
|
||||||
|
reader.ReadBytes(2)
|
||||||
|
m := reader.ReadTlvMap(2)
|
||||||
|
|
||||||
|
if t130, ok := m[0x130]; ok {
|
||||||
|
c.decodeT130(t130)
|
||||||
|
}
|
||||||
|
if t113, ok := m[0x113]; ok {
|
||||||
|
c.decodeT113(t113)
|
||||||
|
}
|
||||||
|
if t528, ok := m[0x528]; ok {
|
||||||
|
c.t528 = t528
|
||||||
|
}
|
||||||
|
if t530, ok := m[0x530]; ok {
|
||||||
|
c.t530 = t530
|
||||||
|
}
|
||||||
|
if t108, ok := m[0x108]; ok {
|
||||||
|
c.ksid = t108
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//openId []byte
|
||||||
|
//openKey []byte
|
||||||
|
//payToken []byte
|
||||||
|
//pf []byte
|
||||||
|
//pfkey []byte
|
||||||
|
gender uint16 = 0
|
||||||
|
age uint16 = 0
|
||||||
|
nick = ""
|
||||||
|
//a1 []byte
|
||||||
|
//noPicSig []byte
|
||||||
|
//ctime = time.Now().Unix()
|
||||||
|
//etime = ctime + 2160000
|
||||||
|
)
|
||||||
|
|
||||||
|
if _, ok := m[0x125]; ok {
|
||||||
|
//openId, openKey = readT125(t125)
|
||||||
|
}
|
||||||
|
if t186, ok := m[0x186]; ok {
|
||||||
|
c.decodeT186(t186)
|
||||||
|
}
|
||||||
|
if t11a, ok := m[0x11a]; ok {
|
||||||
|
nick, age, gender = readT11A(t11a)
|
||||||
|
}
|
||||||
|
if _, ok := m[0x199]; ok {
|
||||||
|
//openId, payToken = readT199(t199)
|
||||||
|
}
|
||||||
|
if _, ok := m[0x200]; ok {
|
||||||
|
//pf, pfkey = readT200(t200)
|
||||||
|
}
|
||||||
|
if _, ok := m[0x512]; ok {
|
||||||
|
|
||||||
|
} // 暂不处理, Http api cookie
|
||||||
|
if _, ok := m[0x531]; ok {
|
||||||
|
//a1, noPicSig = readT531(t531)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.sigInfo = &loginSigInfo{
|
||||||
|
loginBitmap: 0,
|
||||||
|
tgt: m[0x10a],
|
||||||
|
tgtKey: m[0x10d],
|
||||||
|
userStKey: m[0x10e],
|
||||||
|
userStWebSig: m[0x103],
|
||||||
|
sKey: m[0x120],
|
||||||
|
d2: m[0x143],
|
||||||
|
d2Key: m[0x305],
|
||||||
|
wtSessionTicketKey: m[0x134],
|
||||||
|
deviceToken: m[0x322],
|
||||||
|
}
|
||||||
|
c.Nickname = nick
|
||||||
|
c.Age = age
|
||||||
|
c.Gender = gender
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) decodeT130(data []byte) {
|
||||||
|
reader := binary.NewReader(data)
|
||||||
|
reader.ReadBytes(2)
|
||||||
|
c.timeDiff = int64(reader.ReadInt32()) - time.Now().Unix()
|
||||||
|
c.t149 = reader.ReadBytes(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) decodeT113(data []byte) {
|
||||||
|
reader := binary.NewReader(data)
|
||||||
|
uin := reader.ReadInt32() // ?
|
||||||
|
fmt.Println("got t113 uin:", uin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QQClient) decodeT186(data []byte) {
|
||||||
|
c.pwdFlag = data[1] == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- tlv readers ---
|
||||||
|
|
||||||
|
func readT125(data []byte) (openId, openKey []byte) {
|
||||||
|
reader := binary.NewReader(data)
|
||||||
|
openId = reader.ReadBytesShort()
|
||||||
|
openKey = reader.ReadBytesShort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readT11A(data []byte) (nick string, age, gender uint16) {
|
||||||
|
reader := binary.NewReader(data)
|
||||||
|
reader.ReadUInt16()
|
||||||
|
age = reader.ReadUInt16()
|
||||||
|
gender = uint16(reader.ReadByte())
|
||||||
|
nick = reader.ReadStringLimit(int(reader.ReadByte()) & 0xff)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readT199(data []byte) (openId, payToken []byte) {
|
||||||
|
reader := binary.NewReader(data)
|
||||||
|
openId = reader.ReadBytesShort()
|
||||||
|
payToken = reader.ReadBytesShort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readT200(data []byte) (pf, pfKey []byte) {
|
||||||
|
reader := binary.NewReader(data)
|
||||||
|
pf = reader.ReadBytesShort()
|
||||||
|
pfKey = reader.ReadBytesShort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readT531(data []byte) (a1, noPicSig []byte) {
|
||||||
|
reader := binary.NewReader(data)
|
||||||
|
m := reader.ReadTlvMap(2)
|
||||||
|
if m.Exists(0x103) && m.Exists(0x16a) && m.Exists(0x113) && m.Exists(0x10c) {
|
||||||
|
a1 = append(m[0x106], m[0x10c]...)
|
||||||
|
noPicSig = m[0x16a]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
8
go.mod
Normal file
8
go.mod
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module github.com/Mrs4s/MiraiGo
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang/protobuf v1.4.1
|
||||||
|
google.golang.org/protobuf v1.25.0
|
||||||
|
)
|
85
go.sum
Normal file
85
go.sum
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/ishbir/elliptic v0.0.0-20150227224012-7fa75501baae h1:p4rT8RR3B5Q6rXTG5v0Tns45zn4wJucqQKF5hzrjtgU=
|
||||||
|
github.com/ishbir/elliptic v0.0.0-20150227224012-7fa75501baae/go.mod h1:JPqapJUq3G3ojsWKJHZNsUK2OJmarnOxFN9afp3/Ovo=
|
||||||
|
github.com/jhump/protoreflect v1.6.1 h1:4/2yi5LyDPP7nN+Hiird1SAJ6YoxUm13/oxHGRnbPd8=
|
||||||
|
github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
227
message/elements.go
Normal file
227
message/elements.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package message
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type TextElement struct {
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageElement struct {
|
||||||
|
Filename string
|
||||||
|
Url string
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupImageElement struct {
|
||||||
|
ImageId string
|
||||||
|
Md5 []byte
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FaceElement struct {
|
||||||
|
Index int32
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewText(s string) *TextElement {
|
||||||
|
return &TextElement{Content: s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNetImage(filename, url string) *ImageElement {
|
||||||
|
return &ImageElement{
|
||||||
|
Filename: filename,
|
||||||
|
Url: url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGroupImage(id string, md5 []byte) *GroupImageElement {
|
||||||
|
return &GroupImageElement{
|
||||||
|
ImageId: id,
|
||||||
|
Md5: md5,
|
||||||
|
Url: "http://gchat.qpic.cn/gchatpic_new/1/0-0-" + strings.ReplaceAll(id[1:36], "-", "") + "/0?term=2",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFace(index int32) *FaceElement {
|
||||||
|
name := faceMap[int(index)]
|
||||||
|
if name == "" {
|
||||||
|
name = "未知表情"
|
||||||
|
}
|
||||||
|
return &FaceElement{
|
||||||
|
Index: index,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TextElement) Type() ElementType {
|
||||||
|
return Text
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ImageElement) Type() ElementType {
|
||||||
|
return Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FaceElement) Type() ElementType {
|
||||||
|
return Face
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *GroupImageElement) Type() ElementType {
|
||||||
|
return Image
|
||||||
|
}
|
||||||
|
|
||||||
|
var faceMap = map[int]string{
|
||||||
|
14: "微笑",
|
||||||
|
1: "撇嘴",
|
||||||
|
2: "色",
|
||||||
|
3: "发呆",
|
||||||
|
4: "得意",
|
||||||
|
5: "流泪",
|
||||||
|
6: "害羞",
|
||||||
|
7: "闭嘴",
|
||||||
|
8: "睡",
|
||||||
|
9: "大哭",
|
||||||
|
10: "尴尬",
|
||||||
|
11: "发怒",
|
||||||
|
12: "调皮",
|
||||||
|
13: "呲牙",
|
||||||
|
0: "惊讶",
|
||||||
|
15: "难过",
|
||||||
|
16: "酷",
|
||||||
|
96: "冷汗",
|
||||||
|
18: "抓狂",
|
||||||
|
19: "吐",
|
||||||
|
20: "偷笑",
|
||||||
|
21: "可爱",
|
||||||
|
22: "白眼",
|
||||||
|
23: "傲慢",
|
||||||
|
24: "饥饿",
|
||||||
|
25: "困",
|
||||||
|
26: "惊恐",
|
||||||
|
27: "流汗",
|
||||||
|
28: "憨笑",
|
||||||
|
29: "大兵",
|
||||||
|
30: "奋斗",
|
||||||
|
31: "咒骂",
|
||||||
|
32: "疑问",
|
||||||
|
33: "嘘",
|
||||||
|
34: "晕",
|
||||||
|
35: "折磨",
|
||||||
|
36: "衰",
|
||||||
|
37: "骷髅",
|
||||||
|
38: "敲打",
|
||||||
|
39: "再见",
|
||||||
|
97: "擦汗",
|
||||||
|
98: "抠鼻",
|
||||||
|
99: "鼓掌",
|
||||||
|
100: "糗大了",
|
||||||
|
101: "坏笑",
|
||||||
|
102: "左哼哼",
|
||||||
|
103: "右哼哼",
|
||||||
|
104: "哈欠",
|
||||||
|
105: "鄙视",
|
||||||
|
106: "委屈",
|
||||||
|
107: "快哭了",
|
||||||
|
108: "阴险",
|
||||||
|
109: "亲亲",
|
||||||
|
110: "吓",
|
||||||
|
111: "可怜",
|
||||||
|
172: "眨眼睛",
|
||||||
|
182: "笑哭",
|
||||||
|
179: "doge",
|
||||||
|
173: "泪奔",
|
||||||
|
174: "无奈",
|
||||||
|
212: "托腮",
|
||||||
|
175: "卖萌",
|
||||||
|
178: "斜眼笑",
|
||||||
|
177: "喷血",
|
||||||
|
180: "惊喜",
|
||||||
|
181: "骚扰",
|
||||||
|
176: "小纠结",
|
||||||
|
183: "我最美",
|
||||||
|
112: "菜刀",
|
||||||
|
89: "西瓜",
|
||||||
|
113: "啤酒",
|
||||||
|
114: "篮球",
|
||||||
|
115: "乒乓",
|
||||||
|
171: "茶",
|
||||||
|
60: "咖啡",
|
||||||
|
61: "饭",
|
||||||
|
46: "猪头",
|
||||||
|
63: "玫瑰",
|
||||||
|
64: "凋谢",
|
||||||
|
116: "示爱",
|
||||||
|
66: "爱心",
|
||||||
|
67: "心碎",
|
||||||
|
53: "蛋糕",
|
||||||
|
54: "闪电",
|
||||||
|
55: "炸弹",
|
||||||
|
56: "刀",
|
||||||
|
57: "足球",
|
||||||
|
117: "瓢虫",
|
||||||
|
59: "便便",
|
||||||
|
75: "月亮",
|
||||||
|
74: "太阳",
|
||||||
|
69: "礼物",
|
||||||
|
49: "拥抱",
|
||||||
|
76: "强",
|
||||||
|
77: "弱",
|
||||||
|
78: "握手",
|
||||||
|
79: "胜利",
|
||||||
|
118: "抱拳",
|
||||||
|
119: "勾引",
|
||||||
|
120: "拳头",
|
||||||
|
121: "差劲",
|
||||||
|
122: "爱你",
|
||||||
|
123: "NO",
|
||||||
|
124: "OK",
|
||||||
|
42: "爱情",
|
||||||
|
85: "飞吻",
|
||||||
|
43: "跳跳",
|
||||||
|
41: "发抖",
|
||||||
|
86: "怄火",
|
||||||
|
125: "转圈",
|
||||||
|
126: "磕头",
|
||||||
|
127: "回头",
|
||||||
|
128: "跳绳",
|
||||||
|
129: "挥手",
|
||||||
|
130: "激动",
|
||||||
|
131: "街舞",
|
||||||
|
132: "献吻",
|
||||||
|
133: "左太极",
|
||||||
|
134: "右太极",
|
||||||
|
136: "双喜",
|
||||||
|
137: "鞭炮",
|
||||||
|
138: "灯笼",
|
||||||
|
140: "K歌",
|
||||||
|
144: "喝彩",
|
||||||
|
145: "祈祷",
|
||||||
|
146: "爆筋",
|
||||||
|
147: "棒棒糖",
|
||||||
|
148: "喝奶",
|
||||||
|
151: "飞机",
|
||||||
|
158: "钞票",
|
||||||
|
168: "药",
|
||||||
|
169: "手枪",
|
||||||
|
188: "蛋",
|
||||||
|
192: "红包",
|
||||||
|
184: "河蟹",
|
||||||
|
185: "羊驼",
|
||||||
|
190: "菊花",
|
||||||
|
187: "幽灵",
|
||||||
|
193: "大笑",
|
||||||
|
194: "不开心",
|
||||||
|
197: "冷漠",
|
||||||
|
198: "呃",
|
||||||
|
199: "好棒",
|
||||||
|
200: "拜托",
|
||||||
|
201: "点赞",
|
||||||
|
202: "无聊",
|
||||||
|
203: "托脸",
|
||||||
|
204: "吃",
|
||||||
|
205: "送花",
|
||||||
|
206: "害怕",
|
||||||
|
207: "花痴",
|
||||||
|
208: "小样儿",
|
||||||
|
210: "飙泪",
|
||||||
|
211: "我不看",
|
||||||
|
}
|
116
message/message.go
Normal file
116
message/message.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"github.com/Mrs4s/MiraiGo/client/pb/msg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PrivateMessage struct {
|
||||||
|
Id int32
|
||||||
|
Sender *Sender
|
||||||
|
Elements []IMessageElement
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupMessage struct {
|
||||||
|
Id int32
|
||||||
|
GroupUin int64
|
||||||
|
GroupName string
|
||||||
|
Sender *Sender
|
||||||
|
Elements []IMessageElement
|
||||||
|
IsAdministrator bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendingMessage struct {
|
||||||
|
Elements []IMessageElement
|
||||||
|
}
|
||||||
|
|
||||||
|
type Sender struct {
|
||||||
|
Uin int64
|
||||||
|
Nickname string
|
||||||
|
CardName string
|
||||||
|
IsFriend bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type IMessageElement interface {
|
||||||
|
Type() ElementType
|
||||||
|
}
|
||||||
|
|
||||||
|
type ElementType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Text ElementType = iota
|
||||||
|
Image
|
||||||
|
Face
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Sender) IsAnonymous() bool {
|
||||||
|
return s.Uin == 80000000
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PrivateMessage) ToString() (res string) {
|
||||||
|
for _, elem := range msg.Elements {
|
||||||
|
switch e := elem.(type) {
|
||||||
|
case *TextElement:
|
||||||
|
res += e.Content
|
||||||
|
case *ImageElement:
|
||||||
|
res += " [Image= " + e.Filename + " ] "
|
||||||
|
case *FaceElement:
|
||||||
|
res += " [" + e.Name + "] "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *GroupMessage) ToString() (res string) {
|
||||||
|
for _, elem := range msg.Elements {
|
||||||
|
switch e := elem.(type) {
|
||||||
|
case *TextElement:
|
||||||
|
res += e.Content
|
||||||
|
case *ImageElement:
|
||||||
|
res += " [Image= " + e.Filename + " ] "
|
||||||
|
case *FaceElement:
|
||||||
|
res += " [" + e.Name + "] "
|
||||||
|
case *GroupImageElement:
|
||||||
|
res += "[Image= " + e.ImageId + " ]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SendingMessage) Append(e IMessageElement) *SendingMessage {
|
||||||
|
msg.Elements = append(msg.Elements, e)
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToProtoElems(elems []IMessageElement) (r []*msg.Elem) {
|
||||||
|
for _, elem := range elems {
|
||||||
|
switch e := elem.(type) {
|
||||||
|
case *TextElement:
|
||||||
|
r = append(r, &msg.Elem{
|
||||||
|
Text: &msg.Text{
|
||||||
|
Str: e.Content,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case *FaceElement:
|
||||||
|
r = append(r, &msg.Elem{
|
||||||
|
Face: &msg.Face{
|
||||||
|
Index: e.Index,
|
||||||
|
Old: binary.ToBytes(int16(0x1445 - 4 + e.Index)),
|
||||||
|
Buf: []byte{0x00, 0x01, 0x00, 0x04, 0x52, 0xCC, 0xF5, 0xD0},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case *GroupImageElement:
|
||||||
|
r = append(r, &msg.Elem{
|
||||||
|
CustomFace: &msg.CustomFace{
|
||||||
|
FilePath: e.ImageId,
|
||||||
|
Md5: e.Md5[:],
|
||||||
|
Flag: make([]byte, 4),
|
||||||
|
OldData: []byte{0x15, 0x36, 0x20, 0x39, 0x32, 0x6B, 0x41, 0x31, 0x00, 0x38, 0x37, 0x32, 0x66, 0x30, 0x36, 0x36, 0x30, 0x33, 0x61, 0x65, 0x31, 0x30, 0x33, 0x62, 0x37, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
|
0x20, 0x35, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7B, 0x30, 0x31, 0x45, 0x39, 0x34, 0x35, 0x31, 0x42, 0x2D, 0x37, 0x30, 0x45, 0x44,
|
||||||
|
0x2D, 0x45, 0x41, 0x45, 0x33, 0x2D, 0x42, 0x33, 0x37, 0x43, 0x2D, 0x31, 0x30, 0x31, 0x46, 0x31, 0x45, 0x45, 0x42, 0x46, 0x35, 0x42, 0x35, 0x7D, 0x2E, 0x70, 0x6E, 0x67, 0x41},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
35
protocol/crypto/crypto.go
Normal file
35
protocol/crypto/crypto.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EncryptECDH struct {
|
||||||
|
InitialShareKey []byte
|
||||||
|
PublicKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var ECDH = &EncryptECDH{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
//TODO: Keygen
|
||||||
|
ECDH.InitialShareKey, _ = hex.DecodeString("41d0d17c506a5256d0d08d7aac133c70")
|
||||||
|
ECDH.PublicKey, _ = hex.DecodeString("049fb03421ba7ab5fc91c2d94a7657fff7ba8fe09f08a22951a24865212cbc45aff1b5125188fa8f0e30473bc55d54edc2")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EncryptECDH) DoEncrypt(d, k []byte) []byte {
|
||||||
|
w := binary.NewWriter()
|
||||||
|
w.WriteByte(0x01)
|
||||||
|
w.WriteByte(0x01)
|
||||||
|
w.Write(k)
|
||||||
|
w.WriteUInt16(258)
|
||||||
|
w.WriteUInt16(uint16(len(ECDH.PublicKey)))
|
||||||
|
w.Write(ECDH.PublicKey)
|
||||||
|
w.EncryptAndWrite(ECDH.InitialShareKey, d)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EncryptECDH) Id() byte {
|
||||||
|
return 7
|
||||||
|
}
|
1
protocol/crypto/ecdh.go
Normal file
1
protocol/crypto/ecdh.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package crypto
|
233
protocol/crypto/secp192k1.go
Normal file
233
protocol/crypto/secp192k1.go
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A BitCurve represents a Koblitz Curve with a=0.
|
||||||
|
// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html
|
||||||
|
type BitCurve struct {
|
||||||
|
P *big.Int // the order of the underlying field
|
||||||
|
N *big.Int // the order of the base point
|
||||||
|
B *big.Int // the constant of the BitCurve equation
|
||||||
|
Gx, Gy *big.Int // (x,y) of the base point
|
||||||
|
BitSize int // the size of the underlying field
|
||||||
|
}
|
||||||
|
|
||||||
|
// See FIPS 186-3, section D.2.2.1
|
||||||
|
// And http://www.secg.org/sec2-v2.pdf section 2.2.1
|
||||||
|
var secp192k1 = &BitCurve{
|
||||||
|
P: new(big.Int).SetBytes([]byte{
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xEE, 0x37,
|
||||||
|
}), N: new(big.Int).SetBytes([]byte{
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
|
||||||
|
0x26, 0xF2, 0xFC, 0x17, 0x0F, 0x69, 0x46, 0x6A, 0x74, 0xDE, 0xFD, 0x8D,
|
||||||
|
}), B: new(big.Int).SetBytes([]byte{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||||
|
}), Gx: new(big.Int).SetBytes([]byte{
|
||||||
|
0xDB, 0x4F, 0xF1, 0x0E, 0xC0, 0x57, 0xE9, 0xAE, 0x26, 0xB0, 0x7D, 0x02,
|
||||||
|
0x80, 0xB7, 0xF4, 0x34, 0x1D, 0xA5, 0xD1, 0xB1, 0xEA, 0xE0, 0x6C, 0x7D,
|
||||||
|
}), Gy: new(big.Int).SetBytes([]byte{
|
||||||
|
0x9B, 0x2F, 0x2F, 0x6D, 0x9C, 0x56, 0x28, 0xA7, 0x84, 0x41, 0x63, 0xD0,
|
||||||
|
0x15, 0xBE, 0x86, 0x34, 0x40, 0x82, 0xAA, 0x88, 0xD9, 0x5E, 0x2F, 0x9D,
|
||||||
|
}),
|
||||||
|
BitSize: 192,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
|
||||||
|
zinv := new(big.Int).ModInverse(z, BitCurve.P)
|
||||||
|
zinvsq := new(big.Int).Mul(zinv, zinv)
|
||||||
|
|
||||||
|
xOut = new(big.Int).Mul(x, zinvsq)
|
||||||
|
xOut.Mod(xOut, BitCurve.P)
|
||||||
|
zinvsq.Mul(zinvsq, zinv)
|
||||||
|
yOut = new(big.Int).Mul(y, zinvsq)
|
||||||
|
yOut.Mod(yOut, BitCurve.P)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
|
||||||
|
// (x2, y2, z2) and returns their sum, also in Jacobian form.
|
||||||
|
func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
|
||||||
|
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
|
||||||
|
z1z1 := new(big.Int).Mul(z1, z1)
|
||||||
|
z1z1.Mod(z1z1, BitCurve.P)
|
||||||
|
z2z2 := new(big.Int).Mul(z2, z2)
|
||||||
|
z2z2.Mod(z2z2, BitCurve.P)
|
||||||
|
|
||||||
|
u1 := new(big.Int).Mul(x1, z2z2)
|
||||||
|
u1.Mod(u1, BitCurve.P)
|
||||||
|
u2 := new(big.Int).Mul(x2, z1z1)
|
||||||
|
u2.Mod(u2, BitCurve.P)
|
||||||
|
h := new(big.Int).Sub(u2, u1)
|
||||||
|
if h.Sign() == -1 {
|
||||||
|
h.Add(h, BitCurve.P)
|
||||||
|
}
|
||||||
|
i := new(big.Int).Lsh(h, 1)
|
||||||
|
i.Mul(i, i)
|
||||||
|
j := new(big.Int).Mul(h, i)
|
||||||
|
|
||||||
|
s1 := new(big.Int).Mul(y1, z2)
|
||||||
|
s1.Mul(s1, z2z2)
|
||||||
|
s1.Mod(s1, BitCurve.P)
|
||||||
|
s2 := new(big.Int).Mul(y2, z1)
|
||||||
|
s2.Mul(s2, z1z1)
|
||||||
|
s2.Mod(s2, BitCurve.P)
|
||||||
|
r := new(big.Int).Sub(s2, s1)
|
||||||
|
if r.Sign() == -1 {
|
||||||
|
r.Add(r, BitCurve.P)
|
||||||
|
}
|
||||||
|
r.Lsh(r, 1)
|
||||||
|
v := new(big.Int).Mul(u1, i)
|
||||||
|
|
||||||
|
x3 := new(big.Int).Set(r)
|
||||||
|
x3.Mul(x3, x3)
|
||||||
|
x3.Sub(x3, j)
|
||||||
|
x3.Sub(x3, v)
|
||||||
|
x3.Sub(x3, v)
|
||||||
|
x3.Mod(x3, BitCurve.P)
|
||||||
|
|
||||||
|
y3 := new(big.Int).Set(r)
|
||||||
|
v.Sub(v, x3)
|
||||||
|
y3.Mul(y3, v)
|
||||||
|
s1.Mul(s1, j)
|
||||||
|
s1.Lsh(s1, 1)
|
||||||
|
y3.Sub(y3, s1)
|
||||||
|
y3.Mod(y3, BitCurve.P)
|
||||||
|
|
||||||
|
z3 := new(big.Int).Add(z1, z2)
|
||||||
|
z3.Mul(z3, z3)
|
||||||
|
z3.Sub(z3, z1z1)
|
||||||
|
if z3.Sign() == -1 {
|
||||||
|
z3.Add(z3, BitCurve.P)
|
||||||
|
}
|
||||||
|
z3.Sub(z3, z2z2)
|
||||||
|
if z3.Sign() == -1 {
|
||||||
|
z3.Add(z3, BitCurve.P)
|
||||||
|
}
|
||||||
|
z3.Mul(z3, h)
|
||||||
|
z3.Mod(z3, BitCurve.P)
|
||||||
|
|
||||||
|
return x3, y3, z3
|
||||||
|
}
|
||||||
|
|
||||||
|
// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
|
||||||
|
// returns its double, also in Jacobian form.
|
||||||
|
func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
|
||||||
|
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
||||||
|
|
||||||
|
a := new(big.Int).Mul(x, x) //X1²
|
||||||
|
b := new(big.Int).Mul(y, y) //Y1²
|
||||||
|
c := new(big.Int).Mul(b, b) //B²
|
||||||
|
|
||||||
|
d := new(big.Int).Add(x, b) //X1+B
|
||||||
|
d.Mul(d, d) //(X1+B)²
|
||||||
|
d.Sub(d, a) //(X1+B)²-A
|
||||||
|
d.Sub(d, c) //(X1+B)²-A-C
|
||||||
|
d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C)
|
||||||
|
|
||||||
|
e := new(big.Int).Mul(big.NewInt(3), a) //3*A
|
||||||
|
f := new(big.Int).Mul(e, e) //E²
|
||||||
|
|
||||||
|
x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
|
||||||
|
x3.Sub(f, x3) //F-2*D
|
||||||
|
x3.Mod(x3, BitCurve.P)
|
||||||
|
|
||||||
|
y3 := new(big.Int).Sub(d, x3) //D-X3
|
||||||
|
y3.Mul(e, y3) //E*(D-X3)
|
||||||
|
y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
|
||||||
|
y3.Mod(y3, BitCurve.P)
|
||||||
|
|
||||||
|
z3 := new(big.Int).Mul(y, z) //Y1*Z1
|
||||||
|
z3.Mul(big.NewInt(2), z3) //3*Y1*Z1
|
||||||
|
z3.Mod(z3, BitCurve.P)
|
||||||
|
|
||||||
|
return x3, y3, z3
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: double check if it is okay
|
||||||
|
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
|
||||||
|
func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
|
||||||
|
// We have a slight problem in that the identity of the group (the
|
||||||
|
// point at infinity) cannot be represented in (x, y) form on a finite
|
||||||
|
// machine. Thus the standard add/double algorithm has to be tweaked
|
||||||
|
// slightly: our initial state is not the identity, but x, and we
|
||||||
|
// ignore the first true bit in |k|. If we don't find any true bits in
|
||||||
|
// |k|, then we return nil, nil, because we cannot return the identity
|
||||||
|
// element.
|
||||||
|
|
||||||
|
Bz := new(big.Int).SetInt64(1)
|
||||||
|
x := Bx
|
||||||
|
y := By
|
||||||
|
z := Bz
|
||||||
|
|
||||||
|
seenFirstTrue := false
|
||||||
|
for _, byte := range k {
|
||||||
|
for bitNum := 0; bitNum < 8; bitNum++ {
|
||||||
|
if seenFirstTrue {
|
||||||
|
x, y, z = BitCurve.doubleJacobian(x, y, z)
|
||||||
|
}
|
||||||
|
if byte&0x80 == 0x80 {
|
||||||
|
if !seenFirstTrue {
|
||||||
|
seenFirstTrue = true
|
||||||
|
} else {
|
||||||
|
x, y, z = BitCurve.addJacobian(Bx, By, Bz, x, y, z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte <<= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !seenFirstTrue {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return BitCurve.affineFromJacobian(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarBaseMult returns k*G, where G is the base point of the group and k is
|
||||||
|
// an integer in big-endian form.
|
||||||
|
func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
||||||
|
return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
|
||||||
|
|
||||||
|
//TODO: double check if it is okay
|
||||||
|
// GenerateKey returns a public/private key pair. The private key is generated
|
||||||
|
// using the given reader, which must return random data.
|
||||||
|
func (BitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) {
|
||||||
|
byteLen := (BitCurve.BitSize + 7) >> 3
|
||||||
|
priv = make([]byte, byteLen)
|
||||||
|
|
||||||
|
for x == nil {
|
||||||
|
_, err = io.ReadFull(rand, priv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// We have to mask off any excess bits in the case that the size of the
|
||||||
|
// underlying field is not a whole number of bytes.
|
||||||
|
priv[0] &= mask[BitCurve.BitSize%8]
|
||||||
|
// This is because, in tests, rand will return all zeros and we don't
|
||||||
|
// want to get the point at infinity and loop forever.
|
||||||
|
priv[1] ^= 0x42
|
||||||
|
x, y = BitCurve.ScalarBaseMult(priv)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
$ openssl asn1parse -in 1.cer -inform DER -dump
|
||||||
|
0:d=0 hl=2 l= 70 cons: SEQUENCE
|
||||||
|
2:d=1 hl=2 l= 16 cons: SEQUENCE
|
||||||
|
4:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
|
||||||
|
13:d=2 hl=2 l= 5 prim: OBJECT :secp192k1
|
||||||
|
20:d=1 hl=2 l= 50 prim: BIT STRING
|
||||||
|
0000 - 00 04 92 8d 88 50 67 30-88 b3 43 26 4e 0c 6b ac .....Pg0..C&N.k.
|
||||||
|
0010 - b8 49 6d 69 77 99 f3 72-11 de b2 5b b7 39 06 cb .Imiw..r...[.9..
|
||||||
|
0020 - 08 9f ea 96 39 b4 e0 26-04 98 b5 1a 99 2d 50 81 ....9..&.....-P.
|
||||||
|
0030 - 3d a8 =.
|
||||||
|
*/
|
40
protocol/packets/builders.go
Normal file
40
protocol/packets/builders.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package packets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildLoginPacket(uin int64, bodyType byte, key, body, extraData []byte) []byte {
|
||||||
|
w := binary.NewWriter()
|
||||||
|
w.WriteIntLvPacket(4, func(w *binary.Writer) {
|
||||||
|
w.WriteUInt32(0x00_00_00_0A)
|
||||||
|
w.WriteByte(bodyType)
|
||||||
|
w.WriteIntLvPacket(4, func(w *binary.Writer) {
|
||||||
|
w.Write(extraData)
|
||||||
|
})
|
||||||
|
w.WriteByte(0x00)
|
||||||
|
w.WriteString(strconv.FormatInt(uin, 10))
|
||||||
|
if len(key) == 0 {
|
||||||
|
w.Write(body)
|
||||||
|
} else {
|
||||||
|
w.EncryptAndWrite(key, body)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildUniPacket(uin int64, seq uint16, commandName string, bodyType byte, sessionId, extraData, key, body []byte) []byte {
|
||||||
|
w := binary.NewWriter()
|
||||||
|
w.WriteIntLvPacket(4, func(w *binary.Writer) {
|
||||||
|
w.WriteUInt32(0x0B)
|
||||||
|
w.WriteByte(bodyType)
|
||||||
|
w.WriteUInt32(uint32(seq))
|
||||||
|
w.WriteByte(0)
|
||||||
|
w.WriteString(strconv.FormatInt(uin, 10))
|
||||||
|
w.EncryptAndWrite(key, binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUniPacket(commandName, sessionId, extraData, body)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
195
protocol/packets/global.go
Normal file
195
protocol/packets/global.go
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
package packets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"github.com/Mrs4s/MiraiGo/protocol/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrUnknownFlag = errors.New("unknown flag")
|
||||||
|
var ErrDecryptFailed = errors.New("decrypt failed")
|
||||||
|
|
||||||
|
type ISendingPacket interface {
|
||||||
|
CommandId() uint16
|
||||||
|
Writer() *binary.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
type IncomingPacket struct {
|
||||||
|
SequenceId uint16
|
||||||
|
Flag2 byte
|
||||||
|
CommandName string
|
||||||
|
SessionId []byte
|
||||||
|
Payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type IEncryptMethod interface {
|
||||||
|
DoEncrypt([]byte, []byte) []byte
|
||||||
|
Id() byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildOicqRequestPacket(uin int64, commandId uint16, encrypt IEncryptMethod, key []byte, bodyFunc func(writer *binary.Writer)) []byte {
|
||||||
|
b := binary.NewWriter()
|
||||||
|
bodyFunc(b)
|
||||||
|
|
||||||
|
body := encrypt.DoEncrypt(b.Bytes(), key)
|
||||||
|
p := binary.NewWriter()
|
||||||
|
p.WriteByte(0x02)
|
||||||
|
p.WriteUInt16(27 + 2 + uint16(len(body)))
|
||||||
|
p.WriteUInt16(8001)
|
||||||
|
p.WriteUInt16(commandId)
|
||||||
|
p.WriteUInt16(1)
|
||||||
|
p.WriteUInt32(uint32(uin))
|
||||||
|
p.WriteByte(3)
|
||||||
|
p.WriteByte(encrypt.Id())
|
||||||
|
p.WriteByte(0)
|
||||||
|
p.WriteUInt32(2)
|
||||||
|
p.WriteUInt32(0)
|
||||||
|
p.WriteUInt32(0)
|
||||||
|
p.Write(body)
|
||||||
|
p.WriteByte(0x03)
|
||||||
|
return p.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildSsoPacket(seq uint16, commandName, imei string, extData, outPacketSessionId, body, ksid []byte) []byte {
|
||||||
|
p := binary.NewWriter()
|
||||||
|
p.WriteIntLvPacket(4, func(writer *binary.Writer) {
|
||||||
|
writer.WriteUInt32(uint32(seq))
|
||||||
|
writer.WriteUInt32(537062409) // Android pad (sub app id)
|
||||||
|
writer.WriteUInt32(537062409)
|
||||||
|
writer.Write([]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00})
|
||||||
|
if len(extData) == 0 || len(extData) == 4 {
|
||||||
|
writer.WriteUInt32(0x04)
|
||||||
|
} else {
|
||||||
|
writer.WriteUInt32(uint32(len(extData) + 4))
|
||||||
|
writer.Write(extData)
|
||||||
|
}
|
||||||
|
writer.WriteString(commandName)
|
||||||
|
writer.WriteUInt32(0x08)
|
||||||
|
writer.Write(outPacketSessionId)
|
||||||
|
writer.WriteString(imei)
|
||||||
|
writer.WriteUInt32(0x04)
|
||||||
|
{
|
||||||
|
writer.WriteUInt16(uint16(len(ksid)) + 2)
|
||||||
|
writer.Write(ksid)
|
||||||
|
}
|
||||||
|
writer.WriteUInt32(0x04)
|
||||||
|
})
|
||||||
|
|
||||||
|
p.WriteIntLvPacket(4, func(writer *binary.Writer) {
|
||||||
|
writer.Write(body)
|
||||||
|
})
|
||||||
|
return p.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseIncomingPacket(payload, d2key []byte) (*IncomingPacket, error) {
|
||||||
|
reader := binary.NewReader(payload)
|
||||||
|
flag1 := reader.ReadInt32()
|
||||||
|
flag2 := reader.ReadByte()
|
||||||
|
if reader.ReadByte() != 0 { // flag3
|
||||||
|
return nil, ErrUnknownFlag
|
||||||
|
}
|
||||||
|
reader.ReadString() // uin string
|
||||||
|
decrypted := func() (data []byte) {
|
||||||
|
defer func() {
|
||||||
|
if pan := recover(); pan != nil {
|
||||||
|
// TODO: bot.client.tryDecryptOrNull
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
switch flag2 {
|
||||||
|
case 0:
|
||||||
|
return reader.ReadAvailable()
|
||||||
|
case 1:
|
||||||
|
d2 := binary.NewTeaCipher(d2key)
|
||||||
|
return d2.Decrypt(reader.ReadAvailable())
|
||||||
|
case 2:
|
||||||
|
z16 := binary.NewTeaCipher(make([]byte, 16))
|
||||||
|
return z16.Decrypt(reader.ReadAvailable())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if len(decrypted) == 0 {
|
||||||
|
return nil, ErrDecryptFailed
|
||||||
|
}
|
||||||
|
if flag1 != 0x0A && flag1 != 0x0B {
|
||||||
|
return nil, ErrDecryptFailed
|
||||||
|
}
|
||||||
|
return parseSsoFrame(decrypted, flag2), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSsoFrame(payload []byte, flag2 byte) *IncomingPacket {
|
||||||
|
reader := binary.NewReader(payload)
|
||||||
|
reader.ReadInt32() // packet len
|
||||||
|
seqId := reader.ReadInt32()
|
||||||
|
reader.ReadInt32() // return code
|
||||||
|
reader.ReadBytes(int(reader.ReadInt32()) - 4) // extra data
|
||||||
|
commandName := reader.ReadString()
|
||||||
|
sessionId := reader.ReadBytes(int(reader.ReadInt32()) - 4)
|
||||||
|
if commandName == "Heartbeat.Alive" {
|
||||||
|
return &IncomingPacket{
|
||||||
|
SequenceId: uint16(seqId),
|
||||||
|
Flag2: flag2,
|
||||||
|
CommandName: commandName,
|
||||||
|
SessionId: sessionId,
|
||||||
|
Payload: []byte{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compressedFlag := reader.ReadInt32()
|
||||||
|
packet := func() []byte {
|
||||||
|
if compressedFlag == 0 {
|
||||||
|
pktSize := uint64(reader.ReadInt32()) & 0xffffffff
|
||||||
|
if pktSize == uint64(reader.Len()) || pktSize == uint64(reader.Len()+4) {
|
||||||
|
return reader.ReadAvailable()
|
||||||
|
} else {
|
||||||
|
return reader.ReadAvailable() // some logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if compressedFlag == 1 {
|
||||||
|
reader.ReadBytes(4)
|
||||||
|
return binary.ZlibUncompress(reader.ReadAvailable()) // ?
|
||||||
|
}
|
||||||
|
if compressedFlag == 8 {
|
||||||
|
return reader.ReadAvailable()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
return &IncomingPacket{
|
||||||
|
SequenceId: uint16(seqId),
|
||||||
|
Flag2: flag2,
|
||||||
|
CommandName: commandName,
|
||||||
|
SessionId: sessionId,
|
||||||
|
Payload: packet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pkt *IncomingPacket) DecryptPayload(random []byte) ([]byte, error) {
|
||||||
|
reader := binary.NewReader(pkt.Payload)
|
||||||
|
if reader.ReadByte() != 2 {
|
||||||
|
return nil, ErrUnknownFlag
|
||||||
|
}
|
||||||
|
reader.ReadBytes(2)
|
||||||
|
reader.ReadBytes(2)
|
||||||
|
reader.ReadUInt16()
|
||||||
|
reader.ReadUInt16()
|
||||||
|
reader.ReadInt32()
|
||||||
|
encryptType := reader.ReadUInt16()
|
||||||
|
reader.ReadByte()
|
||||||
|
if encryptType == 0 {
|
||||||
|
data := func() (decrypted []byte) {
|
||||||
|
d := reader.ReadBytes(reader.Len() - 1)
|
||||||
|
defer func() {
|
||||||
|
if pan := recover(); pan != nil {
|
||||||
|
tea := binary.NewTeaCipher(random)
|
||||||
|
decrypted = tea.Decrypt(d)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
tea := binary.NewTeaCipher(crypto.ECDH.InitialShareKey)
|
||||||
|
decrypted = tea.Decrypt(d)
|
||||||
|
return
|
||||||
|
}()
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
if encryptType == 4 {
|
||||||
|
panic("todo")
|
||||||
|
}
|
||||||
|
return nil, ErrUnknownFlag
|
||||||
|
}
|
1
protocol/protocol_global.go
Normal file
1
protocol/protocol_global.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package protocol
|
24
protocol/tlv/t1.go
Normal file
24
protocol/tlv/t1.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func T1(uin uint32, ip []byte) []byte {
|
||||||
|
if len(ip) != 4 {
|
||||||
|
panic("invalid ip")
|
||||||
|
}
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x01)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(1)
|
||||||
|
w.WriteUInt32(rand.Uint32())
|
||||||
|
w.WriteUInt32(uin)
|
||||||
|
w.WriteUInt32(uint32(time.Now().UnixNano() / 1e6))
|
||||||
|
w.Write(ip)
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
17
protocol/tlv/t100.go
Normal file
17
protocol/tlv/t100.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T100() []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x100)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(1)
|
||||||
|
w.WriteUInt32(5)
|
||||||
|
w.WriteUInt32(16)
|
||||||
|
w.WriteUInt32(537062409) // Sub app id
|
||||||
|
w.WriteUInt32(0) // App client version
|
||||||
|
w.WriteUInt32(34869472)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
10
protocol/tlv/t104.go
Normal file
10
protocol/tlv/t104.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T104(data []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x104)
|
||||||
|
w.WriteTlv(data)
|
||||||
|
})
|
||||||
|
}
|
57
protocol/tlv/t106.go
Normal file
57
protocol/tlv/t106.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
binary2 "encoding/binary"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func T106(uin, salt uint32, passwordMd5 [16]byte, guidAvailable bool, guid, tgtgtKey []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x106)
|
||||||
|
body := binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(4)
|
||||||
|
w.WriteUInt32(rand.Uint32())
|
||||||
|
w.WriteUInt32(5)
|
||||||
|
w.WriteUInt32(16) // appId
|
||||||
|
w.WriteUInt32(0) // app client version
|
||||||
|
if uin == 0 {
|
||||||
|
w.WriteUInt64(uint64(salt))
|
||||||
|
} else {
|
||||||
|
w.WriteUInt64(uint64(uin))
|
||||||
|
}
|
||||||
|
w.WriteUInt32(uint32(time.Now().UnixNano() / 1e6))
|
||||||
|
w.Write([]byte{0x00, 0x00, 0x00, 0x00}) // fake ip
|
||||||
|
w.WriteByte(0x01)
|
||||||
|
w.Write(passwordMd5[:])
|
||||||
|
w.Write(tgtgtKey)
|
||||||
|
w.WriteUInt32(0)
|
||||||
|
w.WriteBool(guidAvailable)
|
||||||
|
if len(guid) == 0 {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
w.WriteUInt32(rand.Uint32())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.Write(guid)
|
||||||
|
}
|
||||||
|
w.WriteUInt32(537062409) // sub app id (android pad)
|
||||||
|
w.WriteUInt32(1) // password login
|
||||||
|
b := make([]byte, 8)
|
||||||
|
binary2.BigEndian.PutUint64(b, uint64(uin))
|
||||||
|
w.WriteTlv(b)
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
})
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
if salt != 0 {
|
||||||
|
binary2.BigEndian.PutUint32(b, salt)
|
||||||
|
} else {
|
||||||
|
binary2.BigEndian.PutUint32(b, uin)
|
||||||
|
}
|
||||||
|
key := md5.Sum(append(append(passwordMd5[:], []byte{0x00, 0x00, 0x00, 0x00}...), b...))
|
||||||
|
w.EncryptAndWrite(key[:], body)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
15
protocol/tlv/t107.go
Normal file
15
protocol/tlv/t107.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T107(picType uint16) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x107)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(picType)
|
||||||
|
w.WriteByte(0x00)
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
w.WriteByte(0x01)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
16
protocol/tlv/t109.go
Normal file
16
protocol/tlv/t109.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
func T109(androidId []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x109)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
h := md5.Sum(androidId)
|
||||||
|
w.Write(h[:])
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
16
protocol/tlv/t116.go
Normal file
16
protocol/tlv/t116.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T116(miscBitmap, subSigMap uint32) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x116)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteByte(0x00)
|
||||||
|
w.WriteUInt32(miscBitmap)
|
||||||
|
w.WriteUInt32(subSigMap)
|
||||||
|
w.WriteByte(0x01)
|
||||||
|
w.WriteUInt32(1600000226) // app id list
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
17
protocol/tlv/t124.go
Normal file
17
protocol/tlv/t124.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T124(osType, osVersion, simInfo, apn []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x124)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteTlvLimitedSize(osType, 16)
|
||||||
|
w.WriteTlvLimitedSize(osVersion, 16)
|
||||||
|
w.WriteUInt16(2) // Network type wifi
|
||||||
|
w.WriteTlvLimitedSize(simInfo, 16)
|
||||||
|
w.WriteTlvLimitedSize([]byte{}, 16)
|
||||||
|
w.WriteTlvLimitedSize(apn, 16)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
19
protocol/tlv/t128.go
Normal file
19
protocol/tlv/t128.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T128(isGuidFromFileNull, isGuidAvailable, isGuidChanged bool, guidFlag uint32, buildModel, guid, buildBrand []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x128)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
w.WriteBool(isGuidFromFileNull)
|
||||||
|
w.WriteBool(isGuidAvailable)
|
||||||
|
w.WriteBool(isGuidChanged)
|
||||||
|
w.WriteUInt32(guidFlag)
|
||||||
|
w.WriteTlvLimitedSize(buildModel, 32)
|
||||||
|
w.WriteTlvLimitedSize(guid, 16)
|
||||||
|
w.WriteTlvLimitedSize(buildBrand, 16)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
15
protocol/tlv/t141.go
Normal file
15
protocol/tlv/t141.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T141(simInfo, apn []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x141)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(1)
|
||||||
|
w.WriteTlv(simInfo)
|
||||||
|
w.WriteUInt16(2) // network type wifi
|
||||||
|
w.WriteTlv(apn)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
13
protocol/tlv/t142.go
Normal file
13
protocol/tlv/t142.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T142(apkId string) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x142)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
w.WriteTlvLimitedSize([]byte(apkId), 32)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
24
protocol/tlv/t144.go
Normal file
24
protocol/tlv/t144.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T144(
|
||||||
|
androidId, devInfo, osType, osVersion, simInfo, apn []byte,
|
||||||
|
isGuidFromFileNull, isGuidAvailable, isGuidChanged bool,
|
||||||
|
guidFlag uint32,
|
||||||
|
buildModel, guid, buildBrand, tgtgtKey []byte,
|
||||||
|
) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x144)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.EncryptAndWrite(tgtgtKey, binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(5)
|
||||||
|
w.Write(T109(androidId))
|
||||||
|
w.Write(T52D(devInfo))
|
||||||
|
w.Write(T124(osType, osVersion, simInfo, apn))
|
||||||
|
w.Write(T128(isGuidFromFileNull, isGuidAvailable, isGuidChanged, guidFlag, buildModel, guid, buildBrand))
|
||||||
|
w.Write(T16E(buildModel))
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t145.go
Normal file
12
protocol/tlv/t145.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T145(guid []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x145)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.Write(guid)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
14
protocol/tlv/t147.go
Normal file
14
protocol/tlv/t147.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T147(appId uint32, apkVersionName, apkSignatureMd5 []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x147)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt32(appId)
|
||||||
|
w.WriteTlvLimitedSize(apkVersionName, 32)
|
||||||
|
w.WriteTlvLimitedSize(apkSignatureMd5, 32)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t154.go
Normal file
12
protocol/tlv/t154.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T154(seq uint16) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x154)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt32(uint32(seq))
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t166.go
Normal file
12
protocol/tlv/t166.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T166(imageType byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x166)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteByte(imageType)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t16e.go
Normal file
12
protocol/tlv/t16e.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T16E(buildModel []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x16e)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.Write(buildModel)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
14
protocol/tlv/t177.go
Normal file
14
protocol/tlv/t177.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T177() []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x177)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteByte(0x01)
|
||||||
|
w.WriteUInt32(1571193922)
|
||||||
|
w.WriteTlv([]byte("6.0.0.2413"))
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
18
protocol/tlv/t18.go
Normal file
18
protocol/tlv/t18.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T18(appId uint32, uin uint32) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x18)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(1)
|
||||||
|
w.WriteUInt32(1536)
|
||||||
|
w.WriteUInt32(appId)
|
||||||
|
w.WriteUInt32(0)
|
||||||
|
w.WriteUInt32(uin)
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
16
protocol/tlv/t187.go
Normal file
16
protocol/tlv/t187.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
func T187(macAddress []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x187)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
h := md5.Sum(macAddress)
|
||||||
|
w.Write(h[:])
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
16
protocol/tlv/t188.go
Normal file
16
protocol/tlv/t188.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
func T188(androidId []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x188)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
h := md5.Sum(androidId)
|
||||||
|
w.Write(h[:])
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t191.go
Normal file
12
protocol/tlv/t191.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T191(k byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x191)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteByte(k)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t194.go
Normal file
12
protocol/tlv/t194.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T194(imsiMd5 []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x194)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.Write(imsiMd5)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
14
protocol/tlv/t2.go
Normal file
14
protocol/tlv/t2.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T2(result string, sign []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x02)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
w.WriteStringShort(result)
|
||||||
|
w.WriteTlv(sign)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
13
protocol/tlv/t202.go
Normal file
13
protocol/tlv/t202.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T202(wifiBSSID, wifiSSID []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x202)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteTlvLimitedSize(wifiBSSID, 16)
|
||||||
|
w.WriteTlvLimitedSize(wifiSSID, 32)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
52
protocol/tlv/t511.go
Normal file
52
protocol/tlv/t511.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func T511(domains []string) []byte {
|
||||||
|
var arr2 []string
|
||||||
|
for _, d := range domains {
|
||||||
|
if d != "" {
|
||||||
|
arr2 = append(arr2, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x511)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(uint16(len(arr2)))
|
||||||
|
for _, d := range arr2 {
|
||||||
|
indexOf := strings.Index(d, "(")
|
||||||
|
indexOf2 := strings.Index(d, ")")
|
||||||
|
if indexOf != 0 || indexOf2 <= 0 {
|
||||||
|
w.WriteByte(0x01)
|
||||||
|
w.WriteTlv([]byte(d))
|
||||||
|
} else {
|
||||||
|
var b byte
|
||||||
|
var z bool
|
||||||
|
i, err := strconv.Atoi(d[indexOf+1 : indexOf2])
|
||||||
|
if err == nil {
|
||||||
|
z2 := (1048576 & i) > 0
|
||||||
|
if (i & 134217728) > 0 {
|
||||||
|
z = true
|
||||||
|
} else {
|
||||||
|
z = false
|
||||||
|
}
|
||||||
|
if z2 {
|
||||||
|
b = 1
|
||||||
|
} else {
|
||||||
|
b = 0
|
||||||
|
}
|
||||||
|
if z {
|
||||||
|
b |= 2
|
||||||
|
}
|
||||||
|
w.WriteByte(b)
|
||||||
|
w.WriteTlv([]byte(d[indexOf2+1:]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t516.go
Normal file
12
protocol/tlv/t516.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T516() []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x516)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt32(0)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
13
protocol/tlv/t521.go
Normal file
13
protocol/tlv/t521.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T521() []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x521)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt32(0)
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
13
protocol/tlv/t525.go
Normal file
13
protocol/tlv/t525.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T525(t536 []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x525)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(1)
|
||||||
|
w.Write(t536)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t52d.go
Normal file
12
protocol/tlv/t52d.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T52D(devInfo []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x52d)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.Write(devInfo)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
12
protocol/tlv/t536.go
Normal file
12
protocol/tlv/t536.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T536(loginExtraData []byte) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x536)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.Write(loginExtraData)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
14
protocol/tlv/t8.go
Normal file
14
protocol/tlv/t8.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
import "github.com/Mrs4s/MiraiGo/binary"
|
||||||
|
|
||||||
|
func T8(localId uint32) []byte {
|
||||||
|
return binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0x8)
|
||||||
|
w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) {
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
w.WriteUInt32(localId)
|
||||||
|
w.WriteUInt16(0)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
8
protocol/tlv/tlv.go
Normal file
8
protocol/tlv/tlv.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package tlv
|
||||||
|
|
||||||
|
func GuidFlag() uint32 {
|
||||||
|
var flag uint32 = 0
|
||||||
|
flag |= 1 << 24 & 0xFF000000
|
||||||
|
flag |= 0 << 8 & 0xFF00
|
||||||
|
return flag
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user