diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..1f189a229 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,70 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '27 14 * * 1' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/client/builders.go b/client/builders.go index 5980e0149..f4af9f26b 100644 --- a/client/builders.go +++ b/client/builders.go @@ -1094,21 +1094,23 @@ func (c *QQClient) buildGroupMuteAllPacket(groupCode int64, mute bool) (uint16, } // OidbSvc.0x8a0_0 -func (c *QQClient) buildGroupKickPacket(groupCode, memberUin int64, kickMsg string, block bool) (uint16, []byte) { +func (c *QQClient) buildGroupKickPacket(groupCode int64, kickMsg string, block bool, memberUins ...int64) (uint16, []byte) { flagBlock := 0 if block { flagBlock = 1 } + msgKickList := make([]*oidb.D8A0KickMemberInfo, 0, len(memberUins)) + for _, memberUin := range memberUins { + msgKickList = append(msgKickList, &oidb.D8A0KickMemberInfo{ + OptUint32Operate: 5, + OptUint64MemberUin: memberUin, + OptUint32Flag: int32(flagBlock), + }) + } body := &oidb.D8A0ReqBody{ OptUint64GroupCode: groupCode, - MsgKickList: []*oidb.D8A0KickMemberInfo{ - { - OptUint32Operate: 5, - OptUint64MemberUin: memberUin, - OptUint32Flag: int32(flagBlock), - }, - }, - KickMsg: []byte(kickMsg), + MsgKickList: msgKickList, + KickMsg: []byte(kickMsg), } b, _ := proto.Marshal(body) payload := c.packOIDBPackage(2208, 0, b) diff --git a/client/c2c_processor.go b/client/c2c_processor.go index 621c92a14..0f82ddfc5 100644 --- a/client/c2c_processor.go +++ b/client/c2c_processor.go @@ -225,7 +225,7 @@ func troopAddMemberBroadcastDecoder(c *QQClient, pMsg *msg.Message, _ network.Re if group != nil && group.FindMember(pMsg.Head.AuthUin.Unwrap()) == nil { mem, err := c.GetMemberInfo(group.Code, pMsg.Head.AuthUin.Unwrap()) if err != nil { - c.debug("error to fetch new member info: %v", err) + c.debug("failed to fetch new member info: %v", err) return } group.Update(func(info *GroupInfo) { diff --git a/client/client.go b/client/client.go index e72cff602..5d25ab08c 100644 --- a/client/client.go +++ b/client/client.go @@ -379,7 +379,7 @@ func (c *QQClient) RequestSMS() bool { func (c *QQClient) init(tokenLogin bool) error { if len(c.sig.G) == 0 { - c.warning("device lock is disable. http api may fail.") + c.warning("device lock is disabled. HTTP API may fail.") } c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10) if err := c.registerClient(); err != nil { @@ -567,7 +567,7 @@ func (c *QQClient) getGroupMembers(group *GroupInfo, interner *intern.StringInte return nil, err } if data == nil { - return nil, errors.New("group member list unavailable: rsp is nil") + return nil, errors.New("group members list is unavailable: rsp is nil") } rsp := data.(*groupMemberListResponse) nextUin = rsp.NextUin @@ -726,8 +726,8 @@ func (c *QQClient) quitGroup(groupCode int64) { _, _ = c.sendAndWait(c.buildQuitGroupPacket(groupCode)) } -func (c *QQClient) kickGroupMember(groupCode, memberUin int64, msg string, block bool) { - _, _ = c.sendAndWait(c.buildGroupKickPacket(groupCode, memberUin, msg, block)) +func (c *QQClient) KickGroupMembers(groupCode int64, msg string, block bool, memberUins ...int64) { + _, _ = c.sendAndWait(c.buildGroupKickPacket(groupCode, msg, block, memberUins...)) } func (g *GroupInfo) removeMember(uin int64) { diff --git a/client/global.go b/client/global.go index cf90ad48a..7cbefa250 100644 --- a/client/global.go +++ b/client/global.go @@ -92,8 +92,8 @@ func GenIMEI() string { randGen := rand.New(rand.NewSource(time.Now().UnixNano())) for i := 0; i < 14; i++ { // generating all the base digits toAdd := randGen.Intn(10) - fmt.Fprintf(&final, "%d", toAdd) // printing them here! - if (i+1)%2 == 0 { // special proc for every 2nd one + final.WriteString(strconv.Itoa(toAdd)) + if (i+1)%2 == 0 { // special proc for every 2nd one toAdd *= 2 if toAdd >= 10 { toAdd = (toAdd % 10) + 1 @@ -102,7 +102,7 @@ func GenIMEI() string { sum += toAdd // and even add them here! } ctrlDigit := (sum * 9) % 10 // calculating the control digit - fmt.Fprintf(&final, "%d", ctrlDigit) + final.WriteString(strconv.Itoa(ctrlDigit)) return final.String() } diff --git a/client/group_info.go b/client/group_info.go index f2e4610a7..244312314 100644 --- a/client/group_info.go +++ b/client/group_info.go @@ -389,7 +389,7 @@ func (m *GroupMemberInfo) EditSpecialTitle(title string) { func (m *GroupMemberInfo) Kick(msg string, block bool) error { if m.Uin != m.Group.client.Uin && m.Manageable() { - m.Group.client.kickGroupMember(m.Group.Code, m.Uin, msg, block) + m.Group.client.KickGroupMembers(m.Group.Code, msg, block, m.Uin) return nil } else { return errors.New("not manageable") diff --git a/client/group_msg.go b/client/group_msg.go index bed82ba0a..b86309157 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -267,7 +267,7 @@ func decodeMsgSendResponse(c *QQClient, pkt *network.Packet) (any, error) { case 46: c.error("sendPacket msg error: 需要使用安全设备验证") case 55: - c.error("sendPacket msg error: %v Bot has blocked ta.'s content", rsp.Result.Unwrap()) + c.error("sendPacket msg error: %v Bot has been blocked ta.'s content", rsp.Result.Unwrap()) default: c.error("sendPacket msg error: %v %v", rsp.Result.Unwrap(), rsp.ErrMsg.Unwrap()) } @@ -342,7 +342,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage { c.debug("sync group %v.", m.Head.GroupInfo.GroupCode.Unwrap()) info, err := c.GetGroupInfo(m.Head.GroupInfo.GroupCode.Unwrap()) if err != nil { - c.error("error to sync group %v : %+v", m.Head.GroupInfo.GroupCode.Unwrap(), err) + c.error("failed to sync group %v : %+v", m.Head.GroupInfo.GroupCode.Unwrap(), err) return nil } group = info @@ -351,7 +351,7 @@ func (c *QQClient) parseGroupMessage(m *msg.Message) *message.GroupMessage { if len(group.Members) == 0 { mem, err := c.GetGroupMembers(group) if err != nil { - c.error("error to sync group %v member : %+v", m.Head.GroupInfo.GroupCode, err) + c.error("failed to sync group %v members : %+v", m.Head.GroupInfo.GroupCode, err) return nil } group.Members = mem diff --git a/client/guild_eventflow.go b/client/guild_eventflow.go index ac9bef8cc..ce646b8fd 100644 --- a/client/guild_eventflow.go +++ b/client/guild_eventflow.go @@ -151,7 +151,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody if oldInfo == nil { info, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GuildId.Unwrap(), eventBody.ChangeChanInfo.ChanId.Unwrap()) if err != nil { - c.error("error to decode channel info updated event: fetch channel info failed: %v", err) + c.error("failed to decode channel info updated event: fetch channel info failed: %v", err) return } guild.Channels = append(guild.Channels, info) @@ -162,7 +162,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody } newInfo, err := c.GuildService.FetchChannelInfo(m.Head.RoutingHead.GuildId.Unwrap(), eventBody.ChangeChanInfo.ChanId.Unwrap()) if err != nil { - c.error("error to decode channel info updated event: fetch channel info failed: %v", err) + c.error("failed to decode channel info updated event: fetch channel info failed: %v", err) return } for i := range guild.Channels { @@ -187,7 +187,7 @@ func (c *QQClient) processGuildEventBody(m *channel.ChannelMsgContent, eventBody */ profile, err := c.GuildService.FetchGuildMemberProfileInfo(guild.GuildId, eventBody.JoinGuild.MemberTinyid.Unwrap()) if err != nil { - c.error("error to decode member join guild event: get member profile error: %v", err) + c.error("failed to decode member join guild event: get member profile error: %v", err) return } info := &GuildMemberInfo{ diff --git a/client/http_api.go b/client/http_api.go index e0da46ec7..e43ddccee 100644 --- a/client/http_api.go +++ b/client/http_api.go @@ -9,6 +9,7 @@ import ( "net/http" "net/textproto" "net/url" + "regexp" "strconv" "github.com/pkg/errors" @@ -59,15 +60,20 @@ const ( Emotion HonorType = 6 // 快乐源泉 ) +// 匹配 window.__INITIAL_STATE__ = 后的内容 +var honorRe = regexp.MustCompile(`window\.__INITIAL_STATE__\s*?=\s*?(\{.*\})`) + func (c *QQClient) GetGroupHonorInfo(groupCode int64, honorType HonorType) (*GroupHonorInfo, error) { b, err := utils.HttpGetBytes(fmt.Sprintf("https://qun.qq.com/interactive/honorlist?gc=%d&type=%d", groupCode, honorType), c.getCookiesWithDomain("qun.qq.com")) if err != nil { return nil, err } - b = b[bytes.Index(b, []byte(`window.__INITIAL_STATE__=`))+25:] - b = b[:bytes.Index(b, []byte(""))] + matched := honorRe.FindSubmatch(b) + if len(matched) == 0 { + return nil, errors.New("无匹配结果") + } ret := GroupHonorInfo{} - err = json.Unmarshal(b, &ret) + err = json.NewDecoder(bytes.NewReader(matched[1])).Decode(&ret) if err != nil { return nil, err } diff --git a/client/internal/auth/qimei.go b/client/internal/auth/qimei.go index 5694330d7..627ca14c4 100644 --- a/client/internal/auth/qimei.go +++ b/client/internal/auth/qimei.go @@ -5,6 +5,7 @@ import ( "crypto/aes" "crypto/cipher" "crypto/md5" + crand "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" @@ -42,7 +43,7 @@ func (info *Device) RequestQImei() { // init rsa key and aes key publicKey := initPublicKey() - encryptedAesKey, _ := rsa.EncryptPKCS1v15(rand.New(rand.NewSource(time.Now().UnixNano())), publicKey, []byte(cryptKey)) + encryptedAesKey, _ := rsa.EncryptPKCS1v15(crand.Reader, publicKey, []byte(cryptKey)) encryptedPayload := aesEncrypt(payload, []byte(cryptKey)) diff --git a/client/network.go b/client/network.go index 2b97050ac..5709a0429 100644 --- a/client/network.go +++ b/client/network.go @@ -310,6 +310,13 @@ func (c *QQClient) sendAndWaitDynamic(seq uint16, pkt []byte) ([]byte, error) { } } +// SendSsoPacket +// 发送签名回调包给服务器并获取返回结果供提交 +func (c *QQClient) SendSsoPacket(cmd string, body []byte) ([]byte, error) { + seq, data := c.uniPacket(cmd, body) + return c.sendAndWaitDynamic(seq, data) +} + // plannedDisconnect 计划中断线事件 func (c *QQClient) plannedDisconnect(_ *network.TCPClient) { c.debug("planned disconnect.") diff --git a/client/tlv_decoders.go b/client/tlv_decoders.go index 200e5370f..9d74ea450 100644 --- a/client/tlv_decoders.go +++ b/client/tlv_decoders.go @@ -109,7 +109,7 @@ func (c *QQClient) decodeT119(data, ek []byte) { s.PsKeyMap = psKeyMap s.Pt4TokenMap = pt4TokenMap - if len(c.PasswordMd5[:]) > 0 { + if len(c.sig.EncryptedA1) > 51+16 { data, cl := binary.OpenWriterF(func(w *binary.Writer) { w.Write(c.PasswordMd5[:]) w.WriteUInt32(0) // []byte{0x00, 0x00, 0x00, 0x00}... diff --git a/message/message.go b/message/message.go index cbfdebf45..d5fc6a620 100644 --- a/message/message.go +++ b/message/message.go @@ -13,6 +13,13 @@ import ( "github.com/Mrs4s/MiraiGo/utils" ) +type IMessage interface { + GetElements() []IMessageElement + Chat() int64 + ToString() string + Texts() []string +} + type ( PrivateMessage struct { Id int32 @@ -107,7 +114,7 @@ func NewSendingMessage() *SendingMessage { } func (msg *PrivateMessage) ToString() (res string) { - for _, elem := range msg.Elements { + for _, elem := range msg.GetElements() { switch e := elem.(type) { case *TextElement: res += e.Content @@ -120,42 +127,104 @@ func (msg *PrivateMessage) ToString() (res string) { return } +func (msg *PrivateMessage) Chat() int64 { + return msg.Sender.Uin +} + +func (msg *PrivateMessage) GetElements() []IMessageElement { + return msg.Elements +} + +func (msg *PrivateMessage) Texts() []string { + return parseTexts(msg.GetElements()) +} + +func (msg *GroupMessage) Chat() int64 { + return msg.GroupCode +} + +func (msg *GroupMessage) GetElements() []IMessageElement { + return msg.Elements +} + +func (msg *TempMessage) Chat() int64 { + return msg.GroupCode +} + +func (msg *TempMessage) GetElements() []IMessageElement { + return msg.Elements +} + +func (msg *TempMessage) Texts() []string { + return parseTexts(msg.GetElements()) +} + func (msg *TempMessage) ToString() (res string) { + var strBuilder strings.Builder for _, elem := range msg.Elements { switch e := elem.(type) { case *TextElement: - res += e.Content + strBuilder.WriteString(e.Content) case *FaceElement: - res += "[" + e.Name + "]" + strBuilder.WriteString("[") + strBuilder.WriteString(e.Name) + strBuilder.WriteString("]") case *AtElement: - res += e.Display + strBuilder.WriteString(e.Display) } } + res = strBuilder.String() return } func (msg *GroupMessage) ToString() (res string) { - for _, elem := range msg.Elements { + var strBuilder strings.Builder + for _, elem := range msg.GetElements() { switch e := elem.(type) { case *TextElement: - res += e.Content + strBuilder.WriteString(e.Content) case *FaceElement: - res += "[" + e.Name + "]" + strBuilder.WriteString("[") + strBuilder.WriteString(e.Name) + strBuilder.WriteString("]") case *MarketFaceElement: - res += "[" + e.Name + "]" + strBuilder.WriteString("[") + strBuilder.WriteString(e.Name) + strBuilder.WriteString("]") case *GroupImageElement: - res += "[Image: " + e.ImageId + "]" + strBuilder.WriteString("Image: ") + strBuilder.WriteString(e.ImageId) + strBuilder.WriteString("]") case *AtElement: - res += e.Display + strBuilder.WriteString(e.Display) case *RedBagElement: - res += "[RedBag:" + e.Title + "]" + strBuilder.WriteString("[RedBag: ") + strBuilder.WriteString(e.Title) + strBuilder.WriteString("]") case *ReplyElement: - res += "[Reply:" + strconv.FormatInt(int64(e.ReplySeq), 10) + "]" + strBuilder.WriteString("[Reply: ") + strBuilder.WriteString(strconv.FormatInt(int64(e.ReplySeq), 10)) + strBuilder.WriteString("]") } } + res = strBuilder.String() return } +func (msg *GroupMessage) Texts() []string { + return parseTexts(msg.GetElements()) +} + +func parseTexts(elements []IMessageElement) []string { + texts := make([]string, 0, 4) + for _, elem := range elements { + if elem.Type() == Text { + texts = append(texts, elem.(*TextElement).Content) + } + } + return texts +} + func (msg *SendingMessage) Append(e IMessageElement) *SendingMessage { v := reflect.ValueOf(e) if v.Kind() == reflect.Ptr && !v.IsNil() { diff --git a/topic/feed.go b/topic/feed.go index 56cd2f5a6..f48862d67 100644 --- a/topic/feed.go +++ b/topic/feed.go @@ -159,7 +159,7 @@ func DecodeFeed(p *channel.StFeed) *Feed { f.Contents = append(f.Contents, &TextElement{Content: c.TextContent.Text.Unwrap()}) } if c.EmojiContent != nil { - id, _ := strconv.ParseInt(c.EmojiContent.Id.Unwrap(), 10, 64) + id, _ := strconv.ParseInt(c.EmojiContent.Id.Unwrap(), 10, 32) f.Contents = append(f.Contents, &EmojiElement{ Index: int32(id), Id: c.EmojiContent.Id.Unwrap(), diff --git a/utils/string.go b/utils/string.go index 032e1ac36..6162f305c 100644 --- a/utils/string.go +++ b/utils/string.go @@ -44,7 +44,7 @@ func ChunkString(s string, chunkSize int) []string { } func ConvertSubVersionToInt(str string) int32 { - i, _ := strconv.ParseInt(strings.Join(strings.Split(str, "."), ""), 10, 64) + i, _ := strconv.ParseInt(strings.Join(strings.Split(str, "."), ""), 10, 32) return int32(i) * 10 }