Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
1e885dbf
Commit
1e885dbf
authored
May 09, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rearrange MessageSvc and OnlinePush
parent
1c1a37a1
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
1526 additions
and
1340 deletions
+1526
-1340
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
...ain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
+3
-3
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
...in/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
+3
-3
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt
...mmonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt
+3
-3
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt
...n/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt
+2
-2
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
...moe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
+3
-3
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/msgType0x210.kt
...rai/qqandroid/network/protocol/data/proto/msgType0x210.kt
+1
-1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
.../mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+24
-26
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbDeleteMsg.kt
...rk/protocol/packet/chat/receive/MessageSvc.PbDeleteMsg.kt
+55
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt
...twork/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt
+346
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt
...work/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt
+237
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt
...otocol/packet/chat/receive/MessageSvc.PushForceOffline.kt
+29
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushNotify.kt
...ork/protocol/packet/chat/receive/MessageSvc.PushNotify.kt
+42
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
...ndroid/network/protocol/packet/chat/receive/MessageSvc.kt
+0
-600
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt
...protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt
+115
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushTransMsg.kt
...protocol/packet/chat/receive/OnlinePush.PbPushTransMsg.kt
+144
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt
...etwork/protocol/packet/chat/receive/OnlinePush.ReqPush.kt
+518
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
...ndroid/network/protocol/packet/chat/receive/OnlinePush.kt
+0
-698
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
...mmonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+1
-1
No files found.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
View file @
1e885dbf
...
...
@@ -32,7 +32,7 @@ import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull
import
net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
PbSendMsg
import
net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService
import
net.mamoe.mirai.qqandroid.utils.estimateLength
import
net.mamoe.mirai.utils.*
...
...
@@ -358,7 +358,7 @@ internal class GroupImpl(
lateinit
var
source
:
MessageSourceToGroupImpl
bot
.
network
.
run
{
val
response
:
MessageSvc
.
PbSendMsg
.
Response
=
MessageSvc
.
PbSendMsg
.
createToGroup
(
val
response
:
MessageSvc
PbSendMsg
.
Response
=
MessageSvc
PbSendMsg
.
createToGroup
(
bot
.
client
,
this
@GroupImpl
,
msg
,
...
...
@@ -366,7 +366,7 @@ internal class GroupImpl(
)
{
source
=
it
}.
sendAndExpect
()
if
(
response
is
MessageSvc
.
PbSendMsg
.
Response
.
Failed
)
{
if
(
response
is
MessageSvcPbSendMsg
.
Response
.
Failed
)
{
when
(
response
.
resultType
)
{
120
->
throw
BotIsBeingMutedException
(
this
@GroupImpl
)
34
->
{
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
View file @
1e885dbf
...
...
@@ -32,7 +32,7 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.message.MessageSourceToTempImpl
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
PbSendMsg
import
net.mamoe.mirai.utils.*
import
kotlin.contracts.ExperimentalContracts
import
kotlin.contracts.contract
...
...
@@ -68,13 +68,13 @@ internal class MemberImpl constructor(
lateinit
var
source
:
MessageSourceToTempImpl
bot
.
network
.
run
{
check
(
MessageSvc
.
PbSendMsg
.
createToTemp
(
MessageSvcPbSendMsg
.
createToTemp
(
bot
.
client
,
this
@MemberImpl
,
message
.
asMessageChain
()
)
{
source
=
it
}.
sendAndExpect
<
MessageSvc
.
PbSendMsg
.
Response
>()
is
MessageSvc
.
PbSendMsg
.
Response
.
SUCCESS
}.
sendAndExpect
<
MessageSvc
PbSendMsg
.
Response
>()
is
MessageSvc
PbSendMsg
.
Response
.
SUCCESS
)
{
"send message failed"
}
}
return
MessageReceipt
(
source
,
this
,
null
)
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt
View file @
1e885dbf
...
...
@@ -23,7 +23,7 @@ import net.mamoe.mirai.qqandroid.message.MessageSourceToFriendImpl
import
net.mamoe.mirai.qqandroid.message.ensureSequenceIdAvailable
import
net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull
import
net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
PbSendMsg
import
net.mamoe.mirai.utils.MiraiExperimentalAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.verbose
...
...
@@ -38,13 +38,13 @@ internal suspend fun <T : Contact> Friend.sendMessageImpl(generic: T, message: M
lateinit
var
source
:
MessageSourceToFriendImpl
(
bot
.
network
as
QQAndroidBotNetworkHandler
).
run
{
check
(
MessageSvc
.
PbSendMsg
.
createToFriend
(
MessageSvcPbSendMsg
.
createToFriend
(
bot
.
asQQAndroidBot
().
client
,
this
@
sendMessageImpl
,
event
.
message
)
{
source
=
it
}.
sendAndExpect
<
MessageSvc
.
PbSendMsg
.
Response
>()
is
MessageSvc
.
PbSendMsg
.
Response
.
SUCCESS
}.
sendAndExpect
<
MessageSvc
PbSendMsg
.
Response
>()
is
MessageSvc
PbSendMsg
.
Response
.
SUCCESS
)
{
"send message failed"
}
}
return
MessageReceipt
(
source
,
generic
,
null
)
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt
View file @
1e885dbf
...
...
@@ -25,7 +25,7 @@ import net.mamoe.mirai.message.data.OnlineMessageSource
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
PbPushGroupMsg.SendGroupMessageReceipt
import
net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
...
...
@@ -117,7 +117,7 @@ internal class MessageSourceToGroupImpl(
override
var
isRecalledOrPlanned
:
MiraiAtomicBoolean
=
MiraiAtomicBoolean
(
false
)
private
val
sequenceIdDeferred
:
Deferred
<
Int
?
>
=
coroutineScope
.
asyncFromEventOrNull
<
OnlinePush
.
PbPushGroupMsg
.
SendGroupMessageReceipt
,
Int
>(
coroutineScope
.
asyncFromEventOrNull
<
SendGroupMessageReceipt
,
Int
>(
timeoutMillis
=
3000
)
{
if
(
it
.
messageRandom
==
this
@MessageSourceToGroupImpl
.
internalId
)
{
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
View file @
1e885dbf
...
...
@@ -32,7 +32,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopNum
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.*
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
PbGetMsg
import
net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.Heartbeat
...
...
@@ -362,8 +362,8 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
logger
.
info
{
"Syncing friend message history..."
}
withTimeoutOrNull
(
30000
)
{
launch
{
syncFromEvent
<
MessageSvc
.
PbGetMsg
.
GetMsgSuccess
,
Unit
>
{
Unit
}
}
MessageSvc
.
PbGetMsg
(
bot
.
client
,
MsgSvc
.
SyncFlag
.
START
,
currentTimeSeconds
).
sendAndExpect
<
Packet
>()
launch
{
syncFromEvent
<
MessageSvcPbGetMsg
.
GetMsgSuccess
,
Unit
>
{
Unit
}
}
MessageSvcPbGetMsg
(
bot
.
client
,
MsgSvc
.
SyncFlag
.
START
,
currentTimeSeconds
).
sendAndExpect
<
Packet
>()
}
?:
error
(
"timeout syncing friend message history"
)
logger
.
info
{
"Syncing friend message history: Success"
}
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/msgType0x210.kt
View file @
1e885dbf
...
...
@@ -897,7 +897,7 @@ internal class Submsgtype0x27 {
)
:
ProtoBuf
@Serializable
internal
class
MsgBody
(
internal
class
SubMsgType0x27
MsgBody
(
@ProtoId
(
1
)
@JvmField
val
msgModInfos
:
List
<
ForwardBody
>
=
listOf
()
)
:
ProtoBuf
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
View file @
1e885dbf
...
...
@@ -19,8 +19,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.*
import
net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import
net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
...
...
@@ -132,10 +131,10 @@ internal object KnownPacketFactories {
WtLogin
.
Login
,
StatSvc
.
Register
,
StatSvc
.
GetOnlineStatus
,
MessageSvc
.
PbGetMsg
,
MessageSvc
.
PushForceOffline
,
MessageSvc
.
PbSendMsg
,
MessageSvc
.
Del
,
MessageSvcPbGetMsg
,
MessageSvcPushForceOffline
,
MessageSvcPbSendMsg
,
MessageSvc
PbDeleteMsg
,
FriendList
.
GetFriendGroupList
,
FriendList
.
GetTroopListSimplify
,
FriendList
.
GetTroopMemberList
,
...
...
@@ -157,15 +156,15 @@ internal object KnownPacketFactories {
)
object
IncomingFactories
:
List
<
IncomingPacketFactory
<
*
>>
by
mutableListOf
(
OnlinePush
.
PbPushGroupMsg
,
OnlinePush
.
ReqPush
,
OnlinePush
.
PbPushTransMsg
,
MessageSvc
.
PushNotify
,
OnlinePushPbPushGroupMsg
,
OnlinePushReqPush
,
OnlinePushPbPushTransMsg
,
MessageSvcPushNotify
,
ConfigPushSvc
.
PushReq
,
StatSvc
.
ReqMSFOffline
)
// SvcReqMSFLoginNotify 自己的其他设备上限
// MessageSvc
.
PushReaded 电脑阅读了别人的消息, 告知手机
// MessageSvcPushReaded 电脑阅读了别人的消息, 告知手机
// OnlinePush.PbC2CMsgSync 电脑发消息给别人, 同步给手机
@Suppress
(
"MemberVisibilityCanBePrivate"
)
// debugging use
...
...
@@ -259,21 +258,20 @@ internal object KnownPacketFactories {
PacketLogger
.
info
{
"Handle packet: ${it.commandName}"
}
it
.
data
.
withUse
{
when
(
flag2
)
{
0
,
1
->
when
(
it
.
packetFactory
)
{
is
OutgoingPacketFactory
<
*
>
->
consumer
(
it
.
packetFactory
as
OutgoingPacketFactory
<
T
>,
it
.
packetFactory
.
run
{
decode
(
bot
,
it
.
data
)
},
it
.
packetFactory
.
commandName
,
it
.
sequenceId
)
is
IncomingPacketFactory
<
*
>
->
consumer
(
it
.
packetFactory
as
IncomingPacketFactory
<
T
>,
it
.
packetFactory
.
run
{
decode
(
bot
,
it
.
data
,
it
.
sequenceId
)
},
it
.
packetFactory
.
receivingCommandName
,
it
.
sequenceId
)
}
0
,
1
->
when
(
it
.
packetFactory
)
{
is
OutgoingPacketFactory
<
*
>
->
consumer
(
it
.
packetFactory
as
OutgoingPacketFactory
<
T
>,
it
.
packetFactory
.
run
{
decode
(
bot
,
it
.
data
)
},
it
.
packetFactory
.
commandName
,
it
.
sequenceId
)
is
IncomingPacketFactory
<
*
>
->
consumer
(
it
.
packetFactory
as
IncomingPacketFactory
<
T
>,
it
.
packetFactory
.
run
{
decode
(
bot
,
it
.
data
,
it
.
sequenceId
)
},
it
.
packetFactory
.
receivingCommandName
,
it
.
sequenceId
)
}
2
->
it
.
data
.
parseOicqResponse
(
bot
,
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbDeleteMsg.kt
0 → 100644
View file @
1e885dbf
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.flow.map
import
kotlinx.coroutines.flow.toList
import
kotlinx.io.core.ByteReadPacket
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import
net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import
net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
internal
object
MessageSvcPbDeleteMsg
:
OutgoingPacketFactory
<
Nothing
?
>(
"MessageSvcPbDeleteMsg"
)
{
internal
operator
fun
invoke
(
client
:
QQAndroidClient
,
items
:
List
<
MsgSvc
.
PbDeleteMsgReq
.
MsgItem
>)
=
buildOutgoingUniPacket
(
client
)
{
writeProtoBuf
(
MsgSvc
.
PbDeleteMsgReq
.
serializer
(),
MsgSvc
.
PbDeleteMsgReq
(
msgItems
=
items
)
)
}
internal
suspend
fun
delete
(
bot
:
QQAndroidBot
,
messages
:
Flow
<
MsgComm
.
Msg
>)
=
bot
.
network
.
run
{
val
map
=
messages
.
map
{
MsgSvc
.
PbDeleteMsgReq
.
MsgItem
(
fromUin
=
it
.
msgHead
.
fromUin
,
toUin
=
it
.
msgHead
.
toUin
,
// 群为84、好友为187。群通过其他方法删除,但测试结果显示通过187也能删除群消息。
msgType
=
187
,
msgSeq
=
it
.
msgHead
.
msgSeq
,
msgUid
=
it
.
msgHead
.
msgUid
)
}.
toList
()
MessageSvcPbDeleteMsg
(
bot
.
client
,
map
).
sendWithoutExpect
()
}
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
)
=
null
}
\ No newline at end of file
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbGetMsg.kt
0 → 100644
View file @
1e885dbf
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.atomicfu.loop
import
kotlinx.coroutines.FlowPreview
import
kotlinx.coroutines.flow.*
import
kotlinx.io.core.ByteReadPacket
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.data.MemberInfo
import
net.mamoe.mirai.event.AbstractEvent
import
net.mamoe.mirai.event.Event
import
net.mamoe.mirai.event.events.BotJoinGroupEvent
import
net.mamoe.mirai.event.events.MemberJoinEvent
import
net.mamoe.mirai.getFriendOrNull
import
net.mamoe.mirai.message.FriendMessageEvent
import
net.mamoe.mirai.message.TempMessageEvent
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.contact.GroupImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsFriendImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl
import
net.mamoe.mirai.qqandroid.message.toMessageChain
import
net.mamoe.mirai.qqandroid.network.MultiPacket
import
net.mamoe.mirai.qqandroid.network.Packet
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import
net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact
import
net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import
net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import
net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
import
net.mamoe.mirai.utils.*
/**
* 获取好友消息和消息记录
*/
@OptIn
(
MiraiInternalAPI
::
class
)
internal
object
MessageSvcPbGetMsg
:
OutgoingPacketFactory
<
MessageSvcPbGetMsg
.
Response
>(
"MessageSvcPbGetMsg"
)
{
@Suppress
(
"SpellCheckingInspection"
)
operator
fun
invoke
(
client
:
QQAndroidClient
,
syncFlag
:
MsgSvc
.
SyncFlag
=
MsgSvc
.
SyncFlag
.
START
,
msgTime
:
Long
//PbPushMsg.msg.msgHead.msgTime
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
//println("syncCookie=${client.c2cMessageSync.syncCookie?.toUHexString()}")
writeProtoBuf
(
MsgSvc
.
PbGetMsgReq
.
serializer
(),
MsgSvc
.
PbGetMsgReq
(
msgReqType
=
1
,
// from.ctype.toInt()
contextFlag
=
1
,
rambleFlag
=
0
,
latestRambleNumber
=
20
,
otherRambleNumber
=
3
,
onlineSyncFlag
=
1
,
whisperSessionId
=
0
,
syncFlag
=
syncFlag
,
// serverBuf = from.serverBuf ?: EMPTY_BYTE_ARRAY,
syncCookie
=
client
.
c2cMessageSync
.
syncCookie
?:
SyncCookie
(
time
=
msgTime
).
toByteArray
(
SyncCookie
.
serializer
())
//.also { client.c2cMessageSync.syncCookie = it },
// syncFlag = client.c2cMessageSync.syncFlag,
//msgCtrlBuf = client.c2cMessageSync.msgCtrlBuf,
//pubaccountCookie = client.c2cMessageSync.pubAccountCookie
)
)
}
@OptIn
(
MiraiInternalAPI
::
class
)
open
class
GetMsgSuccess
(
delegate
:
List
<
Packet
>)
:
Response
(
MsgSvc
.
SyncFlag
.
STOP
,
delegate
),
Event
,
Packet
.
NoLog
{
override
fun
toString
():
String
=
"MessageSvcPbGetMsg.GetMsgSuccess(messages=<Iterable>))"
}
/**
* 不要直接 expect 这个 class. 它可能还没同步完成
*/
@MiraiInternalAPI
open
class
Response
(
internal
val
syncFlagFromServer
:
MsgSvc
.
SyncFlag
,
delegate
:
List
<
Packet
>)
:
AbstractEvent
(),
MultiPacket
<
Packet
>,
Iterable
<
Packet
>
by
(
delegate
)
{
override
fun
toString
():
String
=
"MessageSvcPbGetMsg.Response(syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))"
}
object
EmptyResponse
:
GetMsgSuccess
(
emptyList
())
private
suspend
fun
MsgComm
.
Msg
.
getNewGroup
(
bot
:
QQAndroidBot
):
Group
?
{
val
troopNum
=
bot
.
network
.
run
{
FriendList
.
GetTroopListSimplify
(
bot
.
client
)
.
sendAndExpect
<
FriendList
.
GetTroopListSimplify
.
Response
>(
retry
=
2
)
}.
groups
.
firstOrNull
{
it
.
groupUin
==
msgHead
.
fromUin
}
?:
return
null
@Suppress
(
"DuplicatedCode"
)
return
GroupImpl
(
bot
=
bot
,
coroutineContext
=
bot
.
coroutineContext
,
id
=
Group
.
calculateGroupCodeByGroupUin
(
msgHead
.
fromUin
),
groupInfo
=
bot
.
_lowLevelQueryGroupInfo
(
troopNum
.
groupCode
).
apply
{
this
as
GroupInfoImpl
if
(
this
.
delegate
.
groupName
==
null
)
{
this
.
delegate
.
groupName
=
troopNum
.
groupName
}
if
(
this
.
delegate
.
groupMemo
==
null
)
{
this
.
delegate
.
groupMemo
=
troopNum
.
groupMemo
}
if
(
this
.
delegate
.
groupUin
==
null
)
{
this
.
delegate
.
groupUin
=
troopNum
.
groupUin
}
this
.
delegate
.
groupCode
=
troopNum
.
groupCode
},
members
=
bot
.
_lowLevelQueryGroupMemberList
(
troopNum
.
groupUin
,
troopNum
.
groupCode
,
troopNum
.
dwGroupOwnerUin
)
)
}
@OptIn
(
LowLevelAPI
::
class
)
private
fun
MsgComm
.
Msg
.
getNewMemberInfo
():
MemberInfo
{
return
object
:
MemberInfo
{
override
val
nameCard
:
String
get
()
=
""
override
val
permission
:
MemberPermission
get
()
=
MemberPermission
.
MEMBER
override
val
specialTitle
:
String
get
()
=
""
override
val
muteTimestamp
:
Int
get
()
=
0
override
val
uin
:
Long
get
()
=
msgHead
.
authUin
override
val
nick
:
String
=
msgHead
.
authNick
.
takeIf
{
it
.
isNotEmpty
()
}
?:
msgHead
.
fromNick
}
}
@OptIn
(
MiraiInternalAPI
::
class
,
MiraiExperimentalAPI
::
class
,
FlowPreview
::
class
,
LowLevelAPI
::
class
)
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
):
Response
{
// 00 00 01 0F 08 00 12 00 1A 34 08 FF C1 C4 F1 05 10 FF C1 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 8A CA 91 D1 0C 48 9B A5 BD 9B 0A 58 DE 9D 99 F8 08 60 1D 68 FF C1 C4 F1 05 70 00 20 02 2A 9D 01 08 F3 C1 C4 F1 05 10 A2 FF 8C F0 03 18 01 22 8A 01 0A 2A 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 A6 01 20 0B 28 AE F9 01 30 F4 C1 C4 F1 05 38 A7 E3 D8 D4 84 80 80 80 01 B8 01 CD B5 01 12 08 08 01 10 00 18 00 20 00 1A 52 0A 50 0A 27 08 00 10 F4 C1 C4 F1 05 18 A7 E3 D8 D4 04 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 08 0A 06 0A 04 4E 4D 53 4C 12 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 12 04 4A 02 08 00 30 01 2A 15 08 97 A2 C1 F1 05 10 95 A6 F5 E5 0C 18 01 30 01 40 01 48 81 01 2A 10 08 D3 F7 B5 F1 05 10 DD F1 92 B7 07 18 01 30 01 38 00 42 00 48 00
val
resp
=
readProtoBuf
(
MsgSvc
.
PbGetMsgResp
.
serializer
())
if
(
resp
.
result
!=
0
)
{
bot
.
network
.
logger
.
warning
{
"MessageSvcPushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}"
}
return
EmptyResponse
}
bot
.
client
.
c2cMessageSync
.
syncCookie
=
resp
.
syncCookie
bot
.
client
.
c2cMessageSync
.
pubAccountCookie
=
resp
.
pubAccountCookie
bot
.
client
.
c2cMessageSync
.
msgCtrlBuf
=
resp
.
msgCtrlBuf
if
(
resp
.
uinPairMsgs
==
null
)
{
return
EmptyResponse
}
val
messages
=
resp
.
uinPairMsgs
.
asFlow
()
.
filterNot
{
it
.
msg
==
null
}
.
flatMapConcat
{
it
.
msg
!!
.
asFlow
()
}
.
also
{
MessageSvcPbDeleteMsg
.
delete
(
bot
,
it
)
}
// 删除消息
.
mapNotNull
<
MsgComm
.
Msg
,
Packet
>
{
msg
->
when
(
msg
.
msgHead
.
msgType
)
{
33
->
{
// 邀请入群
val
group
=
bot
.
getGroupByUinOrNull
(
msg
.
msgHead
.
fromUin
)
if
(
msg
.
msgHead
.
authUin
==
bot
.
id
)
{
if
(
group
!=
null
)
{
return
@
mapNotNull
null
}
// 新群
val
newGroup
=
msg
.
getNewGroup
(
bot
)
?:
return
@
mapNotNull
null
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
bot
.
groups
.
delegate
.
addLast
(
newGroup
)
return
@
mapNotNull
BotJoinGroupEvent
(
newGroup
)
}
else
{
group
?:
return
@
mapNotNull
null
if
(
group
.
members
.
contains
(
msg
.
msgHead
.
authUin
))
{
return
@
mapNotNull
null
}
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
return
@
mapNotNull
MemberJoinEvent
.
Invite
(
group
.
newMember
(
msg
.
getNewMemberInfo
())
.
also
{
group
.
members
.
delegate
.
addLast
(
it
)
})
}
}
34
->
{
// 主动入群
// 27 0B 60 E7 01 44 71 47 90 03 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 36 42 35 35 46 45 32 45 35 36 43 45 45 44 30 38 30 35 31 41 35 42 37 36 39 35 34 45 30 46 43 43 36 36 45 44 43 46 45 43 42 39 33 41 41 44 32 32
val
group
=
bot
.
getGroupByUinOrNull
(
msg
.
msgHead
.
fromUin
)
group
?:
return
@
mapNotNull
null
if
(
group
.
members
.
contains
(
msg
.
msgHead
.
authUin
))
{
return
@
mapNotNull
null
}
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
return
@
mapNotNull
MemberJoinEvent
.
Active
(
group
.
newMember
(
msg
.
getNewMemberInfo
())
.
also
{
group
.
members
.
delegate
.
addLast
(
it
)
})
}
166
->
{
if
(
msg
.
msgHead
.
fromUin
==
bot
.
id
)
{
loop
@
while
(
true
)
{
val
instance
=
bot
.
client
.
getFriendSeq
()
if
(
instance
<
msg
.
msgHead
.
msgSeq
)
{
if
(
bot
.
client
.
setFriendSeq
(
instance
,
msg
.
msgHead
.
msgSeq
))
{
break
@
loop
}
}
else
break
@
loop
}
return
@
mapNotNull
null
}
val
friend
=
bot
.
getFriendOrNull
(
msg
.
msgHead
.
fromUin
)
?:
return
@
mapNotNull
null
friend
.
checkIsFriendImpl
()
if
(!
bot
.
firstLoginSucceed
)
{
return
@
mapNotNull
null
}
friend
.
lastMessageSequence
.
loop
{
instant
->
if
(
msg
.
msgHead
.
msgSeq
>
instant
)
{
if
(
friend
.
lastMessageSequence
.
compareAndSet
(
instant
,
msg
.
msgHead
.
msgSeq
))
{
return
@
mapNotNull
FriendMessageEvent
(
friend
,
msg
.
toMessageChain
(
bot
,
groupIdOrZero
=
0
,
onlineSource
=
true
),
msg
.
msgHead
.
msgTime
)
}
}
else
return
@
mapNotNull
null
}
}
208
->
{
// friend ptt
return
@
mapNotNull
null
}
529
->
{
// 好友文件
return
@
mapNotNull
null
}
141
->
{
val
tmpHead
=
msg
.
msgHead
.
c2cTmpMsgHead
?:
return
@
mapNotNull
null
val
member
=
bot
.
getGroupByUinOrNull
(
tmpHead
.
groupUin
)
?.
getOrNull
(
msg
.
msgHead
.
fromUin
)
?:
return
@
mapNotNull
null
member
.
checkIsMemberImpl
()
if
(
msg
.
msgHead
.
fromUin
==
bot
.
id
||
!
bot
.
firstLoginSucceed
)
{
return
@
mapNotNull
null
}
member
.
lastMessageSequence
.
loop
{
instant
->
if
(
msg
.
msgHead
.
msgSeq
>
instant
)
{
if
(
member
.
lastMessageSequence
.
compareAndSet
(
instant
,
msg
.
msgHead
.
msgSeq
))
{
return
@
mapNotNull
TempMessageEvent
(
member
,
msg
.
toMessageChain
(
bot
,
groupIdOrZero
=
0
,
onlineSource
=
true
,
isTemp
=
true
),
msg
.
msgHead
.
msgTime
)
}
}
else
return
@
mapNotNull
null
}
}
84
,
87
->
{
// 请求入群验证 和 被要求入群
bot
.
network
.
run
{
NewContact
.
SystemMsgNewGroup
(
bot
.
client
).
sendWithoutExpect
()
}
return
@
mapNotNull
null
}
187
->
{
// 请求加好友验证
bot
.
network
.
run
{
NewContact
.
SystemMsgNewFriend
(
bot
.
client
).
sendWithoutExpect
()
}
return
@
mapNotNull
null
}
// 732: 27 0B 60 E7 0C 01 3E 03 3F A2 5E 90 60 E2 00 01 44 71 47 90 00 00 02 58
else
->
{
bot
.
network
.
logger
.
debug
{
"unknown PbGetMsg type ${msg.msgHead.msgType}"
}
return
@
mapNotNull
null
}
}
}
val
list
:
List
<
Packet
>
=
messages
.
toList
()
if
(
resp
.
syncFlag
==
MsgSvc
.
SyncFlag
.
STOP
)
{
return
GetMsgSuccess
(
list
)
}
return
Response
(
resp
.
syncFlag
,
list
)
}
override
suspend
fun
QQAndroidBot
.
handle
(
packet
:
Response
)
{
when
(
packet
.
syncFlagFromServer
)
{
MsgSvc
.
SyncFlag
.
STOP
->
return
MsgSvc
.
SyncFlag
.
START
->
{
network
.
run
{
MessageSvcPbGetMsg
(
client
,
MsgSvc
.
SyncFlag
.
CONTINUE
,
currentTimeSeconds
).
sendAndExpect
<
Packet
>()
}
return
}
MsgSvc
.
SyncFlag
.
CONTINUE
->
{
network
.
run
{
MessageSvcPbGetMsg
(
client
,
MsgSvc
.
SyncFlag
.
CONTINUE
,
currentTimeSeconds
).
sendAndExpect
<
Packet
>()
}
return
}
}
}
}
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt
0 → 100644
View file @
1e885dbf
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.toByteArray
import
net.mamoe.mirai.contact.Friend
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.Member
import
net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.message.data.PttMessage
import
net.mamoe.mirai.message.data.firstOrNull
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.contact.GroupImpl
import
net.mamoe.mirai.qqandroid.message.MessageSourceToFriendImpl
import
net.mamoe.mirai.qqandroid.message.MessageSourceToGroupImpl
import
net.mamoe.mirai.qqandroid.message.MessageSourceToTempImpl
import
net.mamoe.mirai.qqandroid.message.toRichTextElems
import
net.mamoe.mirai.qqandroid.network.Packet
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.*
import
net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import
net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import
net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import
net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
import
net.mamoe.mirai.utils.currentTimeSeconds
import
kotlin.math.absoluteValue
import
kotlin.random.Random
internal
object
MessageSvcPbSendMsg
:
OutgoingPacketFactory
<
MessageSvcPbSendMsg
.
Response
>(
"MessageSvcPbSendMsg"
)
{
sealed
class
Response
:
Packet
{
object
SUCCESS
:
Response
()
{
override
fun
toString
():
String
=
"MessageSvcPbSendMsg.Response.SUCCESS"
}
/**
* 121: 被限制? 个别号才不能发
*/
data class
Failed
(
val
resultType
:
Int
,
val
errorCode
:
Int
,
val
errorMessage
:
String
)
:
Response
()
{
override
fun
toString
():
String
=
"MessageSvcPbSendMsg.Response.Failed(resultType=$resultType, errorCode=$errorCode, errorMessage=$errorMessage)"
}
}
inline
fun
createToFriend
(
client
:
QQAndroidClient
,
qq
:
Friend
,
message
:
MessageChain
,
crossinline
sourceCallback
:
(
MessageSourceToFriendImpl
)
->
Unit
):
OutgoingPacket
{
val
rand
=
Random
.
nextInt
().
absoluteValue
val
source
=
MessageSourceToFriendImpl
(
internalId
=
rand
,
sender
=
client
.
bot
,
target
=
qq
,
time
=
currentTimeSeconds
.
toInt
(),
sequenceId
=
client
.
nextFriendSeq
(),
originalMessage
=
message
)
sourceCallback
(
source
)
return
createToFriend
(
client
,
qq
.
id
,
message
,
source
)
}
/**
* 发送好友消息
*/
@Suppress
(
"FunctionName"
)
private
fun
createToFriend
(
client
:
QQAndroidClient
,
toUin
:
Long
,
message
:
MessageChain
,
source
:
MessageSourceToFriendImpl
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes())
///return@buildOutgoingUniPacket
writeProtoBuf
(
MsgSvc
.
PbSendMsgReq
.
serializer
(),
MsgSvc
.
PbSendMsgReq
(
routingHead
=
MsgSvc
.
RoutingHead
(
c2c
=
MsgSvc
.
C2C
(
toUin
=
toUin
)),
contentHead
=
MsgComm
.
ContentHead
(
pkgNum
=
1
),
msgBody
=
ImMsgBody
.
MsgBody
(
richText
=
ImMsgBody
.
RichText
(
elems
=
message
.
toRichTextElems
(
forGroup
=
false
,
withGeneralFlags
=
true
)
)
),
msgSeq
=
source
.
sequenceId
,
msgRand
=
source
.
internalId
,
syncCookie
=
SyncCookie
(
time
=
source
.
time
.
toLong
()).
toByteArray
(
SyncCookie
.
serializer
())
// msgVia = 1
)
)
}
inline
fun
createToTemp
(
client
:
QQAndroidClient
,
member
:
Member
,
message
:
MessageChain
,
sourceCallback
:
(
MessageSourceToTempImpl
)
->
Unit
):
OutgoingPacket
{
val
source
=
MessageSourceToTempImpl
(
internalId
=
Random
.
nextInt
().
absoluteValue
,
sender
=
client
.
bot
,
target
=
member
,
time
=
currentTimeSeconds
.
toInt
(),
sequenceId
=
client
.
atomicNextMessageSequenceId
(),
originalMessage
=
message
)
sourceCallback
(
source
)
return
createToTemp
(
client
,
(
member
.
group
as
GroupImpl
).
uin
,
member
.
id
,
message
,
source
)
}
/**
* 发送临时消息
*/
private
fun
createToTemp
(
client
:
QQAndroidClient
,
groupUin
:
Long
,
toUin
:
Long
,
message
:
MessageChain
,
source
:
MessageSourceToTempImpl
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
writeProtoBuf
(
MsgSvc
.
PbSendMsgReq
.
serializer
(),
MsgSvc
.
PbSendMsgReq
(
routingHead
=
MsgSvc
.
RoutingHead
(
grpTmp
=
MsgSvc
.
GrpTmp
(
groupUin
,
toUin
)
),
contentHead
=
MsgComm
.
ContentHead
(
pkgNum
=
1
),
msgBody
=
ImMsgBody
.
MsgBody
(
richText
=
ImMsgBody
.
RichText
(
elems
=
message
.
toRichTextElems
(
forGroup
=
false
,
withGeneralFlags
=
true
)
)
),
msgSeq
=
source
.
sequenceId
,
msgRand
=
source
.
internalId
,
syncCookie
=
SyncCookie
(
time
=
source
.
time
.
toLong
()).
toByteArray
(
SyncCookie
.
serializer
())
)
)
}
inline
fun
createToGroup
(
client
:
QQAndroidClient
,
group
:
Group
,
message
:
MessageChain
,
isForward
:
Boolean
,
sourceCallback
:
(
MessageSourceToGroupImpl
)
->
Unit
):
OutgoingPacket
{
val
source
=
MessageSourceToGroupImpl
(
group
,
internalId
=
Random
.
nextInt
().
absoluteValue
,
sender
=
client
.
bot
,
target
=
group
,
time
=
currentTimeSeconds
.
toInt
(),
originalMessage
=
message
//,
// sourceMessage = message
)
sourceCallback
(
source
)
return
createToGroup
(
client
,
group
.
id
,
message
,
isForward
,
source
)
}
/**
* 发送群消息
*/
@Suppress
(
"FunctionName"
)
private
fun
createToGroup
(
client
:
QQAndroidClient
,
groupCode
:
Long
,
message
:
MessageChain
,
isForward
:
Boolean
,
source
:
MessageSourceToGroupImpl
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes())
// DebugLogger.debug("sending group message: " + message.toRichTextElems().contentToString())
///return@buildOutgoingUniPacket
writeProtoBuf
(
MsgSvc
.
PbSendMsgReq
.
serializer
(),
MsgSvc
.
PbSendMsgReq
(
routingHead
=
MsgSvc
.
RoutingHead
(
grp
=
MsgSvc
.
Grp
(
groupCode
=
groupCode
)),
contentHead
=
MsgComm
.
ContentHead
(
pkgNum
=
1
),
msgBody
=
ImMsgBody
.
MsgBody
(
richText
=
ImMsgBody
.
RichText
(
elems
=
message
.
toRichTextElems
(
forGroup
=
true
,
withGeneralFlags
=
true
),
ptt
=
message
.
firstOrNull
(
PttMessage
)
?.
run
{
ImMsgBody
.
Ptt
(
fileName
=
fileName
.
toByteArray
(),
fileMd5
=
md5
)
}
)
),
msgSeq
=
client
.
atomicNextMessageSequenceId
(),
msgRand
=
source
.
internalId
,
syncCookie
=
EMPTY_BYTE_ARRAY
,
msgVia
=
1
,
msgCtrl
=
if
(
isForward
)
MsgCtrl
.
MsgCtrl
(
msgFlag
=
4
)
else
null
)
)
}
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
):
Response
{
val
response
=
readProtoBuf
(
MsgSvc
.
PbSendMsgResp
.
serializer
())
return
if
(
response
.
result
==
0
)
{
Response
.
SUCCESS
}
else
{
Response
.
Failed
(
response
.
result
,
response
.
errtype
,
response
.
errmsg
)
}
}
}
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt
0 → 100644
View file @
1e885dbf
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.ByteReadPacket
import
net.mamoe.mirai.event.events.BotOfflineEvent
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPushForceOffline
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket
/**
* 被挤下线
*/
internal
object
MessageSvcPushForceOffline
:
OutgoingPacketFactory
<
BotOfflineEvent
.
Force
>(
"MessageSvcPushForceOffline"
)
{
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
):
BotOfflineEvent
.
Force
{
val
struct
=
this
.
readUniPacket
(
RequestPushForceOffline
.
serializer
())
return
BotOfflineEvent
.
Force
(
bot
,
title
=
struct
.
title
?:
""
,
message
=
struct
.
tips
?:
""
)
}
}
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushNotify.kt
0 → 100644
View file @
1e885dbf
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPushNotify
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket
import
net.mamoe.mirai.utils.currentTimeSeconds
/**
* 告知要刷新好友消息
*/
internal
object
MessageSvcPushNotify
:
IncomingPacketFactory
<
RequestPushNotify
>(
"MessageSvcPushNotify"
)
{
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
RequestPushNotify
{
discardExact
(
4
)
// don't remove
return
readUniPacket
(
RequestPushNotify
.
serializer
())
}
override
suspend
fun
QQAndroidBot
.
handle
(
packet
:
RequestPushNotify
,
sequenceId
:
Int
):
OutgoingPacket
?
{
network
.
run
{
return
MessageSvcPbGetMsg
(
client
,
MsgSvc
.
SyncFlag
.
START
,
packet
.
stMsgInfo
?.
uMsgTime
?:
currentTimeSeconds
)
}
}
}
\ No newline at end of file
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
deleted
100644 → 0
View file @
1c1a37a1
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
OptIn
(
LowLevelAPI
::
class
)
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.atomicfu.loop
import
kotlinx.coroutines.FlowPreview
import
kotlinx.coroutines.flow.*
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.toByteArray
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.Friend
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.Member
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.data.MemberInfo
import
net.mamoe.mirai.event.AbstractEvent
import
net.mamoe.mirai.event.Event
import
net.mamoe.mirai.event.events.BotJoinGroupEvent
import
net.mamoe.mirai.event.events.BotOfflineEvent
import
net.mamoe.mirai.event.events.MemberJoinEvent
import
net.mamoe.mirai.getFriendOrNull
import
net.mamoe.mirai.message.FriendMessageEvent
import
net.mamoe.mirai.message.TempMessageEvent
import
net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.message.data.PttMessage
import
net.mamoe.mirai.message.data.Voice
import
net.mamoe.mirai.message.data.firstOrNull
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.contact.GroupImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsFriendImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl
import
net.mamoe.mirai.qqandroid.message.*
import
net.mamoe.mirai.qqandroid.network.MultiPacket
import
net.mamoe.mirai.qqandroid.network.Packet
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPushForceOffline
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPushNotify
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgCtrl.MsgCtrl
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie
import
net.mamoe.mirai.qqandroid.network.protocol.packet.*
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact
import
net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import
net.mamoe.mirai.qqandroid.utils._miraiContentToString
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket
import
net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import
net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
import
net.mamoe.mirai.utils.*
import
kotlin.collections.firstOrNull
import
kotlin.math.absoluteValue
import
kotlin.random.Random
internal
class
MessageSvc
{
/**
* 告知要刷新好友消息
*/
internal
object
PushNotify
:
IncomingPacketFactory
<
RequestPushNotify
>(
"MessageSvc.PushNotify"
)
{
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
RequestPushNotify
{
discardExact
(
4
)
// don't remove
return
readUniPacket
(
RequestPushNotify
.
serializer
())
}
override
suspend
fun
QQAndroidBot
.
handle
(
packet
:
RequestPushNotify
,
sequenceId
:
Int
):
OutgoingPacket
?
{
network
.
run
{
return
PbGetMsg
(
client
,
MsgSvc
.
SyncFlag
.
START
,
packet
.
stMsgInfo
?.
uMsgTime
?:
currentTimeSeconds
)
}
}
}
/**
* 获取好友消息和消息记录
*/
@OptIn
(
MiraiInternalAPI
::
class
)
internal
object
PbGetMsg
:
OutgoingPacketFactory
<
PbGetMsg
.
Response
>(
"MessageSvc.PbGetMsg"
)
{
@Suppress
(
"SpellCheckingInspection"
)
operator
fun
invoke
(
client
:
QQAndroidClient
,
syncFlag
:
MsgSvc
.
SyncFlag
=
MsgSvc
.
SyncFlag
.
START
,
msgTime
:
Long
//PbPushMsg.msg.msgHead.msgTime
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
//println("syncCookie=${client.c2cMessageSync.syncCookie?.toUHexString()}")
writeProtoBuf
(
MsgSvc
.
PbGetMsgReq
.
serializer
(),
MsgSvc
.
PbGetMsgReq
(
msgReqType
=
1
,
// from.ctype.toInt()
contextFlag
=
1
,
rambleFlag
=
0
,
latestRambleNumber
=
20
,
otherRambleNumber
=
3
,
onlineSyncFlag
=
1
,
whisperSessionId
=
0
,
syncFlag
=
syncFlag
,
// serverBuf = from.serverBuf ?: EMPTY_BYTE_ARRAY,
syncCookie
=
client
.
c2cMessageSync
.
syncCookie
?:
SyncCookie
(
time
=
msgTime
).
toByteArray
(
SyncCookie
.
serializer
())
//.also { client.c2cMessageSync.syncCookie = it },
// syncFlag = client.c2cMessageSync.syncFlag,
//msgCtrlBuf = client.c2cMessageSync.msgCtrlBuf,
//pubaccountCookie = client.c2cMessageSync.pubAccountCookie
)
)
}
@OptIn
(
MiraiInternalAPI
::
class
)
open
class
GetMsgSuccess
(
delegate
:
List
<
Packet
>)
:
Response
(
MsgSvc
.
SyncFlag
.
STOP
,
delegate
),
Event
,
Packet
.
NoLog
{
override
fun
toString
():
String
=
"MessageSvc.PbGetMsg.GetMsgSuccess(messages=<Iterable>))"
}
/**
* 不要直接 expect 这个 class. 它可能还没同步完成
*/
@MiraiInternalAPI
open
class
Response
(
internal
val
syncFlagFromServer
:
MsgSvc
.
SyncFlag
,
delegate
:
List
<
Packet
>)
:
AbstractEvent
(),
MultiPacket
<
Packet
>,
Iterable
<
Packet
>
by
(
delegate
)
{
override
fun
toString
():
String
=
"MessageSvc.PbGetMsg.Response(syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))"
}
object
EmptyResponse
:
GetMsgSuccess
(
emptyList
())
private
suspend
fun
MsgComm
.
Msg
.
getNewGroup
(
bot
:
QQAndroidBot
):
Group
?
{
val
troopNum
=
bot
.
network
.
run
{
FriendList
.
GetTroopListSimplify
(
bot
.
client
)
.
sendAndExpect
<
FriendList
.
GetTroopListSimplify
.
Response
>(
retry
=
2
)
}.
groups
.
firstOrNull
{
it
.
groupUin
==
msgHead
.
fromUin
}
?:
return
null
@Suppress
(
"DuplicatedCode"
)
return
GroupImpl
(
bot
=
bot
,
coroutineContext
=
bot
.
coroutineContext
,
id
=
Group
.
calculateGroupCodeByGroupUin
(
msgHead
.
fromUin
),
groupInfo
=
bot
.
_lowLevelQueryGroupInfo
(
troopNum
.
groupCode
).
apply
{
this
as
GroupInfoImpl
if
(
this
.
delegate
.
groupName
==
null
)
{
this
.
delegate
.
groupName
=
troopNum
.
groupName
}
if
(
this
.
delegate
.
groupMemo
==
null
)
{
this
.
delegate
.
groupMemo
=
troopNum
.
groupMemo
}
if
(
this
.
delegate
.
groupUin
==
null
)
{
this
.
delegate
.
groupUin
=
troopNum
.
groupUin
}
this
.
delegate
.
groupCode
=
troopNum
.
groupCode
},
members
=
bot
.
_lowLevelQueryGroupMemberList
(
troopNum
.
groupUin
,
troopNum
.
groupCode
,
troopNum
.
dwGroupOwnerUin
)
)
}
private
fun
MsgComm
.
Msg
.
getNewMemberInfo
():
MemberInfo
{
return
object
:
MemberInfo
{
override
val
nameCard
:
String
get
()
=
""
override
val
permission
:
MemberPermission
get
()
=
MemberPermission
.
MEMBER
override
val
specialTitle
:
String
get
()
=
""
override
val
muteTimestamp
:
Int
get
()
=
0
override
val
uin
:
Long
get
()
=
msgHead
.
authUin
override
val
nick
:
String
=
msgHead
.
authNick
.
takeIf
{
it
.
isNotEmpty
()
}
?:
msgHead
.
fromNick
}
}
@OptIn
(
MiraiInternalAPI
::
class
,
MiraiExperimentalAPI
::
class
,
FlowPreview
::
class
,
LowLevelAPI
::
class
)
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
):
Response
{
// 00 00 01 0F 08 00 12 00 1A 34 08 FF C1 C4 F1 05 10 FF C1 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 8A CA 91 D1 0C 48 9B A5 BD 9B 0A 58 DE 9D 99 F8 08 60 1D 68 FF C1 C4 F1 05 70 00 20 02 2A 9D 01 08 F3 C1 C4 F1 05 10 A2 FF 8C F0 03 18 01 22 8A 01 0A 2A 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 A6 01 20 0B 28 AE F9 01 30 F4 C1 C4 F1 05 38 A7 E3 D8 D4 84 80 80 80 01 B8 01 CD B5 01 12 08 08 01 10 00 18 00 20 00 1A 52 0A 50 0A 27 08 00 10 F4 C1 C4 F1 05 18 A7 E3 D8 D4 04 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 08 0A 06 0A 04 4E 4D 53 4C 12 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 12 04 4A 02 08 00 30 01 2A 15 08 97 A2 C1 F1 05 10 95 A6 F5 E5 0C 18 01 30 01 40 01 48 81 01 2A 10 08 D3 F7 B5 F1 05 10 DD F1 92 B7 07 18 01 30 01 38 00 42 00 48 00
val
resp
=
readProtoBuf
(
MsgSvc
.
PbGetMsgResp
.
serializer
())
if
(
resp
.
result
!=
0
)
{
bot
.
network
.
logger
.
warning
{
"MessageSvc.PushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}"
}
return
EmptyResponse
}
bot
.
client
.
c2cMessageSync
.
syncCookie
=
resp
.
syncCookie
bot
.
client
.
c2cMessageSync
.
pubAccountCookie
=
resp
.
pubAccountCookie
bot
.
client
.
c2cMessageSync
.
msgCtrlBuf
=
resp
.
msgCtrlBuf
if
(
resp
.
uinPairMsgs
==
null
)
{
return
EmptyResponse
}
val
messages
=
resp
.
uinPairMsgs
.
asFlow
()
.
filterNot
{
it
.
msg
==
null
}
.
flatMapConcat
{
it
.
msg
!!
.
asFlow
()
}
.
also
{
Del
.
delete
(
bot
,
it
)
}
// 删除消息
.
mapNotNull
<
MsgComm
.
Msg
,
Packet
>
{
msg
->
when
(
msg
.
msgHead
.
msgType
)
{
33
->
{
// 邀请入群
val
group
=
bot
.
getGroupByUinOrNull
(
msg
.
msgHead
.
fromUin
)
if
(
msg
.
msgHead
.
authUin
==
bot
.
id
)
{
if
(
group
!=
null
)
{
return
@
mapNotNull
null
}
// 新群
val
newGroup
=
msg
.
getNewGroup
(
bot
)
?:
return
@
mapNotNull
null
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
bot
.
groups
.
delegate
.
addLast
(
newGroup
)
return
@
mapNotNull
BotJoinGroupEvent
(
newGroup
)
}
else
{
group
?:
return
@
mapNotNull
null
if
(
group
.
members
.
contains
(
msg
.
msgHead
.
authUin
))
{
return
@
mapNotNull
null
}
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
return
@
mapNotNull
MemberJoinEvent
.
Invite
(
group
.
newMember
(
msg
.
getNewMemberInfo
())
.
also
{
group
.
members
.
delegate
.
addLast
(
it
)
})
}
}
34
->
{
// 主动入群
// 27 0B 60 E7 01 44 71 47 90 03 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 36 42 35 35 46 45 32 45 35 36 43 45 45 44 30 38 30 35 31 41 35 42 37 36 39 35 34 45 30 46 43 43 36 36 45 44 43 46 45 43 42 39 33 41 41 44 32 32
val
group
=
bot
.
getGroupByUinOrNull
(
msg
.
msgHead
.
fromUin
)
group
?:
return
@
mapNotNull
null
if
(
group
.
members
.
contains
(
msg
.
msgHead
.
authUin
))
{
return
@
mapNotNull
null
}
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
return
@
mapNotNull
MemberJoinEvent
.
Active
(
group
.
newMember
(
msg
.
getNewMemberInfo
())
.
also
{
group
.
members
.
delegate
.
addLast
(
it
)
})
}
166
->
{
if
(
msg
.
msgHead
.
fromUin
==
bot
.
id
)
{
loop
@
while
(
true
)
{
val
instance
=
bot
.
client
.
getFriendSeq
()
if
(
instance
<
msg
.
msgHead
.
msgSeq
)
{
if
(
bot
.
client
.
setFriendSeq
(
instance
,
msg
.
msgHead
.
msgSeq
))
{
break
@
loop
}
}
else
break
@
loop
}
return
@
mapNotNull
null
}
val
friend
=
bot
.
getFriendOrNull
(
msg
.
msgHead
.
fromUin
)
?:
return
@
mapNotNull
null
friend
.
checkIsFriendImpl
()
if
(!
bot
.
firstLoginSucceed
)
{
return
@
mapNotNull
null
}
friend
.
lastMessageSequence
.
loop
{
instant
->
if
(
msg
.
msgHead
.
msgSeq
>
instant
)
{
if
(
friend
.
lastMessageSequence
.
compareAndSet
(
instant
,
msg
.
msgHead
.
msgSeq
))
{
return
@
mapNotNull
FriendMessageEvent
(
friend
,
msg
.
toMessageChain
(
bot
,
groupIdOrZero
=
0
,
onlineSource
=
true
),
msg
.
msgHead
.
msgTime
)
}
}
else
return
@
mapNotNull
null
}
}
208
->
{
// friend ptt
return
@
mapNotNull
null
}
529
->
{
// 好友文件
return
@
mapNotNull
null
}
141
->
{
val
tmpHead
=
msg
.
msgHead
.
c2cTmpMsgHead
?:
return
@
mapNotNull
null
val
member
=
bot
.
getGroupByUinOrNull
(
tmpHead
.
groupUin
)
?.
getOrNull
(
msg
.
msgHead
.
fromUin
)
?:
return
@
mapNotNull
null
member
.
checkIsMemberImpl
()
if
(
msg
.
msgHead
.
fromUin
==
bot
.
id
||
!
bot
.
firstLoginSucceed
)
{
return
@
mapNotNull
null
}
member
.
lastMessageSequence
.
loop
{
instant
->
if
(
msg
.
msgHead
.
msgSeq
>
instant
)
{
if
(
member
.
lastMessageSequence
.
compareAndSet
(
instant
,
msg
.
msgHead
.
msgSeq
))
{
return
@
mapNotNull
TempMessageEvent
(
member
,
msg
.
toMessageChain
(
bot
,
groupIdOrZero
=
0
,
onlineSource
=
true
,
isTemp
=
true
),
msg
.
msgHead
.
msgTime
)
}
}
else
return
@
mapNotNull
null
}
}
84
,
87
->
{
// 请求入群验证 和 被要求入群
bot
.
network
.
run
{
NewContact
.
SystemMsgNewGroup
(
bot
.
client
).
sendWithoutExpect
()
}
return
@
mapNotNull
null
}
187
->
{
// 请求加好友验证
bot
.
network
.
run
{
NewContact
.
SystemMsgNewFriend
(
bot
.
client
).
sendWithoutExpect
()
}
return
@
mapNotNull
null
}
// 732: 27 0B 60 E7 0C 01 3E 03 3F A2 5E 90 60 E2 00 01 44 71 47 90 00 00 02 58
else
->
{
bot
.
network
.
logger
.
debug
{
"unknown PbGetMsg type ${msg.msgHead.msgType}"
}
return
@
mapNotNull
null
}
}
}
val
list
:
List
<
Packet
>
=
messages
.
toList
()
if
(
resp
.
syncFlag
==
MsgSvc
.
SyncFlag
.
STOP
)
{
return
GetMsgSuccess
(
list
)
}
return
Response
(
resp
.
syncFlag
,
list
)
}
override
suspend
fun
QQAndroidBot
.
handle
(
packet
:
Response
)
{
when
(
packet
.
syncFlagFromServer
)
{
MsgSvc
.
SyncFlag
.
STOP
->
return
MsgSvc
.
SyncFlag
.
START
->
{
network
.
run
{
PbGetMsg
(
client
,
MsgSvc
.
SyncFlag
.
CONTINUE
,
currentTimeSeconds
).
sendAndExpect
<
Packet
>()
}
return
}
MsgSvc
.
SyncFlag
.
CONTINUE
->
{
network
.
run
{
PbGetMsg
(
client
,
MsgSvc
.
SyncFlag
.
CONTINUE
,
currentTimeSeconds
).
sendAndExpect
<
Packet
>()
}
return
}
}
}
}
/**
* 被挤下线
*/
internal
object
PushForceOffline
:
OutgoingPacketFactory
<
BotOfflineEvent
.
Force
>(
"MessageSvc.PushForceOffline"
)
{
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
):
BotOfflineEvent
.
Force
{
val
struct
=
this
.
readUniPacket
(
RequestPushForceOffline
.
serializer
())
return
BotOfflineEvent
.
Force
(
bot
,
title
=
struct
.
title
?:
""
,
message
=
struct
.
tips
?:
""
)
}
}
internal
object
PbSendMsg
:
OutgoingPacketFactory
<
PbSendMsg
.
Response
>(
"MessageSvc.PbSendMsg"
)
{
sealed
class
Response
:
Packet
{
object
SUCCESS
:
Response
()
{
override
fun
toString
():
String
=
"MessageSvc.PbSendMsg.Response.SUCCESS"
}
/**
* 121: 被限制? 个别号才不能发
*/
data class
Failed
(
val
resultType
:
Int
,
val
errorCode
:
Int
,
val
errorMessage
:
String
)
:
Response
()
{
override
fun
toString
():
String
=
"MessageSvc.PbSendMsg.Response.Failed(resultType=$resultType, errorCode=$errorCode, errorMessage=$errorMessage)"
}
}
inline
fun
createToFriend
(
client
:
QQAndroidClient
,
qq
:
Friend
,
message
:
MessageChain
,
crossinline
sourceCallback
:
(
MessageSourceToFriendImpl
)
->
Unit
):
OutgoingPacket
{
val
rand
=
Random
.
nextInt
().
absoluteValue
val
source
=
MessageSourceToFriendImpl
(
internalId
=
rand
,
sender
=
client
.
bot
,
target
=
qq
,
time
=
currentTimeSeconds
.
toInt
(),
sequenceId
=
client
.
nextFriendSeq
(),
originalMessage
=
message
)
sourceCallback
(
source
)
return
createToFriend
(
client
,
qq
.
id
,
message
,
source
)
}
/**
* 发送好友消息
*/
@Suppress
(
"FunctionName"
)
private
fun
createToFriend
(
client
:
QQAndroidClient
,
toUin
:
Long
,
message
:
MessageChain
,
source
:
MessageSourceToFriendImpl
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes())
///return@buildOutgoingUniPacket
writeProtoBuf
(
MsgSvc
.
PbSendMsgReq
.
serializer
(),
MsgSvc
.
PbSendMsgReq
(
routingHead
=
MsgSvc
.
RoutingHead
(
c2c
=
MsgSvc
.
C2C
(
toUin
=
toUin
)),
contentHead
=
MsgComm
.
ContentHead
(
pkgNum
=
1
),
msgBody
=
ImMsgBody
.
MsgBody
(
richText
=
ImMsgBody
.
RichText
(
elems
=
message
.
toRichTextElems
(
forGroup
=
false
,
withGeneralFlags
=
true
)
)
),
msgSeq
=
source
.
sequenceId
,
msgRand
=
source
.
internalId
,
syncCookie
=
SyncCookie
(
time
=
source
.
time
.
toLong
()).
toByteArray
(
SyncCookie
.
serializer
())
// msgVia = 1
)
)
}
inline
fun
createToTemp
(
client
:
QQAndroidClient
,
member
:
Member
,
message
:
MessageChain
,
sourceCallback
:
(
MessageSourceToTempImpl
)
->
Unit
):
OutgoingPacket
{
val
source
=
MessageSourceToTempImpl
(
internalId
=
Random
.
nextInt
().
absoluteValue
,
sender
=
client
.
bot
,
target
=
member
,
time
=
currentTimeSeconds
.
toInt
(),
sequenceId
=
client
.
atomicNextMessageSequenceId
(),
originalMessage
=
message
)
sourceCallback
(
source
)
return
createToTemp
(
client
,
(
member
.
group
as
GroupImpl
).
uin
,
member
.
id
,
message
,
source
)
}
/**
* 发送临时消息
*/
private
fun
createToTemp
(
client
:
QQAndroidClient
,
groupUin
:
Long
,
toUin
:
Long
,
message
:
MessageChain
,
source
:
MessageSourceToTempImpl
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
writeProtoBuf
(
MsgSvc
.
PbSendMsgReq
.
serializer
(),
MsgSvc
.
PbSendMsgReq
(
routingHead
=
MsgSvc
.
RoutingHead
(
grpTmp
=
MsgSvc
.
GrpTmp
(
groupUin
,
toUin
)
),
contentHead
=
MsgComm
.
ContentHead
(
pkgNum
=
1
),
msgBody
=
ImMsgBody
.
MsgBody
(
richText
=
ImMsgBody
.
RichText
(
elems
=
message
.
toRichTextElems
(
forGroup
=
false
,
withGeneralFlags
=
true
)
)
),
msgSeq
=
source
.
sequenceId
,
msgRand
=
source
.
internalId
,
syncCookie
=
SyncCookie
(
time
=
source
.
time
.
toLong
()).
toByteArray
(
SyncCookie
.
serializer
())
)
)
}
inline
fun
createToGroup
(
client
:
QQAndroidClient
,
group
:
Group
,
message
:
MessageChain
,
isForward
:
Boolean
,
sourceCallback
:
(
MessageSourceToGroupImpl
)
->
Unit
):
OutgoingPacket
{
val
source
=
MessageSourceToGroupImpl
(
group
,
internalId
=
Random
.
nextInt
().
absoluteValue
,
sender
=
client
.
bot
,
target
=
group
,
time
=
currentTimeSeconds
.
toInt
(),
originalMessage
=
message
//,
// sourceMessage = message
)
sourceCallback
(
source
)
return
createToGroup
(
client
,
group
.
id
,
message
,
isForward
,
source
)
}
/**
* 发送群消息
*/
@Suppress
(
"FunctionName"
)
private
fun
createToGroup
(
client
:
QQAndroidClient
,
groupCode
:
Long
,
message
:
MessageChain
,
isForward
:
Boolean
,
source
:
MessageSourceToGroupImpl
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes())
// DebugLogger.debug("sending group message: " + message.toRichTextElems().contentToString())
///return@buildOutgoingUniPacket
writeProtoBuf
(
MsgSvc
.
PbSendMsgReq
.
serializer
(),
MsgSvc
.
PbSendMsgReq
(
routingHead
=
MsgSvc
.
RoutingHead
(
grp
=
MsgSvc
.
Grp
(
groupCode
=
groupCode
)),
contentHead
=
MsgComm
.
ContentHead
(
pkgNum
=
1
),
msgBody
=
ImMsgBody
.
MsgBody
(
richText
=
ImMsgBody
.
RichText
(
elems
=
message
.
toRichTextElems
(
forGroup
=
true
,
withGeneralFlags
=
true
),
ptt
=
message
.
firstOrNull
(
PttMessage
)
?.
run
{
ImMsgBody
.
Ptt
(
fileName
=
fileName
.
toByteArray
(),
fileMd5
=
md5
)
}
)
),
msgSeq
=
client
.
atomicNextMessageSequenceId
(),
msgRand
=
source
.
internalId
,
syncCookie
=
EMPTY_BYTE_ARRAY
,
msgVia
=
1
,
msgCtrl
=
if
(
isForward
)
MsgCtrl
(
msgFlag
=
4
)
else
null
)
)
}
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
):
Response
{
val
response
=
readProtoBuf
(
MsgSvc
.
PbSendMsgResp
.
serializer
())
return
if
(
response
.
result
==
0
)
{
Response
.
SUCCESS
}
else
{
Response
.
Failed
(
response
.
result
,
response
.
errtype
,
response
.
errmsg
)
}
}
}
internal
object
Del
:
OutgoingPacketFactory
<
Nothing
?
>(
"MessageSvc.PbDeleteMsg"
)
{
internal
operator
fun
invoke
(
client
:
QQAndroidClient
,
items
:
List
<
MsgSvc
.
PbDeleteMsgReq
.
MsgItem
>)
=
buildOutgoingUniPacket
(
client
)
{
writeProtoBuf
(
MsgSvc
.
PbDeleteMsgReq
.
serializer
(),
MsgSvc
.
PbDeleteMsgReq
(
msgItems
=
items
)
)
}
internal
suspend
fun
delete
(
bot
:
QQAndroidBot
,
messages
:
Flow
<
MsgComm
.
Msg
>)
=
bot
.
network
.
run
{
val
map
=
messages
.
map
{
MsgSvc
.
PbDeleteMsgReq
.
MsgItem
(
fromUin
=
it
.
msgHead
.
fromUin
,
toUin
=
it
.
msgHead
.
toUin
,
// 群为84、好友为187。群通过其他方法删除,但测试结果显示通过187也能删除群消息。
msgType
=
187
,
msgSeq
=
it
.
msgHead
.
msgSeq
,
msgUid
=
it
.
msgHead
.
msgUid
)
}.
toList
()
Del
(
bot
.
client
,
map
).
sendWithoutExpect
()
}
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
)
=
null
}
}
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt
0 → 100644
View file @
1e885dbf
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.ByteReadPacket
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.contact.nameCardOrNick
import
net.mamoe.mirai.event.AbstractEvent
import
net.mamoe.mirai.event.Event
import
net.mamoe.mirai.event.broadcast
import
net.mamoe.mirai.event.events.MemberCardChangeEvent
import
net.mamoe.mirai.getGroupOrNull
import
net.mamoe.mirai.message.GroupMessageEvent
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.contact.GroupImpl
import
net.mamoe.mirai.qqandroid.contact.MemberImpl
import
net.mamoe.mirai.qqandroid.message.toMessageChain
import
net.mamoe.mirai.qqandroid.network.Packet
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgOnlinePush
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.Oidb0x8fc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import
net.mamoe.mirai.qqandroid.utils._miraiContentToString
import
net.mamoe.mirai.qqandroid.utils.encodeToString
import
net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
/**
* 接受群消息
*/
internal
object
OnlinePushPbPushGroupMsg
:
IncomingPacketFactory
<
Packet
?
>(
"OnlinePush.PbPushGroupMsg"
)
{
internal
class
SendGroupMessageReceipt
(
val
messageRandom
:
Int
,
val
sequenceId
:
Int
)
:
Packet
,
Event
,
Packet
.
NoLog
,
AbstractEvent
()
{
override
fun
toString
():
String
{
return
"OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt(messageRandom=$messageRandom, sequenceId=$sequenceId)"
}
}
@OptIn
(
ExperimentalStdlibApi
::
class
)
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
Packet
?
{
// 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00
if
(!
bot
.
firstLoginSucceed
)
return
null
val
pbPushMsg
=
readProtoBuf
(
MsgOnlinePush
.
PbPushMsg
.
serializer
())
if
(
pbPushMsg
.
msg
.
msgHead
.
fromUin
==
bot
.
id
)
{
return
SendGroupMessageReceipt
(
pbPushMsg
.
msg
.
msgBody
.
richText
.
attr
!!
.
random
,
pbPushMsg
.
msg
.
msgHead
.
msgSeq
)
}
var
extraInfo
:
ImMsgBody
.
ExtraInfo
?
=
null
var
anonymous
:
ImMsgBody
.
AnonymousGroupMsg
?
=
null
for
(
elem
in
pbPushMsg
.
msg
.
msgBody
.
richText
.
elems
)
{
when
{
elem
.
extraInfo
!=
null
->
extraInfo
=
elem
.
extraInfo
elem
.
anonGroupMsg
!=
null
->
anonymous
=
elem
.
anonGroupMsg
}
}
val
group
=
bot
.
getGroupOrNull
(
pbPushMsg
.
msg
.
msgHead
.
groupInfo
!!
.
groupCode
)
as
GroupImpl
?
?:
return
null
// 机器人还正在进群
val
sender
=
if
(
anonymous
!=
null
)
{
group
.
newAnonymous
(
anonymous
.
anonNick
.
encodeToString
())
}
else
{
group
[
pbPushMsg
.
msg
.
msgHead
.
fromUin
]
}
as
MemberImpl
val
name
=
if
(
anonymous
!=
null
)
{
sender
.
nameCard
}
else
{
extraInfo
?.
groupCard
?.
takeIf
{
it
.
isNotEmpty
()
}
?.
run
{
kotlin
.
runCatching
{
if
(
this
[
0
]
==
0
x0A
.
toByte
())
loadAs
(
Oidb0x8fc
.
CommCardNameBuf
.
serializer
()).
richCardName
?.
joinToString
(
""
)
{
it
.
text
.
encodeToString
()
}
else
return
@
runCatching
null
}.
getOrNull
()
?:
encodeToString
()
}
?:
pbPushMsg
.
msg
.
msgHead
.
groupInfo
.
groupCard
.
takeIf
{
it
.
isNotEmpty
()
}
?:
sender
.
nameCardOrNick
// 没有 extraInfo 就从 head 里取
}
val
flags
=
extraInfo
?.
flags
?:
0
return
GroupMessageEvent
(
senderName
=
name
.
also
{
if
(
it
!=
sender
.
nameCard
)
{
val
origin
=
sender
.
_nameCard
sender
.
_nameCard
=
name
MemberCardChangeEvent
(
origin
,
name
,
sender
).
broadcast
()
}
},
sender
=
sender
,
message
=
pbPushMsg
.
msg
.
toMessageChain
(
bot
,
groupIdOrZero
=
group
.
id
,
onlineSource
=
true
),
permission
=
when
{
flags
and
16
!=
0
->
MemberPermission
.
ADMINISTRATOR
flags
and
8
!=
0
->
MemberPermission
.
OWNER
flags
==
0
->
MemberPermission
.
MEMBER
else
->
{
bot
.
logger
.
warning
(
"判断群 ${sender.group.id} 的群员 ${sender.id} 的权限失败: ${pbPushMsg.msg.msgHead._miraiContentToString()}. 请完整截图或复制此日志并确认其真实权限后发送给 mirai 维护者以帮助解决问题."
)
sender
.
permission
}
},
time
=
pbPushMsg
.
msg
.
msgHead
.
msgTime
)
}
}
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushTransMsg.kt
0 → 100644
View file @
1e885dbf
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
@
file
:
OptIn
(
MiraiInternalAPI
::
class
,
MiraiExperimentalAPI
::
class
,
JavaFriendlyAPI
::
class
,
ExperimentalUnsignedTypes
::
class
)
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readUByte
import
kotlinx.io.core.readUInt
import
net.mamoe.mirai.JavaFriendlyAPI
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.event.events.BotGroupPermissionChangeEvent
import
net.mamoe.mirai.event.events.MemberLeaveEvent
import
net.mamoe.mirai.event.events.MemberPermissionChangeEvent
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.contact.GroupImpl
import
net.mamoe.mirai.qqandroid.contact.MemberImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl
import
net.mamoe.mirai.qqandroid.network.Packet
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.OnlinePushTrans
import
net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import
net.mamoe.mirai.qqandroid.utils.read
import
net.mamoe.mirai.utils.MiraiExperimentalAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
internal
object
OnlinePushPbPushTransMsg
:
IncomingPacketFactory
<
Packet
?
>(
"OnlinePush.PbPushTransMsg"
,
"OnlinePush.RespPush"
)
{
@OptIn
(
MiraiInternalAPI
::
class
)
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
Packet
?
{
val
content
=
this
.
readProtoBuf
(
OnlinePushTrans
.
PbMsgInfo
.
serializer
())
if
(!
bot
.
client
.
pbPushTransMsgCacheList
.
ensureNoDuplication
(
content
.
msgSeq
))
{
return
null
}
content
.
msgData
.
read
<
Unit
>
{
when
(
content
.
msgType
)
{
44
->
{
this
.
discardExact
(
5
)
val
var4
=
readByte
().
toInt
()
var
var5
=
0L
val
target
=
readUInt
().
toLong
()
if
(
var4
!=
0
&&
var4
!=
1
)
{
var5
=
readUInt
().
toLong
()
}
val
group
=
bot
.
getGroupByUin
(
content
.
fromUin
)
as
GroupImpl
if
(
var5
==
0L
&&
this
.
remaining
==
1L
)
{
//管理员变更
val
newPermission
=
if
(
this
.
readByte
().
toInt
()
==
1
)
MemberPermission
.
ADMINISTRATOR
else
MemberPermission
.
MEMBER
if
(
target
==
bot
.
id
)
{
if
(
group
.
botPermission
==
newPermission
)
{
return
null
}
return
BotGroupPermissionChangeEvent
(
group
,
group
.
botPermission
.
also
{
group
.
botAsMember
.
checkIsMemberImpl
().
permission
=
newPermission
},
newPermission
)
}
else
{
val
member
=
group
[
target
]
as
MemberImpl
if
(
member
.
permission
==
newPermission
)
{
return
null
}
return
MemberPermissionChangeEvent
(
member
,
member
.
permission
.
also
{
member
.
permission
=
newPermission
},
newPermission
)
}
}
}
34
->
{
/* quit
27 0B 60 E7
01
2F 55 7C B8
82
00 30 42 33 32 46 30 38 33 32 39 32 35 30 31 39 33 45 46 32 45 30 36 35 41 35 41 33 42 37 35 43 41 34 46 37 42 38 42 38 42 44 43 35 35 34 35 44 38 30
*/
/* kick
27 0B 60 E7
01
A8 32 51 A1
83 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 39 32 46 45 30 36 31 41 33 37 36 43 44 35 37 35 37 39 45 37 32 34 44 37 37 30 36 46 39 39 43 35 35 33 33 31 34 44 32 44 46 35 45 42 43 31 31 36
*/
readUInt
().
toLong
()
// group, uin or code ?
discardExact
(
1
)
val
target
=
readUInt
().
toLong
()
val
type
=
readUByte
().
toInt
()
val
operator
=
readUInt
().
toLong
()
val
groupUin
=
content
.
fromUin
when
(
type
)
{
0
x82
->
bot
.
getGroupByUinOrNull
(
groupUin
)
?.
let
{
group
->
val
member
=
group
.
getOrNull
(
target
)
as
?
MemberImpl
?:
return
null
return
MemberLeaveEvent
.
Quit
(
member
.
also
{
group
.
members
.
delegate
.
remove
(
member
)
})
}
0
x83
->
bot
.
getGroupByUin
(
groupUin
).
let
{
group
->
val
member
=
group
.
getOrNull
(
target
)
as
?
MemberImpl
?:
return
null
return
MemberLeaveEvent
.
Kick
(
member
.
also
{
group
.
members
.
delegate
.
remove
(
member
)
},
group
.
members
[
operator
])
}
}
}
}
}
return
null
}
override
suspend
fun
QQAndroidBot
.
handle
(
packet
:
Packet
?,
sequenceId
:
Int
):
OutgoingPacket
?
{
return
buildResponseUniPacket
(
client
,
sequenceId
=
sequenceId
)
{}
}
}
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt
0 → 100644
View file @
1e885dbf
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
@
file
:
OptIn
(
MiraiInternalAPI
::
class
,
MiraiExperimentalAPI
::
class
,
JavaFriendlyAPI
::
class
,
ExperimentalUnsignedTypes
::
class
)
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readBytes
import
kotlinx.io.core.readUInt
import
kotlinx.serialization.Serializable
import
net.mamoe.mirai.*
import
net.mamoe.mirai.data.FriendInfo
import
net.mamoe.mirai.event.events.*
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.contact.GroupImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsInstance
import
net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl
import
net.mamoe.mirai.qqandroid.message.contextualBugReportException
import
net.mamoe.mirai.qqandroid.network.MultiPacketBySequence
import
net.mamoe.mirai.qqandroid.network.Packet
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgInfo
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgType0x210
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.OnlinePushPack
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.Submsgtype0x27.SubMsgType0x27.*
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.Submsgtype0x44
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.Submsgtype0xb3
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.TroopTips0x857
import
net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePushReqPush.ignoredLambda528
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePushReqPush.lambda528
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePushReqPush.lambda732
import
net.mamoe.mirai.qqandroid.utils._miraiContentToString
import
net.mamoe.mirai.qqandroid.utils.encodeToString
import
net.mamoe.mirai.qqandroid.utils.io.ProtoBuf
import
net.mamoe.mirai.qqandroid.utils.io.readString
import
net.mamoe.mirai.qqandroid.utils.io.serialization.*
import
net.mamoe.mirai.qqandroid.utils.read
import
net.mamoe.mirai.qqandroid.utils.toUHexString
import
net.mamoe.mirai.utils.MiraiExperimentalAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.currentTimeSeconds
import
net.mamoe.mirai.utils.debug
//0C 01 B1 89 BE 09 5E 3D 72 A6 00 01 73 68 FC 06 00 00 00 3C
internal
object
OnlinePushReqPush
:
IncomingPacketFactory
<
OnlinePushReqPush
.
Response
>(
"OnlinePush.ReqPush"
,
"OnlinePush.RespPush"
)
{
// to reduce nesting depth
private
fun
List
<
MsgInfo
>.
deco
(
client
:
QQAndroidClient
,
mapper
:
ByteReadPacket
.(
msgInfo
:
MsgInfo
)
->
Sequence
<
Packet
>
):
Sequence
<
Packet
>
{
return
asSequence
().
filter
{
msg
->
client
.
onlinePushCacheList
.
ensureNoDuplication
(
msg
.
shMsgSeq
)
}.
flatMap
{
it
.
vMsg
.
read
{
mapper
(
it
)
}
}
}
@Suppress
(
"unused"
)
// bug
private
fun
lambda732
(
block
:
ByteReadPacket
.(
group
:
GroupImpl
,
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>):
ByteReadPacket
.(
group
:
GroupImpl
,
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>
{
return
block
}
private
fun
lambda528
(
block
:
MsgType0x210
.(
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>):
MsgType0x210
.(
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>
{
return
block
}
val
ignoredLambda528
:
MsgType0x210
.(
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>
=
lambda528
{
emptySequence
()
}
@ExperimentalUnsignedTypes
@OptIn
(
ExperimentalStdlibApi
::
class
)
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
Response
{
val
reqPushMsg
=
readUniPacket
(
OnlinePushPack
.
SvcReqPushMsg
.
serializer
(),
"req"
)
val
packets
:
Sequence
<
Packet
>
=
reqPushMsg
.
vMsgInfos
.
deco
(
bot
.
client
)
{
msgInfo
->
when
(
msgInfo
.
shMsgType
.
toInt
())
{
732
->
{
val
group
=
bot
.
getGroup
(
readUInt
().
toLong
())
GroupImpl
.
checkIsInstance
(
group
)
val
internalType
=
readByte
().
toInt
()
discardExact
(
1
)
Transformers732
[
internalType
]
?.
let
{
it
(
this
@
deco
,
group
,
bot
)
}
?:
kotlin
.
run
{
bot
.
network
.
logger
.
debug
{
"unknown group 732 type $internalType, data: "
+
readBytes
().
toUHexString
()
}
return
@
deco
emptySequence
()
}
}
// 00 27 1A 0C 1C 2C 3C 4C 5D 00 0C 6D 00 0C 7D 00 0C 8D 00 0C 9C AC BC CC DD 00 0C EC FC 0F 0B 2A 0C 1C 2C 3C 4C 5C 6C 0B 3A 0C 1C 2C 3C 4C 5C 6C 7C 8D 00 0C 9D 00 0C AC BD 00 0C CD 00 0C DC ED 00 0C FC 0F FC 10 0B 4A 0C 1C 2C 3C 4C 5C 6C 7C 8C 96 00 0B 5A 0C 1C 2C 3C 4C 5C 6C 7C 8C 9D 00 0C 0B 6A 0C 1A 0C 1C 26 00 0B 2A 0C 0B 3A 0C 16 00 0B 4A 09 0C 0B 5A 09 0C 0B 0B 7A 0C 1C 2C 36 00 0B 8A 0C 1C 2C 36 00 0B 9A 09 0C 0B AD 00 00 1E 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B
528
->
{
val
notifyMsgBody
=
readJceStruct
(
MsgType0x210
.
serializer
())
Transformers528
[
notifyMsgBody
.
uSubMsgType
]
?.
let
{
processor
->
processor
(
notifyMsgBody
,
bot
)
}
?:
kotlin
.
run
{
bot
.
network
.
logger
.
debug
{
// Network(1994701021) 16:03:54 : unknown group 528 type 0x0000000000000026, data: 08 01 12 40 0A 06 08 F4 EF BB 8F 04 10 E7 C1 AD B8 02 18 01 22 2C 10 01 1A 1A 18 B4 DC F8 9B 0C 20 E7 C1 AD B8 02 28 06 30 02 A2 01 04 08 93 D6 03 A8 01 08 20 00 28 00 32 08 18 01 20 FE AF AF F5 05 28 00
// VIP 进群提示
"unknown group 528 type 0x${notifyMsgBody.uSubMsgType.toUHexString("")}, data: "
+
notifyMsgBody
.
vProtobuf
.
toUHexString
()
}
return
@
deco
emptySequence
()
}
}
else
->
{
bot
.
network
.
logger
.
debug
{
"unknown sh type ${msgInfo.shMsgType.toInt()}"
}
bot
.
network
.
logger
.
debug
{
"data=${readBytes().toUHexString()}"
}
return
@
deco
emptySequence
()
}
}
}
return
Response
(
reqPushMsg
,
packets
)
}
@Suppress
(
"SpellCheckingInspection"
)
internal
data class
Response
(
val
request
:
OnlinePushPack
.
SvcReqPushMsg
,
val
sequence
:
Sequence
<
Packet
>)
:
MultiPacketBySequence
<
Packet
>(
sequence
)
{
override
fun
toString
():
String
{
return
"OnlinePush.ReqPush.Response"
}
}
override
suspend
fun
QQAndroidBot
.
handle
(
packet
:
Response
,
sequenceId
:
Int
):
OutgoingPacket
?
{
return
buildResponseUniPacket
(
client
)
{
writeJceStruct
(
RequestPacket
.
serializer
(),
RequestPacket
(
sServantName
=
"OnlinePush"
,
sFuncName
=
"SvcRespPushMsg"
,
iRequestId
=
sequenceId
,
sBuffer
=
jceRequestSBuffer
(
"resp"
,
OnlinePushPack
.
SvcRespPushMsg
.
serializer
(),
OnlinePushPack
.
SvcRespPushMsg
(
packet
.
request
.
uin
,
packet
.
request
.
vMsgInfos
.
map
{
msg
->
OnlinePushPack
.
DelMsgInfo
(
fromUin
=
msg
.
lFromUin
,
shMsgSeq
=
msg
.
shMsgSeq
,
vMsgCookies
=
msg
.
vMsgCookies
,
uMsgTime
=
msg
.
uMsgTime
// captured 0
)
}
)
)
)
)
}
}
}
private
object
Transformers732
:
Map
<
Int
,
ByteReadPacket
.(
GroupImpl
,
QQAndroidBot
)
-
>
Sequence
<
Packet
>>
by
mapOf
(
// mute
0
x0c
to
lambda732
{
group
:
GroupImpl
,
bot
:
QQAndroidBot
->
val
operatorUin
=
readUInt
().
toLong
()
if
(
operatorUin
==
bot
.
id
)
{
return
@
lambda732
emptySequence
()
}
val
operator
=
group
.
getOrNull
(
operatorUin
)
?:
return
@
lambda732
emptySequence
()
readUInt
().
toLong
()
// time
this
.
discardExact
(
2
)
val
target
=
readUInt
().
toLong
()
val
timeSeconds
=
readInt
()
if
(
target
==
0L
)
{
val
new
=
timeSeconds
!=
0
if
(
group
.
settings
.
isMuteAll
==
new
)
{
return
@
lambda732
emptySequence
()
}
group
.
_muteAll
=
new
return
@
lambda732
sequenceOf
(
GroupMuteAllEvent
(!
new
,
new
,
group
,
operator
))
}
if
(
target
==
bot
.
id
)
{
return
@
lambda732
when
{
group
.
botMuteRemaining
==
timeSeconds
->
emptySequence
()
timeSeconds
==
0
||
timeSeconds
==
0
xFFFF_FFFF
.
toInt
()
->
{
group
.
botAsMember
.
checkIsMemberImpl
().
_muteTimestamp
=
0
sequenceOf
(
BotUnmuteEvent
(
operator
))
}
else
->
{
group
.
botAsMember
.
checkIsMemberImpl
().
_muteTimestamp
=
currentTimeSeconds
.
toInt
()
+
timeSeconds
sequenceOf
(
BotMuteEvent
(
timeSeconds
,
operator
))
}
}
}
val
member
=
group
.
getOrNull
(
target
)
?:
return
@
lambda732
emptySequence
()
member
.
checkIsMemberImpl
()
if
(
member
.
_muteTimestamp
==
timeSeconds
)
{
return
@
lambda732
emptySequence
()
}
member
.
_muteTimestamp
=
timeSeconds
return
@
lambda732
if
(
timeSeconds
==
0
)
sequenceOf
(
MemberUnmuteEvent
(
member
,
operator
))
else
sequenceOf
(
MemberMuteEvent
(
member
,
timeSeconds
,
operator
))
},
// anonymous
0
x0e
to
lambda732
{
group
:
GroupImpl
,
_
:
QQAndroidBot
->
// 匿名
val
operator
=
group
.
getOrNull
(
readUInt
().
toLong
())
?:
return
@
lambda732
emptySequence
()
val
new
=
readInt
()
==
0
if
(
group
.
settings
.
isAnonymousChatEnabled
==
new
)
{
return
@
lambda732
emptySequence
()
}
group
.
_anonymousChat
=
new
return
@
lambda732
sequenceOf
(
GroupAllowAnonymousChatEvent
(!
new
,
new
,
group
,
operator
))
},
// 传字符串信息
0
x10
to
lambda732
{
group
:
GroupImpl
,
bot
:
QQAndroidBot
->
val
dataBytes
=
readBytes
(
26
)
when
(
dataBytes
[
0
].
toInt
())
{
59
->
{
// TODO 应该在 Transformers528 处理
val
size
=
readByte
().
toInt
()
// orthodox, don't `readUByte`
if
(
size
<
0
)
{
// java.lang.IllegalStateException: negative array size: -100, remaining bytes=B0 E6 99 90 D8 E8 02 98 06 01
// java.lang.IllegalStateException: negative array size: -121, remaining bytes=03 10 D9 F7 A2 93 0D 18 E0 DB E8 CA 0B 32 22 61 34 64 31 34 64 61 64 65 65 38 32 32 34 62 64 32 35 34 65 63 37 62 62 30 33 30 66 61 36 66 61 6D 6A 38 0E 48 00 58 01 70 C8 E8 9B 07 7A AD 02 3C 7B 22 69 63 6F 6E 22 3A 22 71 71 77 61 6C 6C 65 74 5F 63 75 73 74 6F 6D 5F 74 69 70 73 5F 69 64 69 6F 6D 5F 69 63 6F 6E 2E 70 6E 67 22 2C 22 61 6C 74 22 3A 22 22 7D 3E 3C 7B 22 63 6D 64 22 3A 31 2C 22 64 61 74 61 22 3A 22 6C 69 73 74 69 64 3D 31 30 30 30 30 34 35 32 30 31 32 30 30 34 30 38 31 32 30 30 31 30 39 36 31 32 33 31 34 35 30 30 26 67 72 6F 75 70 74 79 70 65 3D 31 22 2C 22 74 65 78 74 43 6F 6C 6F 72 22 3A 22 30 78 38 37 38 42 39 39 22 2C 22 74 65 78 74 22 3A 22 E6 8E A5 E9 BE 99 E7 BA A2 E5 8C 85 E4 B8 8B E4 B8 80 E4 B8 AA E6 8B BC E9 9F B3 EF BC 9A 22 7D 3E 3C 7B 22 63 6D 64 22 3A 31 2C 22 64 61 74 61 22 3A 22 6C 69 73 74 69 64 3D 31 30 30 30 30 34 35 32 30 31 32 30 30 34 30 38 31 32 30 30 31 30 39 36 31 32 33 31 34 35 30 30 26 67 72 6F 75 70 74 79 70 65 3D 31 22 2C 22 74 65 78 74 43 6F 6C 6F 72 22 3A 22 30 78 45 36 32 35 35 35 22 2C 22 74 65 78 74 22 3A 22 64 69 6E 67 22 7D 3E 82 01 0C E8 80 81 E5 83 A7 E5 85 A5 E5 AE 9A 88 01 03 92 01 04 64 69 6E 67 A0 01 00
// negative array size: -40, remaining bytes=D6 94 C3 8C D8 E8 02 98 06 01
error
(
"negative array size: $size, remaining bytes=${readBytes().toUHexString()}"
)
}
// println(dataBytes.toUHexString())
//println(message + ":" + dataBytes.toUHexString())
val
new
=
when
(
val
message
=
readString
(
size
))
{
"管理员已关闭群聊坦白说"
->
false
"管理员已开启群聊坦白说"
->
true
else
->
{
bot
.
network
.
logger
.
debug
{
"Unknown server messages $message"
}
return
@
lambda732
emptySequence
()
}
}
if
(
group
.
settings
.
isConfessTalkEnabled
==
new
)
{
return
@
lambda732
emptySequence
()
}
return
@
lambda732
sequenceOf
(
GroupAllowConfessTalkEvent
(
new
,
false
,
group
,
false
)
)
}
0
x2D
->
{
// 修改群名. 在 Transformers528 0x27L 处理
return
@
lambda732
emptySequence
()
}
else
->
{
/*
bot.network.logger.debug("unknown Transformer732 0xunknown type: ${dataBytes[0].toString(16)
.toUpperCase()}")
bot.network.logger.debug("unknown Transformer732 0xdata= ${readBytes().toUHexString()}")
*/
return
@
lambda732
emptySequence
()
/*
if (group.name == message) {
return@lambda732 emptySequence()
}
return@lambda732 sequenceOf(
GroupNameChangeEvent(
group.name.also { group._name = message },
message, group, false
)
)*/
}
}
},
// recall
0
x11
to
lambda732
{
group
:
GroupImpl
,
bot
:
QQAndroidBot
->
discardExact
(
1
)
val
proto
=
readProtoBuf
(
TroopTips0x857
.
NotifyMsgBody
.
serializer
())
val
recallReminder
=
proto
.
optMsgRecall
?:
return
@
lambda732
emptySequence
()
val
operator
=
if
(
recallReminder
.
uin
==
bot
.
id
)
group
.
botAsMember
else
group
.
getOrNull
(
recallReminder
.
uin
)
?:
return
@
lambda732
emptySequence
()
return
@
lambda732
recallReminder
.
recalledMsgList
.
asSequence
().
mapNotNull
{
pkg
->
when
{
pkg
.
authorUin
==
bot
.
id
&&
operator
.
id
==
bot
.
id
->
null
else
->
{
MessageRecallEvent
.
GroupRecall
(
bot
,
pkg
.
authorUin
,
pkg
.
seq
,
pkg
.
msgRandom
,
pkg
.
time
,
operator
,
group
)
}
}
}
}
)
// uSubMsgType to vProtobuf
// 138 or 139: top_package/akln.java:1568
// 66: top_package/nhz.java:269
/**
* @see MsgType0x210
*/
@OptIn
(
LowLevelAPI
::
class
,
MiraiInternalAPI
::
class
)
private
object
Transformers528
:
Map
<
Long
,
MsgType0x210
.(
QQAndroidBot
)
-
>
Sequence
<
Packet
>>
by
mapOf
(
// 提示共同好友
0
x111L
to
ignoredLambda528
,
// 新好友
0
xB3L
to
lambda528
{
bot
->
// 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07
val
body
=
vProtobuf
.
loadAs
(
Submsgtype0xb3
.
SubMsgType0xb3
.
MsgBody
.
serializer
())
val
new
=
bot
.
_lowLevelNewFriend
(
object
:
FriendInfo
{
override
val
uin
:
Long
get
()
=
body
.
msgAddFrdNotify
.
fuin
override
val
nick
:
String
get
()
=
body
.
msgAddFrdNotify
.
fuinNick
})
bot
.
friends
.
delegate
.
addLast
(
new
)
return
@
lambda528
sequenceOf
(
FriendAddEvent
(
new
))
},
0
xE2L
to
lambda528
{
// TODO: unknown. maybe messages.
// 0A 35 08 00 10 A2 FF 8C F0 03 1A 1B E5 90 8C E6 84 8F E4 BD A0 E7 9A 84 E5 8A A0 E5 A5 BD E5 8F 8B E8 AF B7 E6 B1 82 22 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B 28 01
// vProtobuf.loadAs(Msgtype0x210.serializer())
return
@
lambda528
emptySequence
()
},
0
x44L
to
lambda528
{
bot
->
val
msg
=
vProtobuf
.
loadAs
(
Submsgtype0x44
.
Submsgtype0x44
.
MsgBody
.
serializer
())
when
{
msg
.
msgCleanCountMsg
!=
null
->
{
}
msg
.
msgFriendMsgSync
!=
null
->
{
}
else
->
{
bot
.
network
.
logger
.
debug
{
"OnlinePush528 0x44L: "
+
msg
.
_miraiContentToString
()
}
}
}
return
@
lambda528
emptySequence
()
},
// bot 在其他客户端被踢或主动退出而同步情况
0
xD4L
to
lambda528
{
bot
->
@Serializable
data class
SubD4
(
// ok
val
uin
:
Long
)
:
ProtoBuf
val
uin
=
vProtobuf
.
loadAs
(
SubD4
.
serializer
()).
uin
val
group
=
bot
.
getGroupByUinOrNull
(
uin
)
?:
bot
.
getGroupOrNull
(
uin
)
return
@
lambda528
if
(
group
!=
null
&&
bot
.
groups
.
delegate
.
remove
(
group
))
{
sequenceOf
(
BotLeaveEvent
.
Active
(
group
))
}
else
emptySequence
()
},
// 群相关, ModFriendRemark, DelFriend, ModGroupProfile
0
x27L
to
lambda528
{
bot
->
fun
ModFriendRemark
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
{
return
this
.
msgFrdRmk
?.
asSequence
()
?.
mapNotNull
{
val
friend
=
bot
.
getFriendOrNull
(
it
.
fuin
)
?:
return
@
mapNotNull
null
// TODO: 2020/4/10 ADD REMARK QUERY
FriendRemarkChangeEvent
(
bot
,
friend
,
it
.
rmkName
)
}
?:
emptySequence
()
}
fun
DelFriend
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
{
return
this
.
uint64Uins
?.
asSequence
()
?.
mapNotNull
{
val
friend
=
bot
.
getFriendOrNull
(
it
)
?:
return
@
mapNotNull
null
if
(
bot
.
friends
.
delegate
.
remove
(
friend
))
{
FriendDeleteEvent
(
friend
)
}
else
null
}
?:
emptySequence
()
}
fun
ModGroupProfile
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
{
return
this
.
msgGroupProfileInfos
?.
asSequence
()
?.
mapNotNull
{
info
->
when
(
info
.
field
)
{
1
->
{
// 群名
val
new
=
info
.
value
.
encodeToString
()
val
group
=
bot
.
getGroupOrNull
(
this
.
groupCode
)
?:
return
@
mapNotNull
null
group
.
checkIsGroupImpl
()
val
old
=
group
.
name
if
(
new
==
old
)
return
@
mapNotNull
null
val
operator
=
if
(
this
.
cmdUin
==
bot
.
id
)
null
else
group
.
getOrNull
(
this
.
cmdUin
)
?:
return
@
mapNotNull
null
group
.
_name
=
new
return
@
mapNotNull
GroupNameChangeEvent
(
old
,
new
,
group
,
operator
)
}
2
->
{
// 头像
// top_package/akkz.java:3446
/*
var4 = var82.byteAt(0);
short var3 = (short) (var82.byteAt(1) | var4 << 8);
var85 = var18.method_77927(var7 + "");
var85.troopface = var3;
var85.hasSetNewTroopHead = true;
*/
bot
.
logger
.
debug
(
contextualBugReportException
(
"解析 Transformers528 0x27L ModGroupProfile 群头像修改"
,
forDebug
=
"this=${this._miraiContentToString()}"
))
null
}
3
->
{
// troop.credit.data
// top_package/akkz.java:3475
// top_package/akkz.java:3498
bot
.
logger
.
debug
(
contextualBugReportException
(
"解析 Transformers528 0x27L ModGroupProfile 群 troop.credit.data"
,
forDebug
=
"this=${this._miraiContentToString()}"
))
null
}
else
->
null
}
}
?:
emptySequence
()
}
fun
ModGroupMemberProfile
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
{
return
this
.
msgGroupMemberProfileInfos
?.
asSequence
()
?.
mapNotNull
{
info
->
when
(
info
.
field
)
{
1
->
{
// name card
val
new
=
info
.
value
val
group
=
bot
.
getGroupOrNull
(
this
.
groupCode
)
?:
return
@
mapNotNull
null
group
.
checkIsGroupImpl
()
val
member
=
group
.
getOrNull
(
this
.
uin
)
?:
return
@
mapNotNull
null
member
.
checkIsMemberImpl
()
val
old
=
member
.
nameCard
if
(
new
==
old
)
return
@
mapNotNull
null
member
.
_nameCard
=
new
return
@
mapNotNull
MemberCardChangeEvent
(
old
,
new
,
member
)
}
2
->
{
if
(
info
.
value
.
singleOrNull
()
?.
toInt
()
!=
0
)
{
bot
.
logger
.
debug
{
"Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}"
}
}
return
@
mapNotNull
null
}
else
->
{
bot
.
logger
.
debug
{
"Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}"
}
return
@
mapNotNull
null
}
}
}
?:
emptySequence
()
}
fun
ModCustomFace
.
transform
():
Sequence
<
Packet
>
=
sequenceOf
(
BotFaceChangedEvent
(
Bot
.
getInstance
(
uin
)))
return
@
lambda528
vProtobuf
.
loadAs
(
SubMsgType0x27MsgBody
.
serializer
()).
msgModInfos
.
asSequence
()
.
flatMap
{
when
{
it
.
msgModFriendRemark
!=
null
->
it
.
msgModFriendRemark
.
transform
(
bot
)
it
.
msgDelFriend
!=
null
->
it
.
msgDelFriend
.
transform
(
bot
)
it
.
msgModGroupProfile
!=
null
->
it
.
msgModGroupProfile
.
transform
(
bot
)
it
.
msgModGroupMemberProfile
!=
null
->
it
.
msgModGroupMemberProfile
.
transform
(
bot
)
it
.
msgModCustomFace
!=
null
->
it
.
msgModCustomFace
.
transform
()
else
->
{
bot
.
network
.
logger
.
debug
{
"Transformers528 0x27L: new data: ${it._miraiContentToString()}"
}
emptySequence
()
}
}
}
// 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B
}
)
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
deleted
100644 → 0
View file @
1c1a37a1
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"EXPERIMENTAL_UNSIGNED_LITERALS"
,
"EXPERIMENTAL_API_USAGE"
,
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.*
import
kotlinx.serialization.Serializable
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.contact.nameCardOrNick
import
net.mamoe.mirai.data.FriendInfo
import
net.mamoe.mirai.event.AbstractEvent
import
net.mamoe.mirai.event.Event
import
net.mamoe.mirai.event.broadcast
import
net.mamoe.mirai.event.events.*
import
net.mamoe.mirai.getFriendOrNull
import
net.mamoe.mirai.getGroupOrNull
import
net.mamoe.mirai.message.GroupMessageEvent
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.contact.*
import
net.mamoe.mirai.qqandroid.message.contextualBugReportException
import
net.mamoe.mirai.qqandroid.message.toMessageChain
import
net.mamoe.mirai.qqandroid.network.MultiPacketBySequence
import
net.mamoe.mirai.qqandroid.network.Packet
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgInfo
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgType0x210
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.OnlinePushPack
import
net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.*
import
net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import
net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket
import
net.mamoe.mirai.qqandroid.utils._miraiContentToString
import
net.mamoe.mirai.qqandroid.utils.encodeToString
import
net.mamoe.mirai.qqandroid.utils.io.ProtoBuf
import
net.mamoe.mirai.qqandroid.utils.io.readString
import
net.mamoe.mirai.qqandroid.utils.io.serialization.*
import
net.mamoe.mirai.qqandroid.utils.read
import
net.mamoe.mirai.qqandroid.utils.toUHexString
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.currentTimeSeconds
import
net.mamoe.mirai.utils.debug
internal
class
OnlinePush
{
/**
* 接受群消息
*/
internal
object
PbPushGroupMsg
:
IncomingPacketFactory
<
Packet
?
>(
"OnlinePush.PbPushGroupMsg"
)
{
internal
class
SendGroupMessageReceipt
(
val
messageRandom
:
Int
,
val
sequenceId
:
Int
)
:
Packet
,
Event
,
Packet
.
NoLog
,
AbstractEvent
()
{
override
fun
toString
():
String
{
return
"OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt(messageRandom=$messageRandom, sequenceId=$sequenceId)"
}
}
@OptIn
(
ExperimentalStdlibApi
::
class
)
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
Packet
?
{
// 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00
if
(!
bot
.
firstLoginSucceed
)
return
null
val
pbPushMsg
=
readProtoBuf
(
MsgOnlinePush
.
PbPushMsg
.
serializer
())
if
(
pbPushMsg
.
msg
.
msgHead
.
fromUin
==
bot
.
id
)
{
return
SendGroupMessageReceipt
(
pbPushMsg
.
msg
.
msgBody
.
richText
.
attr
!!
.
random
,
pbPushMsg
.
msg
.
msgHead
.
msgSeq
)
}
var
extraInfo
:
ImMsgBody
.
ExtraInfo
?
=
null
var
anonymous
:
ImMsgBody
.
AnonymousGroupMsg
?
=
null
for
(
elem
in
pbPushMsg
.
msg
.
msgBody
.
richText
.
elems
)
{
when
{
elem
.
extraInfo
!=
null
->
extraInfo
=
elem
.
extraInfo
elem
.
anonGroupMsg
!=
null
->
anonymous
=
elem
.
anonGroupMsg
}
}
val
group
=
bot
.
getGroupOrNull
(
pbPushMsg
.
msg
.
msgHead
.
groupInfo
!!
.
groupCode
)
as
GroupImpl
?
?:
return
null
// 机器人还正在进群
val
sender
=
if
(
anonymous
!=
null
)
{
group
.
newAnonymous
(
anonymous
.
anonNick
.
encodeToString
())
}
else
{
group
[
pbPushMsg
.
msg
.
msgHead
.
fromUin
]
}
as
MemberImpl
val
name
=
if
(
anonymous
!=
null
)
{
sender
.
nameCard
}
else
{
extraInfo
?.
groupCard
?.
takeIf
{
it
.
isNotEmpty
()
}
?.
run
{
kotlin
.
runCatching
{
if
(
this
[
0
]
==
0
x0A
.
toByte
())
loadAs
(
Oidb0x8fc
.
CommCardNameBuf
.
serializer
()).
richCardName
?.
joinToString
(
""
)
{
it
.
text
.
encodeToString
()
}
else
return
@
runCatching
null
}.
getOrNull
()
?:
encodeToString
()
}
?:
pbPushMsg
.
msg
.
msgHead
.
groupInfo
.
groupCard
.
takeIf
{
it
.
isNotEmpty
()
}
?:
sender
.
nameCardOrNick
// 没有 extraInfo 就从 head 里取
}
val
flags
=
extraInfo
?.
flags
?:
0
return
GroupMessageEvent
(
senderName
=
name
.
also
{
if
(
it
!=
sender
.
nameCard
)
{
val
origin
=
sender
.
_nameCard
sender
.
_nameCard
=
name
MemberCardChangeEvent
(
origin
,
name
,
sender
).
broadcast
()
}
},
sender
=
sender
,
message
=
pbPushMsg
.
msg
.
toMessageChain
(
bot
,
groupIdOrZero
=
group
.
id
,
onlineSource
=
true
),
permission
=
when
{
flags
and
16
!=
0
->
MemberPermission
.
ADMINISTRATOR
flags
and
8
!=
0
->
MemberPermission
.
OWNER
flags
==
0
->
MemberPermission
.
MEMBER
else
->
{
bot
.
logger
.
warning
(
"判断群 ${sender.group.id} 的群员 ${sender.id} 的权限失败: ${pbPushMsg.msg.msgHead._miraiContentToString()}. 请完整截图或复制此日志并确认其真实权限后发送给 mirai 维护者以帮助解决问题."
)
sender
.
permission
}
},
time
=
pbPushMsg
.
msg
.
msgHead
.
msgTime
)
}
}
internal
object
PbPushTransMsg
:
IncomingPacketFactory
<
Packet
?
>(
"OnlinePush.PbPushTransMsg"
,
"OnlinePush.RespPush"
)
{
@OptIn
(
MiraiInternalAPI
::
class
)
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
Packet
?
{
val
content
=
this
.
readProtoBuf
(
OnlinePushTrans
.
PbMsgInfo
.
serializer
())
if
(!
bot
.
client
.
pbPushTransMsgCacheList
.
ensureNoDuplication
(
content
.
msgSeq
))
{
return
null
}
content
.
msgData
.
read
<
Unit
>
{
when
(
content
.
msgType
)
{
44
->
{
this
.
discardExact
(
5
)
val
var4
=
readByte
().
toInt
()
var
var5
=
0L
val
target
=
readUInt
().
toLong
()
if
(
var4
!=
0
&&
var4
!=
1
)
{
var5
=
readUInt
().
toLong
()
}
val
group
=
bot
.
getGroupByUin
(
content
.
fromUin
)
as
GroupImpl
if
(
var5
==
0L
&&
this
.
remaining
==
1L
)
{
//管理员变更
val
newPermission
=
if
(
this
.
readByte
().
toInt
()
==
1
)
MemberPermission
.
ADMINISTRATOR
else
MemberPermission
.
MEMBER
if
(
target
==
bot
.
id
)
{
if
(
group
.
botPermission
==
newPermission
)
{
return
null
}
return
BotGroupPermissionChangeEvent
(
group
,
group
.
botPermission
.
also
{
group
.
botAsMember
.
checkIsMemberImpl
().
permission
=
newPermission
},
newPermission
)
}
else
{
val
member
=
group
[
target
]
as
MemberImpl
if
(
member
.
permission
==
newPermission
)
{
return
null
}
return
MemberPermissionChangeEvent
(
member
,
member
.
permission
.
also
{
member
.
permission
=
newPermission
},
newPermission
)
}
}
}
34
->
{
/* quit
27 0B 60 E7
01
2F 55 7C B8
82
00 30 42 33 32 46 30 38 33 32 39 32 35 30 31 39 33 45 46 32 45 30 36 35 41 35 41 33 42 37 35 43 41 34 46 37 42 38 42 38 42 44 43 35 35 34 35 44 38 30
*/
/* kick
27 0B 60 E7
01
A8 32 51 A1
83 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 39 32 46 45 30 36 31 41 33 37 36 43 44 35 37 35 37 39 45 37 32 34 44 37 37 30 36 46 39 39 43 35 35 33 33 31 34 44 32 44 46 35 45 42 43 31 31 36
*/
readUInt
().
toLong
()
// group, uin or code ?
discardExact
(
1
)
val
target
=
readUInt
().
toLong
()
val
type
=
readUByte
().
toInt
()
val
operator
=
readUInt
().
toLong
()
val
groupUin
=
content
.
fromUin
when
(
type
)
{
0
x82
->
bot
.
getGroupByUinOrNull
(
groupUin
)
?.
let
{
group
->
val
member
=
group
.
getOrNull
(
target
)
as
?
MemberImpl
?:
return
null
return
MemberLeaveEvent
.
Quit
(
member
.
also
{
group
.
members
.
delegate
.
remove
(
member
)
})
}
0
x83
->
bot
.
getGroupByUin
(
groupUin
).
let
{
group
->
val
member
=
group
.
getOrNull
(
target
)
as
?
MemberImpl
?:
return
null
return
MemberLeaveEvent
.
Kick
(
member
.
also
{
group
.
members
.
delegate
.
remove
(
member
)
},
group
.
members
[
operator
])
}
}
}
}
}
return
null
}
override
suspend
fun
QQAndroidBot
.
handle
(
packet
:
Packet
?,
sequenceId
:
Int
):
OutgoingPacket
?
{
return
buildResponseUniPacket
(
client
,
sequenceId
=
sequenceId
)
{}
}
}
//0C 01 B1 89 BE 09 5E 3D 72 A6 00 01 73 68 FC 06 00 00 00 3C
internal
object
ReqPush
:
IncomingPacketFactory
<
ReqPush
.
Response
>(
"OnlinePush.ReqPush"
,
"OnlinePush.RespPush"
)
{
// to reduce nesting depth
private
fun
List
<
MsgInfo
>.
deco
(
client
:
QQAndroidClient
,
mapper
:
ByteReadPacket
.(
msgInfo
:
MsgInfo
)
->
Sequence
<
Packet
>
):
Sequence
<
Packet
>
{
return
asSequence
().
filter
{
msg
->
client
.
onlinePushCacheList
.
ensureNoDuplication
(
msg
.
shMsgSeq
)
}.
flatMap
{
it
.
vMsg
.
read
{
mapper
(
it
)
}
}
}
private
fun
lambda732
(
block
:
ByteReadPacket
.(
group
:
GroupImpl
,
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>):
ByteReadPacket
.(
group
:
GroupImpl
,
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>
{
return
block
}
object
Transformers732
:
Map
<
Int
,
ByteReadPacket
.(
GroupImpl
,
QQAndroidBot
)
-
>
Sequence
<
Packet
>>
by
mapOf
(
// mute
0
x0c
to
lambda732
{
group
:
GroupImpl
,
bot
:
QQAndroidBot
->
val
operatorUin
=
readUInt
().
toLong
()
if
(
operatorUin
==
bot
.
id
)
{
return
@
lambda732
emptySequence
()
}
val
operator
=
group
.
getOrNull
(
operatorUin
)
?:
return
@
lambda732
emptySequence
()
readUInt
().
toLong
()
// time
this
.
discardExact
(
2
)
val
target
=
readUInt
().
toLong
()
val
timeSeconds
=
readInt
()
if
(
target
==
0L
)
{
val
new
=
timeSeconds
!=
0
if
(
group
.
settings
.
isMuteAll
==
new
)
{
return
@
lambda732
emptySequence
()
}
group
.
_muteAll
=
new
return
@
lambda732
sequenceOf
(
GroupMuteAllEvent
(!
new
,
new
,
group
,
operator
))
}
if
(
target
==
bot
.
id
)
{
return
@
lambda732
when
{
group
.
botMuteRemaining
==
timeSeconds
->
emptySequence
()
timeSeconds
==
0
||
timeSeconds
==
0
xFFFF_FFFF
.
toInt
()
->
{
group
.
botAsMember
.
checkIsMemberImpl
().
_muteTimestamp
=
0
sequenceOf
(
BotUnmuteEvent
(
operator
))
}
else
->
{
group
.
botAsMember
.
checkIsMemberImpl
().
_muteTimestamp
=
currentTimeSeconds
.
toInt
()
+
timeSeconds
sequenceOf
(
BotMuteEvent
(
timeSeconds
,
operator
))
}
}
}
val
member
=
group
.
getOrNull
(
target
)
?:
return
@
lambda732
emptySequence
()
member
.
checkIsMemberImpl
()
if
(
member
.
_muteTimestamp
==
timeSeconds
)
{
return
@
lambda732
emptySequence
()
}
member
.
_muteTimestamp
=
timeSeconds
return
@
lambda732
if
(
timeSeconds
==
0
)
sequenceOf
(
MemberUnmuteEvent
(
member
,
operator
))
else
sequenceOf
(
MemberMuteEvent
(
member
,
timeSeconds
,
operator
))
},
// anonymous
0
x0e
to
lambda732
{
group
:
GroupImpl
,
_
:
QQAndroidBot
->
// 匿名
val
operator
=
group
.
getOrNull
(
readUInt
().
toLong
())
?:
return
@
lambda732
emptySequence
()
val
new
=
readInt
()
==
0
if
(
group
.
settings
.
isAnonymousChatEnabled
==
new
)
{
return
@
lambda732
emptySequence
()
}
group
.
_anonymousChat
=
new
return
@
lambda732
sequenceOf
(
GroupAllowAnonymousChatEvent
(!
new
,
new
,
group
,
operator
))
},
// 传字符串信息
0
x10
to
lambda732
{
group
:
GroupImpl
,
bot
:
QQAndroidBot
->
val
dataBytes
=
readBytes
(
26
)
when
(
dataBytes
[
0
].
toInt
())
{
59
->
{
// TODO 应该在 Transformers528 处理
val
size
=
readByte
().
toInt
()
// orthodox, don't `readUByte`
if
(
size
<
0
)
{
// java.lang.IllegalStateException: negative array size: -100, remaining bytes=B0 E6 99 90 D8 E8 02 98 06 01
// java.lang.IllegalStateException: negative array size: -121, remaining bytes=03 10 D9 F7 A2 93 0D 18 E0 DB E8 CA 0B 32 22 61 34 64 31 34 64 61 64 65 65 38 32 32 34 62 64 32 35 34 65 63 37 62 62 30 33 30 66 61 36 66 61 6D 6A 38 0E 48 00 58 01 70 C8 E8 9B 07 7A AD 02 3C 7B 22 69 63 6F 6E 22 3A 22 71 71 77 61 6C 6C 65 74 5F 63 75 73 74 6F 6D 5F 74 69 70 73 5F 69 64 69 6F 6D 5F 69 63 6F 6E 2E 70 6E 67 22 2C 22 61 6C 74 22 3A 22 22 7D 3E 3C 7B 22 63 6D 64 22 3A 31 2C 22 64 61 74 61 22 3A 22 6C 69 73 74 69 64 3D 31 30 30 30 30 34 35 32 30 31 32 30 30 34 30 38 31 32 30 30 31 30 39 36 31 32 33 31 34 35 30 30 26 67 72 6F 75 70 74 79 70 65 3D 31 22 2C 22 74 65 78 74 43 6F 6C 6F 72 22 3A 22 30 78 38 37 38 42 39 39 22 2C 22 74 65 78 74 22 3A 22 E6 8E A5 E9 BE 99 E7 BA A2 E5 8C 85 E4 B8 8B E4 B8 80 E4 B8 AA E6 8B BC E9 9F B3 EF BC 9A 22 7D 3E 3C 7B 22 63 6D 64 22 3A 31 2C 22 64 61 74 61 22 3A 22 6C 69 73 74 69 64 3D 31 30 30 30 30 34 35 32 30 31 32 30 30 34 30 38 31 32 30 30 31 30 39 36 31 32 33 31 34 35 30 30 26 67 72 6F 75 70 74 79 70 65 3D 31 22 2C 22 74 65 78 74 43 6F 6C 6F 72 22 3A 22 30 78 45 36 32 35 35 35 22 2C 22 74 65 78 74 22 3A 22 64 69 6E 67 22 7D 3E 82 01 0C E8 80 81 E5 83 A7 E5 85 A5 E5 AE 9A 88 01 03 92 01 04 64 69 6E 67 A0 01 00
// negative array size: -40, remaining bytes=D6 94 C3 8C D8 E8 02 98 06 01
error
(
"negative array size: $size, remaining bytes=${readBytes().toUHexString()}"
)
}
// println(dataBytes.toUHexString())
//println(message + ":" + dataBytes.toUHexString())
val
new
=
when
(
val
message
=
readString
(
size
))
{
"管理员已关闭群聊坦白说"
->
false
"管理员已开启群聊坦白说"
->
true
else
->
{
bot
.
network
.
logger
.
debug
{
"Unknown server messages $message"
}
return
@
lambda732
emptySequence
()
}
}
if
(
group
.
settings
.
isConfessTalkEnabled
==
new
)
{
return
@
lambda732
emptySequence
()
}
return
@
lambda732
sequenceOf
(
GroupAllowConfessTalkEvent
(
new
,
false
,
group
,
false
)
)
}
0
x2D
->
{
// 修改群名. 在 Transformers528 0x27L 处理
return
@
lambda732
emptySequence
()
}
else
->
{
/*
bot.network.logger.debug("unknown Transformer732 0xunknown type: ${dataBytes[0].toString(16)
.toUpperCase()}")
bot.network.logger.debug("unknown Transformer732 0xdata= ${readBytes().toUHexString()}")
*/
return
@
lambda732
emptySequence
()
/*
if (group.name == message) {
return@lambda732 emptySequence()
}
return@lambda732 sequenceOf(
GroupNameChangeEvent(
group.name.also { group._name = message },
message, group, false
)
)*/
}
}
},
// recall
0
x11
to
lambda732
{
group
:
GroupImpl
,
bot
:
QQAndroidBot
->
discardExact
(
1
)
val
proto
=
readProtoBuf
(
TroopTips0x857
.
NotifyMsgBody
.
serializer
())
val
recallReminder
=
proto
.
optMsgRecall
?:
return
@
lambda732
emptySequence
()
val
operator
=
if
(
recallReminder
.
uin
==
bot
.
id
)
group
.
botAsMember
else
group
.
getOrNull
(
recallReminder
.
uin
)
?:
return
@
lambda732
emptySequence
()
return
@
lambda732
recallReminder
.
recalledMsgList
.
asSequence
().
mapNotNull
{
pkg
->
when
{
pkg
.
authorUin
==
bot
.
id
&&
operator
.
id
==
bot
.
id
->
null
else
->
{
MessageRecallEvent
.
GroupRecall
(
bot
,
pkg
.
authorUin
,
pkg
.
seq
,
pkg
.
msgRandom
,
pkg
.
time
,
operator
,
group
)
}
}
}
}
)
private
fun
lambda528
(
block
:
MsgType0x210
.(
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>):
MsgType0x210
.(
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>
{
return
block
}
val
ignoredLambda528
:
MsgType0x210
.(
bot
:
QQAndroidBot
)
->
Sequence
<
Packet
>
=
lambda528
{
emptySequence
()
}
// uSubMsgType to vProtobuf
// 138 or 139: top_package/akln.java:1568
// 66: top_package/nhz.java:269
/**
* @see MsgType0x210
*/
@OptIn
(
LowLevelAPI
::
class
,
MiraiInternalAPI
::
class
)
object
Transformers528
:
Map
<
Long
,
MsgType0x210
.(
QQAndroidBot
)
-
>
Sequence
<
Packet
>>
by
mapOf
(
// 提示共同好友
0
x111L
to
ignoredLambda528
,
// 新好友
0
xB3L
to
lambda528
{
bot
->
// 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07
val
body
=
vProtobuf
.
loadAs
(
Submsgtype0xb3
.
SubMsgType0xb3
.
MsgBody
.
serializer
())
val
new
=
bot
.
_lowLevelNewFriend
(
object
:
FriendInfo
{
override
val
uin
:
Long
get
()
=
body
.
msgAddFrdNotify
.
fuin
override
val
nick
:
String
get
()
=
body
.
msgAddFrdNotify
.
fuinNick
})
bot
.
friends
.
delegate
.
addLast
(
new
)
return
@
lambda528
sequenceOf
(
FriendAddEvent
(
new
))
},
0
xE2L
to
lambda528
{
// TODO: unknown. maybe messages.
// 0A 35 08 00 10 A2 FF 8C F0 03 1A 1B E5 90 8C E6 84 8F E4 BD A0 E7 9A 84 E5 8A A0 E5 A5 BD E5 8F 8B E8 AF B7 E6 B1 82 22 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B 28 01
// vProtobuf.loadAs(Msgtype0x210.serializer())
return
@
lambda528
emptySequence
()
},
0
x44L
to
lambda528
{
bot
->
val
msg
=
vProtobuf
.
loadAs
(
Submsgtype0x44
.
Submsgtype0x44
.
MsgBody
.
serializer
())
when
{
msg
.
msgCleanCountMsg
!=
null
->
{
}
msg
.
msgFriendMsgSync
!=
null
->
{
}
else
->
{
bot
.
network
.
logger
.
debug
{
"OnlinePush528 0x44L: "
+
msg
.
_miraiContentToString
()
}
}
}
return
@
lambda528
emptySequence
()
},
// bot 在其他客户端被踢或主动退出而同步情况
0
xD4L
to
lambda528
{
bot
->
@Serializable
data class
SubD4
(
// ok
val
uin
:
Long
)
:
ProtoBuf
val
uin
=
vProtobuf
.
loadAs
(
SubD4
.
serializer
()).
uin
val
group
=
bot
.
getGroupByUinOrNull
(
uin
)
?:
bot
.
getGroupOrNull
(
uin
)
return
@
lambda528
if
(
group
!=
null
&&
bot
.
groups
.
delegate
.
remove
(
group
))
{
sequenceOf
(
BotLeaveEvent
.
Active
(
group
))
}
else
emptySequence
()
},
// 群相关, ModFriendRemark, DelFriend, ModGroupProfile
0
x27L
to
lambda528
{
bot
->
fun
Submsgtype0x27
.
SubMsgType0x27
.
ModFriendRemark
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
{
return
this
.
msgFrdRmk
?.
asSequence
()
?.
mapNotNull
{
val
friend
=
bot
.
getFriendOrNull
(
it
.
fuin
)
?:
return
@
mapNotNull
null
// TODO: 2020/4/10 ADD REMARK QUERY
FriendRemarkChangeEvent
(
bot
,
friend
,
it
.
rmkName
)
}
?:
emptySequence
()
}
fun
Submsgtype0x27
.
SubMsgType0x27
.
DelFriend
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
{
return
this
.
uint64Uins
?.
asSequence
()
?.
mapNotNull
{
val
friend
=
bot
.
getFriendOrNull
(
it
)
?:
return
@
mapNotNull
null
if
(
bot
.
friends
.
delegate
.
remove
(
friend
))
{
FriendDeleteEvent
(
friend
)
}
else
null
}
?:
emptySequence
()
}
fun
Submsgtype0x27
.
SubMsgType0x27
.
ModGroupProfile
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
{
return
this
.
msgGroupProfileInfos
?.
asSequence
()
?.
mapNotNull
{
info
->
when
(
info
.
field
)
{
1
->
{
// 群名
val
new
=
info
.
value
.
encodeToString
()
val
group
=
bot
.
getGroupOrNull
(
this
.
groupCode
)
?:
return
@
mapNotNull
null
group
.
checkIsGroupImpl
()
val
old
=
group
.
name
if
(
new
==
old
)
return
@
mapNotNull
null
val
operator
=
if
(
this
.
cmdUin
==
bot
.
id
)
null
else
group
.
getOrNull
(
this
.
cmdUin
)
?:
return
@
mapNotNull
null
group
.
_name
=
new
return
@
mapNotNull
GroupNameChangeEvent
(
old
,
new
,
group
,
operator
)
}
2
->
{
// 头像
// top_package/akkz.java:3446
/*
var4 = var82.byteAt(0);
short var3 = (short) (var82.byteAt(1) | var4 << 8);
var85 = var18.method_77927(var7 + "");
var85.troopface = var3;
var85.hasSetNewTroopHead = true;
*/
bot
.
logger
.
debug
(
contextualBugReportException
(
"解析 Transformers528 0x27L ModGroupProfile 群头像修改"
,
forDebug
=
"this=${this._miraiContentToString()}"
))
null
}
3
->
{
// troop.credit.data
// top_package/akkz.java:3475
// top_package/akkz.java:3498
bot
.
logger
.
debug
(
contextualBugReportException
(
"解析 Transformers528 0x27L ModGroupProfile 群 troop.credit.data"
,
forDebug
=
"this=${this._miraiContentToString()}"
))
null
}
else
->
null
}
}
?:
emptySequence
()
}
fun
Submsgtype0x27
.
SubMsgType0x27
.
ModGroupMemberProfile
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
{
return
this
.
msgGroupMemberProfileInfos
?.
asSequence
()
?.
mapNotNull
{
info
->
when
(
info
.
field
)
{
1
->
{
// name card
val
new
=
info
.
value
val
group
=
bot
.
getGroupOrNull
(
this
.
groupCode
)
?:
return
@
mapNotNull
null
group
.
checkIsGroupImpl
()
val
member
=
group
.
getOrNull
(
this
.
uin
)
?:
return
@
mapNotNull
null
member
.
checkIsMemberImpl
()
val
old
=
member
.
nameCard
if
(
new
==
old
)
return
@
mapNotNull
null
member
.
_nameCard
=
new
return
@
mapNotNull
MemberCardChangeEvent
(
old
,
new
,
member
)
}
2
->
{
if
(
info
.
value
.
singleOrNull
()
?.
toInt
()
!=
0
)
{
bot
.
logger
.
debug
{
"Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}"
}
}
return
@
mapNotNull
null
}
else
->
{
bot
.
logger
.
debug
{
"Unknown Transformers528 0x27L ModGroupMemberProfile, field=${info.field}, value=${info.value}"
}
return
@
mapNotNull
null
}
}
}
?:
emptySequence
()
}
fun
Submsgtype0x27
.
SubMsgType0x27
.
ModCustomFace
.
transform
(
bot
:
QQAndroidBot
):
Sequence
<
Packet
>
=
sequenceOf
(
BotFaceChangedEvent
(
Bot
.
getInstance
(
uin
)))
return
@
lambda528
vProtobuf
.
loadAs
(
Submsgtype0x27
.
SubMsgType0x27
.
MsgBody
.
serializer
()).
msgModInfos
.
asSequence
()
.
flatMap
{
when
{
it
.
msgModFriendRemark
!=
null
->
it
.
msgModFriendRemark
.
transform
(
bot
)
it
.
msgDelFriend
!=
null
->
it
.
msgDelFriend
.
transform
(
bot
)
it
.
msgModGroupProfile
!=
null
->
it
.
msgModGroupProfile
.
transform
(
bot
)
it
.
msgModGroupMemberProfile
!=
null
->
it
.
msgModGroupMemberProfile
.
transform
(
bot
)
it
.
msgModCustomFace
!=
null
->
it
.
msgModCustomFace
.
transform
(
bot
)
else
->
{
bot
.
network
.
logger
.
debug
{
"Transformers528 0x27L: new data: ${it._miraiContentToString()}"
}
emptySequence
()
}
}
}
// 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B
}
)
@ExperimentalUnsignedTypes
@OptIn
(
ExperimentalStdlibApi
::
class
)
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
Response
{
val
reqPushMsg
=
readUniPacket
(
OnlinePushPack
.
SvcReqPushMsg
.
serializer
(),
"req"
)
val
packets
:
Sequence
<
Packet
>
=
reqPushMsg
.
vMsgInfos
.
deco
(
bot
.
client
)
{
msgInfo
->
when
(
msgInfo
.
shMsgType
.
toInt
())
{
732
->
{
val
group
=
bot
.
getGroup
(
readUInt
().
toLong
())
GroupImpl
.
checkIsInstance
(
group
)
val
internalType
=
readByte
().
toInt
()
discardExact
(
1
)
Transformers732
[
internalType
]
?.
let
{
it
(
this
@
deco
,
group
,
bot
)
}
?:
kotlin
.
run
{
bot
.
network
.
logger
.
debug
{
"unknown group 732 type $internalType, data: "
+
readBytes
().
toUHexString
()
}
return
@
deco
emptySequence
()
}
}
// 00 27 1A 0C 1C 2C 3C 4C 5D 00 0C 6D 00 0C 7D 00 0C 8D 00 0C 9C AC BC CC DD 00 0C EC FC 0F 0B 2A 0C 1C 2C 3C 4C 5C 6C 0B 3A 0C 1C 2C 3C 4C 5C 6C 7C 8D 00 0C 9D 00 0C AC BD 00 0C CD 00 0C DC ED 00 0C FC 0F FC 10 0B 4A 0C 1C 2C 3C 4C 5C 6C 7C 8C 96 00 0B 5A 0C 1C 2C 3C 4C 5C 6C 7C 8C 9D 00 0C 0B 6A 0C 1A 0C 1C 26 00 0B 2A 0C 0B 3A 0C 16 00 0B 4A 09 0C 0B 5A 09 0C 0B 0B 7A 0C 1C 2C 36 00 0B 8A 0C 1C 2C 36 00 0B 9A 09 0C 0B AD 00 00 1E 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B
528
->
{
val
notifyMsgBody
=
readJceStruct
(
MsgType0x210
.
serializer
())
Transformers528
[
notifyMsgBody
.
uSubMsgType
]
?.
let
{
processor
->
processor
(
notifyMsgBody
,
bot
)
}
?:
kotlin
.
run
{
bot
.
network
.
logger
.
debug
{
// Network(1994701021) 16:03:54 : unknown group 528 type 0x0000000000000026, data: 08 01 12 40 0A 06 08 F4 EF BB 8F 04 10 E7 C1 AD B8 02 18 01 22 2C 10 01 1A 1A 18 B4 DC F8 9B 0C 20 E7 C1 AD B8 02 28 06 30 02 A2 01 04 08 93 D6 03 A8 01 08 20 00 28 00 32 08 18 01 20 FE AF AF F5 05 28 00
// VIP 进群提示
"unknown group 528 type 0x${notifyMsgBody.uSubMsgType.toUHexString("")}, data: "
+
notifyMsgBody
.
vProtobuf
.
toUHexString
()
}
return
@
deco
emptySequence
()
}
}
else
->
{
bot
.
network
.
logger
.
debug
{
"unknown sh type ${msgInfo.shMsgType.toInt()}"
}
bot
.
network
.
logger
.
debug
{
"data=${readBytes().toUHexString()}"
}
return
@
deco
emptySequence
()
}
}
}
return
Response
(
reqPushMsg
,
packets
)
}
@Suppress
(
"SpellCheckingInspection"
)
internal
data class
Response
(
val
request
:
OnlinePushPack
.
SvcReqPushMsg
,
val
sequence
:
Sequence
<
Packet
>)
:
MultiPacketBySequence
<
Packet
>(
sequence
)
{
override
fun
toString
():
String
{
return
"OnlinePush.ReqPush.Response"
}
}
override
suspend
fun
QQAndroidBot
.
handle
(
packet
:
Response
,
sequenceId
:
Int
):
OutgoingPacket
?
{
return
buildResponseUniPacket
(
client
)
{
writeJceStruct
(
RequestPacket
.
serializer
(),
RequestPacket
(
sServantName
=
"OnlinePush"
,
sFuncName
=
"SvcRespPushMsg"
,
iRequestId
=
sequenceId
,
sBuffer
=
jceRequestSBuffer
(
"resp"
,
OnlinePushPack
.
SvcRespPushMsg
.
serializer
(),
OnlinePushPack
.
SvcRespPushMsg
(
packet
.
request
.
uin
,
packet
.
request
.
vMsgInfos
.
map
{
msg
->
OnlinePushPack
.
DelMsgInfo
(
fromUin
=
msg
.
lFromUin
,
shMsgSeq
=
msg
.
shMsgSeq
,
vMsgCookies
=
msg
.
vMsgCookies
,
uMsgTime
=
msg
.
uMsgTime
// captured 0
)
}
)
)
)
)
}
}
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
View file @
1e885dbf
...
...
@@ -155,7 +155,7 @@ sealed class MessageRecallEvent : BotEvent, AbstractEvent() {
override
val
messageInternalId
:
Int
,
override
val
messageTime
:
Int
,
/**
* 撤回操作人, 可能为 [Bot.
uin
] 或好友的 [User.id]
* 撤回操作人, 可能为 [Bot.
id
] 或好友的 [User.id]
*/
val
operator
:
Long
)
:
MessageRecallEvent
(),
Packet
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment