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
2b49a758
Commit
2b49a758
authored
Apr 04, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reconstruct MessageSource, fix #197, #133
parent
9f7088d4
Changes
25
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
616 additions
and
860 deletions
+616
-860
mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
...roidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
+0
-185
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt
...n/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt
+56
-55
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
...ain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
+8
-7
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
...in/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
+8
-4
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt
...onMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt
+8
-4
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceImpl.kt
...in/net/mamoe/mirai/qqandroid/message/MessageSourceImpl.kt
+174
-154
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/convension.kt
...in/kotlin/net/mamoe/mirai/qqandroid/message/convension.kt
+39
-22
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt
...ai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt
+6
-4
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
+31
-30
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
+7
-8
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/contentToString.kt
...kotlin/net/mamoe/mirai/qqandroid/utils/contentToString.kt
+5
-2
mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.jvm.kt
...Main/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.jvm.kt
+0
-0
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt
+0
-2
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
...roidMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
+24
-83
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+7
-13
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
...mmonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+3
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
...core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
+0
-33
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
...ommonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
+3
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
...commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
+4
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
...ommonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
+1
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt
...mmonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt
+52
-61
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
...Main/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
+148
-57
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt
...monMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt
+7
-49
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt
+1
-5
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
.../jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
+24
-74
No files found.
mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
deleted
100644 → 0
View file @
9f7088d4
/*
* 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
import
io.ktor.utils.io.ByteReadChannel
import
io.ktor.utils.io.consumeEachBufferRange
import
io.ktor.utils.io.core.Input
import
io.ktor.utils.io.core.readBytes
import
kotlinx.coroutines.io.*
import
kotlinx.io.core.*
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.qqandroid.utils.ByteArrayPool
import
net.mamoe.mirai.qqandroid.utils.toReadPacket
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
java.nio.ByteBuffer
@OptIn
(
MiraiInternalAPI
::
class
)
@Suppress
(
"DEPRECATION"
)
internal
actual
fun
ByteReadChannel
.
toKotlinByteReadChannel
():
kotlinx
.
coroutines
.
io
.
ByteReadChannel
{
return
object
: kotlinx.coroutines.io.
ByteReadChannel
{
override
val
availableForRead
:
Int
get
()
=
this
@
toKotlinByteReadChannel
.
availableForRead
override
val
isClosedForRead
:
Boolean
get
()
=
this
@
toKotlinByteReadChannel
.
isClosedForRead
override
val
isClosedForWrite
:
Boolean
get
()
=
this
@
toKotlinByteReadChannel
.
isClosedForWrite
@Suppress
(
"DEPRECATION_ERROR"
,
"OverridingDeprecatedMember"
)
override
var
readByteOrder
:
ByteOrder
get
()
=
when
(
this
@
toKotlinByteReadChannel
.
readByteOrder
)
{
io
.
ktor
.
utils
.
io
.
core
.
ByteOrder
.
BIG_ENDIAN
->
ByteOrder
.
BIG_ENDIAN
io
.
ktor
.
utils
.
io
.
core
.
ByteOrder
.
LITTLE_ENDIAN
->
ByteOrder
.
LITTLE_ENDIAN
}
set
(
value
)
{
this
@
toKotlinByteReadChannel
.
readByteOrder
=
when
(
value
)
{
ByteOrder
.
BIG_ENDIAN
->
io
.
ktor
.
utils
.
io
.
core
.
ByteOrder
.
BIG_ENDIAN
ByteOrder
.
LITTLE_ENDIAN
->
io
.
ktor
.
utils
.
io
.
core
.
ByteOrder
.
LITTLE_ENDIAN
}
}
@Suppress
(
"DEPRECATION_ERROR"
,
"DEPRECATION"
,
"OverridingDeprecatedMember"
)
override
val
totalBytesRead
:
Long
get
()
=
this
@
toKotlinByteReadChannel
.
totalBytesRead
override
fun
cancel
(
cause
:
Throwable
?):
Boolean
=
this
@
toKotlinByteReadChannel
.
cancel
(
cause
)
override
suspend
fun
consumeEachBufferRange
(
visitor
:
ConsumeEachBufferVisitor
)
=
this
@
toKotlinByteReadChannel
.
consumeEachBufferRange
(
visitor
)
override
suspend
fun
discard
(
max
:
Long
):
Long
=
this
@
toKotlinByteReadChannel
.
discard
(
max
)
@Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_OVERRIDE"
)
@ExperimentalIoApi
override
fun
<
R
>
lookAhead
(
visitor
:
LookAheadSession
.()
->
R
):
R
{
return
this
@
toKotlinByteReadChannel
.
lookAhead
l
@
{
visitor
(
object
:
LookAheadSession
{
override
fun
consumed
(
n
:
Int
)
{
return
this
@
l
.
consumed
(
n
)
}
override
fun
request
(
skip
:
Int
,
atLeast
:
Int
):
ByteBuffer
?
{
return
this
@
l
.
request
(
skip
,
atLeast
)
}
})
}
}
@Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_OVERRIDE"
)
@ExperimentalIoApi
override
suspend
fun
<
R
>
lookAheadSuspend
(
visitor
:
suspend
LookAheadSuspendSession
.()
->
R
):
R
=
this
@
toKotlinByteReadChannel
.
lookAheadSuspend
l
@
{
visitor
(
object
:
LookAheadSuspendSession
{
override
suspend
fun
awaitAtLeast
(
n
:
Int
):
Boolean
{
return
this
@
l
.
awaitAtLeast
(
n
)
}
override
fun
consumed
(
n
:
Int
)
{
return
this
@
l
.
consumed
(
n
)
}
override
fun
request
(
skip
:
Int
,
atLeast
:
Int
):
ByteBuffer
?
{
return
this
@
l
.
request
(
skip
,
atLeast
)
}
})
}
override
suspend
fun
read
(
min
:
Int
,
consumer
:
(
ByteBuffer
)
->
Unit
)
=
this
@
toKotlinByteReadChannel
.
read
(
min
,
consumer
)
override
suspend
fun
readAvailable
(
dst
:
ByteBuffer
):
Int
=
this
@
toKotlinByteReadChannel
.
readAvailable
(
dst
)
override
suspend
fun
readAvailable
(
dst
:
ByteArray
,
offset
:
Int
,
length
:
Int
):
Int
=
this
@
toKotlinByteReadChannel
.
readAvailable
(
dst
,
offset
,
length
)
override
suspend
fun
readAvailable
(
dst
:
IoBuffer
):
Int
{
ByteArrayPool
.
useInstance
{
val
read
=
this
@
toKotlinByteReadChannel
.
readAvailable
(
it
,
0
,
it
.
size
)
dst
.
writeFully
(
it
,
0
,
read
)
return
read
}
}
override
suspend
fun
readBoolean
():
Boolean
=
this
@
toKotlinByteReadChannel
.
readBoolean
()
override
suspend
fun
readByte
():
Byte
=
this
@
toKotlinByteReadChannel
.
readByte
()
override
suspend
fun
readDouble
():
Double
=
this
@
toKotlinByteReadChannel
.
readDouble
()
override
suspend
fun
readFloat
():
Float
=
this
@
toKotlinByteReadChannel
.
readFloat
()
override
suspend
fun
readFully
(
dst
:
ByteBuffer
):
Int
{
TODO
(
"not implemented"
)
}
override
suspend
fun
readFully
(
dst
:
ByteArray
,
offset
:
Int
,
length
:
Int
)
=
this
@
toKotlinByteReadChannel
.
readFully
(
dst
,
offset
,
length
)
override
suspend
fun
readFully
(
dst
:
IoBuffer
,
n
:
Int
)
{
ByteArrayPool
.
useInstance
{
dst
.
writeFully
(
it
,
0
,
this
.
readAvailable
(
it
,
0
,
it
.
size
))
}
}
override
suspend
fun
readInt
():
Int
=
this
@
toKotlinByteReadChannel
.
readInt
()
override
suspend
fun
readLong
():
Long
=
this
@
toKotlinByteReadChannel
.
readLong
()
override
suspend
fun
readPacket
(
size
:
Int
,
headerSizeHint
:
Int
):
ByteReadPacket
{
return
this
@
toKotlinByteReadChannel
.
readPacket
(
size
,
headerSizeHint
).
readBytes
().
toReadPacket
()
}
override
suspend
fun
readRemaining
(
limit
:
Long
,
headerSizeHint
:
Int
):
ByteReadPacket
{
return
this
@
toKotlinByteReadChannel
.
readRemaining
(
limit
,
headerSizeHint
).
readBytes
().
toReadPacket
()
}
@OptIn
(
ExperimentalIoApi
::
class
)
@ExperimentalIoApi
override
fun
readSession
(
consumer
:
ReadSession
.()
->
Unit
)
{
@Suppress
(
"DEPRECATION"
)
this
@
toKotlinByteReadChannel
.
readSession
lambda
@
{
consumer
(
object
:
ReadSession
{
override
val
availableForRead
:
Int
get
()
=
this
@
lambda
.
availableForRead
override
fun
discard
(
n
:
Int
):
Int
=
this
@
lambda
.
discard
(
n
)
override
fun
request
(
atLeast
:
Int
):
IoBuffer
?
{
val
ioBuffer
:
io
.
ktor
.
utils
.
io
.
core
.
IoBuffer
=
this
@
lambda
.
request
(
atLeast
)
?:
return
null
val
buffer
=
IoBuffer
.
Pool
.
borrow
()
val
bytes
=
(
ioBuffer
as
Input
).
readBytes
()
buffer
.
writeFully
(
bytes
)
return
buffer
}
})
}
}
override
suspend
fun
readShort
():
Short
=
this
@
toKotlinByteReadChannel
.
readShort
()
@Suppress
(
"EXPERIMENTAL_OVERRIDE"
,
"EXPERIMENTAL_API_USAGE"
)
@ExperimentalIoApi
override
suspend
fun
readSuspendableSession
(
consumer
:
suspend
SuspendableReadSession
.()
->
Unit
)
=
this
@
toKotlinByteReadChannel
.
readSuspendableSession
l
@
{
consumer
(
object
:
SuspendableReadSession
{
override
val
availableForRead
:
Int
get
()
=
this
@
l
.
availableForRead
override
suspend
fun
await
(
atLeast
:
Int
):
Boolean
=
this
@
l
.
await
(
atLeast
)
override
fun
discard
(
n
:
Int
):
Int
=
this
@
l
.
discard
(
n
)
override
fun
request
(
atLeast
:
Int
):
IoBuffer
?
{
@Suppress
(
"DuplicatedCode"
)
val
ioBuffer
:
io
.
ktor
.
utils
.
io
.
core
.
IoBuffer
=
this
@
l
.
request
(
atLeast
)
?:
return
null
val
buffer
=
IoBuffer
.
Pool
.
borrow
()
val
bytes
=
(
ioBuffer
as
Input
).
readBytes
()
buffer
.
writeFully
(
bytes
)
return
buffer
}
})
}
override
suspend
fun
readUTF8Line
(
limit
:
Int
):
String
?
=
this
@
toKotlinByteReadChannel
.
readUTF8Line
(
limit
)
override
suspend
fun
<
A
:
Appendable
>
readUTF8LineTo
(
out
:
A
,
limit
:
Int
):
Boolean
=
this
@
toKotlinByteReadChannel
.
readUTF8LineTo
(
out
,
limit
)
}
}
\ No newline at end of file
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
→
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.
common.
kt
View file @
2b49a758
...
...
@@ -24,6 +24,7 @@ import kotlinx.serialization.UnstableDefault
import
kotlinx.serialization.json.Json
import
kotlinx.serialization.json.JsonConfiguration
import
kotlinx.serialization.json.int
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.BotImpl
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.*
...
...
@@ -35,8 +36,7 @@ import net.mamoe.mirai.message.data.*
import
net.mamoe.mirai.qqandroid.contact.MemberInfoImpl
import
net.mamoe.mirai.qqandroid.contact.QQImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl
import
net.mamoe.mirai.qqandroid.message.OnlineFriendImageImpl
import
net.mamoe.mirai.qqandroid.message.OnlineGroupImageImpl
import
net.mamoe.mirai.qqandroid.message.*
import
net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
...
...
@@ -50,11 +50,22 @@ import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
import
net.mamoe.mirai.qqandroid.utils.toReadPacket
import
net.mamoe.mirai.utils.*
import
kotlin.collections.asSequence
import
kotlin.contracts.ExperimentalContracts
import
kotlin.contracts.contract
import
kotlin.coroutines.CoroutineContext
import
kotlin.jvm.JvmSynthetic
import
kotlin.math.absoluteValue
import
kotlin.random.Random
@OptIn
(
ExperimentalContracts
::
class
)
internal
fun
Bot
.
asQQAndroidBot
():
QQAndroidBot
{
contract
{
returns
()
implies
(
this
@
asQQAndroidBot
is
QQAndroidBot
)
}
return
this
as
QQAndroidBot
}
@OptIn
(
MiraiInternalAPI
::
class
)
internal
class
QQAndroidBot
constructor
(
context
:
Context
,
...
...
@@ -170,69 +181,58 @@ internal abstract class QQAndroidBotBase constructor(
TODO
(
"not implemented"
)
}
@Suppress
(
"RemoveExplicitTypeArguments"
)
// false positive
@ExperimentalMessageSource
override
suspend
fun
recall
(
source
:
MessageSource
)
{
if
(
source
.
senderId
!=
id
&&
source
.
groupId
!=
0L
)
{
getGroup
(
source
.
groupId
).
checkBotPermissionOperator
()
}
// println(source._miraiContentToString())
source
.
ensureSequenceIdAvailable
()
network
.
run
{
val
response
:
PbMessageSvc
.
PbMsgWithDraw
.
Response
=
if
(
source
.
groupId
==
0L
)
{
PbMessageSvc
.
PbMsgWithDraw
.
Friend
(
bot
.
client
,
source
.
senderId
,
source
.
sequenceId
,
source
.
messageRandom
,
source
.
time
).
sendAndExpect
()
}
else
{
MessageRecallEvent
.
GroupRecall
(
bot
,
source
.
senderId
,
source
.
id
,
source
.
time
.
toInt
(),
null
,
getGroup
(
source
.
groupId
)
).
broadcast
()
PbMessageSvc
.
PbMsgWithDraw
.
Group
(
bot
.
client
,
source
.
groupId
,
check
(
source
is
MessageSourceImpl
)
val
response
:
PbMessageSvc
.
PbMsgWithDraw
.
Response
=
when
(
source
)
{
is
MessageSourceToGroupImpl
,
is
MessageSourceFromGroupImpl
->
{
val
group
=
when
(
source
)
{
is
MessageSourceToGroupImpl
->
source
.
target
is
MessageSourceFromGroupImpl
->
source
.
group
else
->
error
(
"stub"
)
}
group
.
checkBotPermissionOperator
()
MessageRecallEvent
.
GroupRecall
(
this
,
source
.
senderId
,
source
.
id
,
source
.
time
,
null
,
group
).
broadcast
()
network
.
run
{
PbMessageSvc
.
PbMsgWithDraw
.
createForGroupMessage
(
bot
.
asQQAndroidBot
().
client
,
group
.
id
,
source
.
sequenceId
,
source
.
messageRandom
).
sendAndExpect
()
source
.
id
).
sendAndExpect
<
PbMessageSvc
.
PbMsgWithDraw
.
Response
>
()
}
check
(
response
is
PbMessageSvc
.
PbMsgWithDraw
.
Response
.
Success
)
{
"Failed to recall message #${source.id}: $response"
}
}
}
@OptIn
(
LowLevelAPI
::
class
)
override
suspend
fun
_lowLevelRecallFriendMessage
(
friendId
:
Long
,
messageId
:
Long
,
time
:
Long
)
{
network
.
run
{
val
response
:
PbMessageSvc
.
PbMsgWithDraw
.
Response
=
PbMessageSvc
.
PbMsgWithDraw
.
Friend
(
client
,
friendId
,
(
messageId
shr
32
).
toInt
(),
messageId
.
toInt
(),
time
)
.
sendAndExpect
()
check
(
response
is
PbMessageSvc
.
PbMsgWithDraw
.
Response
.
Success
)
{
"Failed to recall message #${messageId}: $response"
}
}
is
MessageSourceFromFriendImpl
,
is
MessageSourceToFriendImpl
->
network
.
run
{
PbMessageSvc
.
PbMsgWithDraw
.
createForFriendMessage
(
bot
.
client
,
source
.
senderId
,
source
.
sequenceId
,
source
.
id
,
source
.
time
).
sendAndExpect
<
PbMessageSvc
.
PbMsgWithDraw
.
Response
>
()
}
else
->
error
(
"stub"
)
}
}
@LowLevelAPI
override
suspend
fun
_lowLevelRecallGroupMessage
(
groupId
:
Long
,
messageId
:
Long
)
{
network
.
run
{
val
response
:
PbMessageSvc
.
PbMsgWithDraw
.
Response
=
PbMessageSvc
.
PbMsgWithDraw
.
Group
(
client
,
groupId
,
(
messageId
shr
32
).
toInt
(),
messageId
.
toInt
())
.
sendAndExpect
()
check
(
response
is
PbMessageSvc
.
PbMsgWithDraw
.
Response
.
Success
)
{
"Failed to recall message #${messageId}: $response"
}
}
check
(
response
is
PbMessageSvc
.
PbMsgWithDraw
.
Response
.
Success
)
{
"Failed to recall message #${source.id}: $response"
}
}
@LowLevelAPI
@MiraiExperimentalAPI
override
suspend
fun
_lowLevelGetAnnouncements
(
groupId
:
Long
,
page
:
Int
,
amount
:
Int
):
GroupAnnouncementList
{
...
...
@@ -382,6 +382,7 @@ internal abstract class QQAndroidBotBase constructor(
val
group
=
getGroup
(
groupCode
)
val
time
=
currentTimeSeconds
message
.
firstIsInstanceOrNull
<
QuoteReply
>()
?.
source
?.
ensureSequenceIdAvailable
()
network
.
run
{
val
data
=
message
.
calculateValidationDataForGroup
(
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
View file @
2b49a758
...
...
@@ -25,7 +25,9 @@ import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.data.*
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.message.MessageSourceFromSendGroup
import
net.mamoe.mirai.qqandroid.message.MessageSourceToGroupImpl
import
net.mamoe.mirai.qqandroid.message.ensureSequenceIdAvailable
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
...
...
@@ -303,18 +305,17 @@ internal class GroupImpl(
||
imageCount
>=
4
||
(
event
.
message
.
any
<
QuoteReply
>()
&&
(
imageCount
!=
0
||
length
>
100
))
)
{
return
bot
.
lowLevelSendLongGroupMessage
(
this
.
id
,
event
.
message
)
}
)
return
bot
.
lowLevelSendLongGroupMessage
(
this
.
id
,
event
.
message
)
msg
=
event
.
message
}
else
msg
=
message
.
asMessageChain
()
msg
.
firstIsInstanceOrNull
<
QuoteReply
>()
?.
source
?.
ensureSequenceIdAvailable
()
lateinit
var
source
:
MessageSource
FromSendGroup
lateinit
var
source
:
MessageSource
ToGroupImpl
bot
.
network
.
run
{
val
response
:
MessageSvc
.
PbSendMsg
.
Response
=
MessageSvc
.
PbSendMsg
.
ToGroup
(
val
response
:
MessageSvc
.
PbSendMsg
.
Response
=
MessageSvc
.
PbSendMsg
.
create
ToGroup
(
bot
.
client
,
id
,
this
@GroupImpl
,
msg
)
{
source
=
it
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
View file @
2b49a758
...
...
@@ -22,10 +22,13 @@ import net.mamoe.mirai.event.broadcast
import
net.mamoe.mirai.event.events.*
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.data.Message
import
net.mamoe.mirai.message.data.MessageSource
import
net.mamoe.mirai.message.data.OfflineFriendImage
import
net.mamoe.mirai.message.data.QuoteReply
import
net.mamoe.mirai.message.data.asMessageChain
import
net.mamoe.mirai.qqandroid.QQAndroidBot
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.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
...
...
@@ -63,12 +66,13 @@ internal class MemberImpl constructor(
if
(
event
.
isCancelled
)
{
throw
EventCancelledException
(
"cancelled by FriendMessageSendEvent"
)
}
lateinit
var
source
:
MessageSource
lateinit
var
source
:
MessageSourceToFriendImpl
event
.
message
.
firstIsInstanceOrNull
<
QuoteReply
>()
?.
source
?.
ensureSequenceIdAvailable
()
bot
.
network
.
run
{
check
(
MessageSvc
.
PbSendMsg
.
ToFriend
(
MessageSvc
.
PbSendMsg
.
create
ToFriend
(
bot
.
client
,
id
,
this
@MemberImpl
,
event
.
message
)
{
source
=
it
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt
View file @
2b49a758
...
...
@@ -29,10 +29,13 @@ import net.mamoe.mirai.event.events.ImageUploadEvent
import
net.mamoe.mirai.event.events.MessageSendEvent
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.data.Message
import
net.mamoe.mirai.message.data.MessageSource
import
net.mamoe.mirai.message.data.OfflineFriendImage
import
net.mamoe.mirai.message.data.QuoteReply
import
net.mamoe.mirai.message.data.asMessageChain
import
net.mamoe.mirai.qqandroid.QQAndroidBot
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.highway.postImage
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352
import
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn
...
...
@@ -80,12 +83,13 @@ internal class QQImpl(
if
(
event
.
isCancelled
)
{
throw
EventCancelledException
(
"cancelled by FriendMessageSendEvent"
)
}
lateinit
var
source
:
MessageSource
event
.
message
.
firstIsInstanceOrNull
<
QuoteReply
>()
?.
source
?.
ensureSequenceIdAvailable
()
lateinit
var
source
:
MessageSourceToFriendImpl
bot
.
network
.
run
{
check
(
MessageSvc
.
PbSendMsg
.
ToFriend
(
MessageSvc
.
PbSendMsg
.
create
ToFriend
(
bot
.
client
,
id
,
this
@QQImpl
,
event
.
message
)
{
source
=
it
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceImpl.kt
View file @
2b49a758
This diff is collapsed.
Click to expand it.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/convension.kt
View file @
2b49a758
...
...
@@ -14,8 +14,8 @@ package net.mamoe.mirai.qqandroid.message
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readUInt
import
kotlinx.io.core.toByteArray
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.Member
import
net.mamoe.mirai.message.data.*
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.HummerCommelem
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
...
...
@@ -42,9 +42,11 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
if
(
this
.
any
<
QuoteReply
>())
{
when
(
val
source
=
this
[
QuoteReply
].
source
)
{
is
MessageSourceFromServer
->
elements
.
add
(
ImMsgBody
.
Elem
(
srcMsg
=
source
.
delegate
))
is
MessageSourceFromMsg
->
elements
.
add
(
ImMsgBody
.
Elem
(
srcMsg
=
source
.
toJceData
()))
is
MessageSourceFromSend
->
elements
.
add
(
ImMsgBody
.
Elem
(
srcMsg
=
source
.
toJceData
()))
is
OfflineMessageSourceImpl
->
elements
.
add
(
ImMsgBody
.
Elem
(
srcMsg
=
source
.
delegate
))
is
MessageSourceToFriendImpl
->
elements
.
add
(
ImMsgBody
.
Elem
(
srcMsg
=
source
.
toJceDataImplForFriend
()))
is
MessageSourceToGroupImpl
->
elements
.
add
(
ImMsgBody
.
Elem
(
srcMsg
=
source
.
toJceDataImplForGroup
()))
is
MessageSourceFromFriendImpl
->
elements
.
add
(
ImMsgBody
.
Elem
(
srcMsg
=
source
.
toJceDataImplForFriend
()))
is
MessageSourceFromGroupImpl
->
elements
.
add
(
ImMsgBody
.
Elem
(
srcMsg
=
source
.
toJceDataImplForGroup
()))
else
->
error
(
"unsupported MessageSource implementation: ${source::class.simpleName}"
)
}
}
...
...
@@ -122,19 +124,17 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
.
also
{
transformOneMessage
(
UNSUPPORTED_FLASH_MESSAGE_PLAIN
)
}
is
AtAll
->
elements
.
add
(
atAllData
)
is
Face
->
elements
.
add
(
ImMsgBody
.
Elem
(
face
=
it
.
toJceData
()))
is
QuoteReply
ToSend
->
{
is
QuoteReply
->
{
if
(
forGroup
)
{
check
(
it
is
QuoteReplyToSend
.
ToGroup
)
{
"sending a quote to group using QuoteReplyToSend.ToFriend is prohibited"
}
if
(
it
.
sender
is
Member
)
{
transformOneMessage
(
it
.
createAt
())
when
(
val
source
=
it
.
source
)
{
is
OnlineMessageSource
.
Incoming
.
FromGroup
->
{
transformOneMessage
(
At
(
source
.
sender
))
transformOneMessage
(
PlainText
(
" "
))
}
}
transformOneMessage
(
PlainText
(
" "
))
}
}
is
QuoteReply
,
// already transformed above
is
MessageSource
,
// mirai only
is
MessageSource
,
// mirai metadata only
is
RichMessage
// already transformed above
->
{
...
...
@@ -173,28 +173,36 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
private
val
PB_RESERVE_FOR_RICH_MESSAGE
=
"08 09 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 C8 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 02 08 03 90 04 80 80 80 10 B8 04 00 C0 04 00"
.
hexToBytes
()
@Suppress
(
"SpellCheckingInspection"
)
private
val
PB_RESERVE_FOR_DOUTU
=
"78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00"
.
hexToBytes
()
private
val
PB_RESERVE_FOR_ELSE
=
"78 00 F8 01 00 C8 02 00"
.
hexToBytes
()
@OptIn
(
ExperimentalUnsignedTypes
::
class
,
MiraiInternalAPI
::
class
)
internal
fun
MsgComm
.
Msg
.
toMessageChain
():
MessageChain
{
internal
fun
MsgComm
.
Msg
.
toMessageChain
(
bot
:
Bot
,
isGroup
:
Boolean
,
addSource
:
Boolean
):
MessageChain
{
val
elements
=
this
.
msgBody
.
richText
.
elems
return
buildMessageChain
(
elements
.
size
+
1
)
{
+
MessageSourceFromMsg
(
delegate
=
this
@
toMessageChain
)
elements
.
joinToMessageChain
(
this
)
if
(
addSource
)
{
if
(
isGroup
)
{
+
MessageSourceFromGroupImpl
(
bot
,
this
@
toMessageChain
)
}
else
{
+
MessageSourceFromFriendImpl
(
bot
,
this
@
toMessageChain
)
}
}
elements
.
joinToMessageChain
(
bot
,
this
)
}.
cleanupRubbishMessageElements
()
}
// These two functions have difference method signature, don't combine.
@OptIn
(
ExperimentalUnsignedTypes
::
class
,
MiraiInternalAPI
::
class
)
internal
fun
ImMsgBody
.
SourceMsg
.
toMessageChain
():
MessageChain
{
internal
fun
ImMsgBody
.
SourceMsg
.
toMessageChain
(
bot
:
Bot
):
MessageChain
{
val
elements
=
this
.
elems
!!
return
buildMessageChain
(
elements
.
size
+
1
)
{
+
MessageSourceFromServer
(
delegate
=
this
@
toMessageChain
)
elements
.
joinToMessageChain
(
this
)
+
OfflineMessageSourceImpl
(
delegate
=
this
@
toMessageChain
,
bot
=
bot
)
elements
.
joinToMessageChain
(
bot
,
this
)
}.
cleanupRubbishMessageElements
()
}
...
...
@@ -228,7 +236,7 @@ private fun MessageChain.cleanupRubbishMessageElements(): MessageChain {
}
internal
inline
fun
<
reified
R
>
Iterable
<*>.
firstIsInstance
():
R
{
this
.
forEach
{
for
(
it
in
this
)
{
if
(
it
is
R
)
{
return
it
}
...
...
@@ -236,12 +244,21 @@ internal inline fun <reified R> Iterable<*>.firstIsInstance(): R {
throw
NoSuchElementException
(
"Collection contains no element matching the predicate."
)
}
internal
inline
fun
<
reified
R
>
Iterable
<*>.
firstIsInstanceOrNull
():
R
?
{
for
(
it
in
this
)
{
if
(
it
is
R
)
{
return
it
}
}
return
null
}
@OptIn
(
MiraiInternalAPI
::
class
,
LowLevelAPI
::
class
)
internal
fun
List
<
ImMsgBody
.
Elem
>.
joinToMessageChain
(
message
:
MessageChainBuilder
)
{
internal
fun
List
<
ImMsgBody
.
Elem
>.
joinToMessageChain
(
bot
:
Bot
,
message
:
MessageChainBuilder
)
{
// (this._miraiContentToString())
this
.
forEach
{
when
{
it
.
srcMsg
!=
null
->
message
.
add
(
QuoteReply
(
MessageSourceFromServer
(
it
.
srcMsg
)))
it
.
srcMsg
!=
null
->
message
.
add
(
QuoteReply
(
OfflineMessageSourceImpl
(
it
.
srcMsg
,
bot
)))
it
.
notOnlineImage
!=
null
->
message
.
add
(
OnlineFriendImageImpl
(
it
.
notOnlineImage
))
it
.
customFace
!=
null
->
message
.
add
(
OnlineGroupImageImpl
(
it
.
customFace
))
it
.
face
!=
null
->
message
.
add
(
Face
(
it
.
face
.
index
))
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt
View file @
2b49a758
...
...
@@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat
import
kotlinx.io.core.ByteReadPacket
...
...
@@ -40,7 +42,7 @@ internal class PbMessageSvc {
}
// 12 1A 08 01 10 00 18 E7 C1 AD B8 02 22 0A 08 BF BA 03 10 BF 81 CB B7 03 2A 02 08 00
fun
Group
(
fun
createForGroupMessage
(
client
:
QQAndroidClient
,
groupCode
:
Long
,
messageSequenceId
:
Int
,
// 56639
...
...
@@ -71,12 +73,12 @@ internal class PbMessageSvc {
)
}
fun
Friend
(
fun
createForFriendMessage
(
client
:
QQAndroidClient
,
toUin
:
Long
,
messageSequenceId
:
Int
,
// 56639
messageRandom
:
Int
,
// 921878719
time
:
Long
time
:
Int
):
OutgoingPacket
=
buildOutgoingUniPacket
(
client
)
{
val
messageUid
:
Long
=
262144L
.
shl
(
32
)
or
messageRandom
.
toLong
().
and
(
0
xffFFffFF
)
writeProtoBuf
(
...
...
@@ -91,7 +93,7 @@ internal class PbMessageSvc {
toUin
=
toUin
,
msgSeq
=
messageSequenceId
,
msgUid
=
messageUid
,
msgTime
=
time
and
0
xffffffff
,
msgTime
=
time
.
toULong
().
toLong
()
,
routingHead
=
MsgSvc
.
RoutingHead
(
c2c
=
MsgSvc
.
C2C
(
toUin
=
toUin
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
View file @
2b49a758
...
...
@@ -8,6 +8,7 @@
*/
@
file
:
OptIn
(
LowLevelAPI
::
class
)
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
...
...
@@ -19,6 +20,7 @@ import kotlinx.io.core.discardExact
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.data.MemberInfo
import
net.mamoe.mirai.event.events.BotJoinGroupEvent
import
net.mamoe.mirai.event.events.BotOfflineEvent
...
...
@@ -29,8 +31,8 @@ import net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.contact.GroupImpl
import
net.mamoe.mirai.qqandroid.contact.checkIsQQImpl
import
net.mamoe.mirai.qqandroid.message.MessageSource
FromSendFriend
import
net.mamoe.mirai.qqandroid.message.MessageSource
FromSendGroup
import
net.mamoe.mirai.qqandroid.message.MessageSource
ToFriendImpl
import
net.mamoe.mirai.qqandroid.message.MessageSource
ToGroupImpl
import
net.mamoe.mirai.qqandroid.message.toMessageChain
import
net.mamoe.mirai.qqandroid.message.toRichTextElems
import
net.mamoe.mirai.qqandroid.network.MultiPacketByIterable
...
...
@@ -82,6 +84,7 @@ internal class MessageSvc {
*/
@OptIn
(
MiraiInternalAPI
::
class
)
internal
object
PbGetMsg
:
OutgoingPacketFactory
<
PbGetMsg
.
Response
>(
"MessageSvc.PbGetMsg"
)
{
@Suppress
(
"SpellCheckingInspection"
)
operator
fun
invoke
(
client
:
QQAndroidClient
,
syncFlag
:
MsgSvc
.
SyncFlag
=
MsgSvc
.
SyncFlag
.
START
,
...
...
@@ -226,7 +229,7 @@ internal class MessageSvc {
if
(
friend
.
lastMessageSequence
.
compareAndSet
(
instant
,
msg
.
msgHead
.
msgSeq
))
{
return
@
mapNotNull
FriendMessage
(
friend
,
msg
.
toMessageChain
()
msg
.
toMessageChain
(
bot
,
isGroup
=
false
,
addSource
=
true
)
)
}
}
else
return
@
mapNotNull
null
...
...
@@ -289,34 +292,33 @@ internal class MessageSvc {
}
}
inline
fun
ToFriend
(
inline
fun
create
ToFriend
(
client
:
QQAndroidClient
,
toUin
:
Long
,
qq
:
QQ
,
message
:
MessageChain
,
crossinline
sourceCallback
:
(
MessageSource
FromSendFriend
)
->
Unit
crossinline
sourceCallback
:
(
MessageSource
ToFriendImpl
)
->
Unit
):
OutgoingPacket
{
val
source
=
MessageSourceFromSendFriend
(
messageRandom
=
Random
.
nextInt
().
absoluteValue
,
senderId
=
client
.
uin
,
toUin
=
toUin
,
time
=
currentTimeSeconds
,
groupId
=
0
,
val
source
=
MessageSourceToFriendImpl
(
id
=
Random
.
nextInt
().
absoluteValue
,
sender
=
client
.
bot
,
target
=
qq
,
time
=
currentTimeSeconds
.
toInt
(),
sequenceId
=
client
.
atomicNextMessageSequenceId
(),
originalMessage
=
message
)
sourceCallback
(
source
)
return
ToFriend
(
client
,
toUin
,
message
,
source
)
return
createToFriend
(
client
,
qq
.
id
,
message
,
source
)
}
/**
* 发送好友消息
*/
@Suppress
(
"FunctionName"
)
private
fun
ToFriend
(
private
fun
create
ToFriend
(
client
:
QQAndroidClient
,
toUin
:
Long
,
message
:
MessageChain
,
source
:
MessageSource
FromSendFriend
source
:
MessageSource
ToFriendImpl
):
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())
...
...
@@ -331,43 +333,42 @@ internal class MessageSvc {
)
),
msgSeq
=
source
.
sequenceId
,
msgRand
=
source
.
messageRandom
,
syncCookie
=
SyncCookie
(
time
=
source
.
time
).
toByteArray
(
SyncCookie
.
serializer
())
msgRand
=
source
.
id
,
syncCookie
=
SyncCookie
(
time
=
source
.
time
.
toULong
().
toLong
()
).
toByteArray
(
SyncCookie
.
serializer
())
// msgVia = 1
)
)
}
inline
fun
ToGroup
(
inline
fun
create
ToGroup
(
client
:
QQAndroidClient
,
group
Code
:
Long
,
group
:
Group
,
message
:
MessageChain
,
sourceCallback
:
(
MessageSource
FromSendGroup
)
->
Unit
sourceCallback
:
(
MessageSource
ToGroupImpl
)
->
Unit
):
OutgoingPacket
{
val
source
=
MessageSourceFromSendGroup
(
messageRandom
=
Random
.
nextInt
().
absoluteValue
,
senderId
=
client
.
uin
,
toUin
=
Group
.
calculateGroupUinByGroupCode
(
groupCode
),
time
=
currentTimeSeconds
,
groupId
=
groupCode
,
val
source
=
MessageSourceToGroupImpl
(
id
=
Random
.
nextInt
().
absoluteValue
,
sender
=
client
.
bot
,
target
=
group
,
time
=
currentTimeSeconds
.
toInt
(),
originalMessage
=
message
//,
// sourceMessage = message
)
sourceCallback
(
source
)
return
ToGroup
(
client
,
groupCode
,
message
,
source
)
return
createToGroup
(
client
,
group
.
id
,
message
,
source
)
}
/**
* 发送群消息
*/
@Suppress
(
"FunctionName"
)
private
fun
ToGroup
(
private
fun
create
ToGroup
(
client
:
QQAndroidClient
,
groupCode
:
Long
,
message
:
MessageChain
,
source
:
MessageSource
FromSendGroup
source
:
MessageSource
ToGroupImpl
):
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())
...
...
@@ -384,7 +385,7 @@ internal class MessageSvc {
)
),
msgSeq
=
client
.
atomicNextMessageSequenceId
(),
msgRand
=
source
.
messageRandom
,
msgRand
=
source
.
id
,
syncCookie
=
EMPTY_BYTE_ARRAY
,
msgVia
=
1
)
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
View file @
2b49a758
...
...
@@ -84,7 +84,7 @@ internal class OnlinePush {
return
GroupMessage
(
senderName
=
pbPushMsg
.
msg
.
msgHead
.
groupInfo
.
groupCard
,
sender
=
group
[
pbPushMsg
.
msg
.
msgHead
.
fromUin
],
message
=
pbPushMsg
.
msg
.
toMessageChain
(),
message
=
pbPushMsg
.
msg
.
toMessageChain
(
bot
,
isGroup
=
true
,
addSource
=
true
),
permission
=
when
{
flags
and
16
!=
0
->
MemberPermission
.
ADMINISTRATOR
flags
and
8
!=
0
->
MemberPermission
.
OWNER
...
...
@@ -369,13 +369,12 @@ internal class OnlinePush {
if
(
meta
.
authorUin
==
bot
.
id
)
{
null
}
else
MessageRecallEvent
.
GroupRecall
(
bot
,
meta
.
authorUin
,
meta
.
seq
.
toLong
().
shl
(
32
)
or
meta
.
msgRandom
.
toLong
().
and
(
0
xffffffff
),
meta
.
time
,
memebr
,
group
bot
=
bot
,
authorId
=
meta
.
authorUin
,
messageId
=
meta
.
msgRandom
,
messageTime
=
meta
.
time
,
operator
=
memebr
,
group
=
group
)
}
}
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/contentToString.kt
View file @
2b49a758
...
...
@@ -16,6 +16,7 @@ import kotlin.reflect.KProperty
import
kotlin.reflect.KProperty1
import
kotlin.reflect.KType
private
val
indent
:
String
=
" "
.
repeat
(
4
)
/**
...
...
@@ -126,10 +127,12 @@ internal fun Any?._miraiContentToString(prefix: String = ""): String = when (thi
internal
expect
fun
KProperty1
<*,
*>.
getValueAgainstPermission
(
receiver
:
Any
):
Any
?
@Suppress
(
"EXTENSION_SHADOWED_BY_MEMBER"
)
private
val
KProperty1
<*,
*>.
isConst
:
Boolean
get
()
=
false
// on JVM, it will be resolved to member function
private
val
KProperty1
<*,
*>.
isConst
:
Boolean
get
()
=
false
// on JVM, it will be resolved to member function
@Suppress
(
"EXTENSION_SHADOWED_BY_MEMBER"
)
private
val
KClass
<*>.
isData
:
Boolean
get
()
=
false
// on JVM, it will be resolved to member function
private
val
KClass
<*>.
isData
:
Boolean
get
()
=
false
// on JVM, it will be resolved to member function
private
fun
Any
.
contentToStringReflectively
(
prefix
:
String
,
...
...
mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
→
mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.
jvm.
kt
View file @
2b49a758
File moved
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt
View file @
2b49a758
...
...
@@ -7,7 +7,6 @@ import kotlinx.coroutines.io.ByteReadChannel
import
net.mamoe.mirai.contact.*
import
net.mamoe.mirai.data.AddFriendResult
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.data.ExperimentalMessageSource
import
net.mamoe.mirai.message.data.Image
import
net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.message.data.MessageSource
...
...
@@ -156,7 +155,6 @@ actual abstract class Bot actual constructor() : CoroutineScope, LowLevelBotAPIA
* @see _lowLevelRecallFriendMessage 低级 API
* @see _lowLevelRecallGroupMessage 低级 API
*/
@ExperimentalMessageSource
@JvmSynthetic
actual
abstract
suspend
fun
recall
(
source
:
MessageSource
)
...
...
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
View file @
2b49a758
...
...
@@ -7,10 +7,14 @@ import kotlinx.coroutines.Job
import
kotlinx.coroutines.runBlocking
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.JavaFriendlyAPI
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.*
import
net.mamoe.mirai.message.data.*
import
net.mamoe.mirai.recallIn
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.Member
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.message.data.ExperimentalMessageSource
import
net.mamoe.mirai.message.data.Message
import
net.mamoe.mirai.message.data.OnlineMessageSource
import
net.mamoe.mirai.message.data.QuoteReply
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.getValue
import
net.mamoe.mirai.utils.unsafeWeakRef
...
...
@@ -31,7 +35,7 @@ import net.mamoe.mirai.utils.unsafeWeakRef
@OptIn
(
MiraiInternalAPI
::
class
)
actual
open
class
MessageReceipt
<
out
C
:
Contact
>
@OptIn
(
ExperimentalMessageSource
::
class
)
actual
constructor
(
actual
val
source
:
MessageSource
,
actual
val
source
:
OnlineMessageSource
.
Outgoing
,
target
:
C
,
private
val
botAsMember
:
Member
?
)
{
...
...
@@ -51,96 +55,33 @@ actual constructor(
private
val
_isRecalled
=
atomic
(
false
)
/**
* 撤回这条消息. [recall] 或 [recallIn] 只能被调用一次.
*
* @see Bot.recall
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
*/
@OptIn
(
ExperimentalMessageSource
::
class
)
actual
suspend
fun
recall
()
{
@Suppress
(
"BooleanLiteralArgument"
)
if
(
_isRecalled
.
compareAndSet
(
false
,
true
))
{
when
(
val
contact
=
target
)
{
is
Group
->
{
contact
.
bot
.
recall
(
source
)
}
is
QQ
->
{
TODO
()
}
else
->
error
(
"Unknown contact type"
)
}
}
else
error
(
"message is already or planned to be recalled"
)
}
/**
* 在一段时间后撤回这条消息.. [recall] 或 [recallIn] 只能被调用一次.
*
* @param millis 延迟时间, 单位为毫秒
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
*/
actual
fun
recallIn
(
millis
:
Long
):
Job
{
@Suppress
(
"BooleanLiteralArgument"
)
if
(
_isRecalled
.
compareAndSet
(
false
,
true
))
{
return
when
(
val
contact
=
target
)
{
is
QQ
,
is
Group
->
contact
.
bot
.
recallIn
(
source
,
millis
)
else
->
error
(
"Unknown contact type"
)
}
}
else
error
(
"message is already or planned to be recalled"
)
}
/**
* [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable] 然后引用这条消息.
* @see MessageChain.quote 引用一条消息
*/
@OptIn
(
ExperimentalMessageSource
::
class
)
actual
open
suspend
fun
quote
():
QuoteReplyToSend
{
this
.
source
.
ensureSequenceIdAvailable
()
@OptIn
(
LowLevelAPI
::
class
)
return
_unsafeQuote
()
}
/**
* 引用这条消息, 但不会 [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable].
* 在 sequenceId 可用前就发送这条消息则会导致一个异常.
* 当且仅当用于存储而不用于发送时使用这个方法.
*
* @see MessageChain.quote 引用一条消息
*/
@OptIn
(
ExperimentalMessageSource
::
class
)
@LowLevelAPI
@Suppress
(
"FunctionName"
)
actual
fun
_unsafeQuote
():
QuoteReplyToSend
{
return
this
.
source
.
quote
(
botAsMember
as
?
QQ
)
}
/**
* 引用这条消息并回复.
* @see MessageChain.quote 引用一条消息
*/
@JvmSynthetic
actual
suspend
fun
quoteReply
(
message
:
MessageChain
)
{
target
.
sendMessage
(
this
.
quote
()
+
message
)
@JavaFriendlyAPI
@JvmName
(
"quoteReply"
)
fun
__quoteReplyBlockingForJava__
(
message
:
Message
):
MessageReceipt
<
C
>
{
return
runBlocking
{
return
@
runBlocking
quoteReply
(
message
)
}
}
@JavaFriendlyAPI
@JvmName
(
"quoteReply"
)
fun
__quoteReplyBlockingForJava__
(
message
:
Message
)
{
runBlocking
{
quoteReply
(
message
)
}
fun
__quoteReplyBlockingForJava__
(
message
:
String
):
MessageReceipt
<
C
>
{
r
eturn
r
unBlocking
{
quoteReply
(
message
)
}
}
@JavaFriendlyAPI
@JvmName
(
"recall"
)
fun
__recallBlockingForJava__
()
{
runBlocking
{
recall
()
}
return
runBlocking
{
return
@
runBlocking
recall
()
}
}
@JavaFriendlyAPI
@JvmName
(
"recall"
)
fun
__recallInBlockingForJava__
(
timeMillis
:
Long
):
Job
{
return
recallIn
(
timeMillis
=
timeMillis
)
}
@JavaFriendlyAPI
@JvmName
(
"quote"
)
fun
__quoteBlockingForJava__
()
{
r
unBlocking
{
quote
()
}
fun
__quoteBlockingForJava__
()
:
QuoteReply
{
r
eturn
this
.
quote
()
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
View file @
2b49a758
...
...
@@ -19,10 +19,7 @@ import kotlinx.coroutines.launch
import
net.mamoe.mirai.contact.*
import
net.mamoe.mirai.data.AddFriendResult
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.data.ExperimentalMessageSource
import
net.mamoe.mirai.message.data.Image
import
net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.message.data.MessageSource
import
net.mamoe.mirai.message.data.*
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.LoginFailedException
import
net.mamoe.mirai.utils.*
...
...
@@ -170,10 +167,8 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
* @throws PermissionDeniedException 当 [Bot] 无权限操作时
*
* @see Bot.recall (扩展函数) 接受参数 [MessageChain]
* @see _lowLevelRecallFriendMessage 低级 API
* @see _lowLevelRecallGroupMessage 低级 API
* @see MessageSource.recall
*/
@ExperimentalMessageSource
@JvmSynthetic
abstract
suspend
fun
recall
(
source
:
MessageSource
)
...
...
@@ -222,20 +217,19 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
/**
* 撤回这条消息.
* 根据 [message] 内的 [MessageSource] 进行相关判断.
*
* [Bot] 撤回自己的消息不需要权限.
* [Bot] 撤回群员的消息需要管理员权限.
* [Bot] 撤回自己的消息不需要权限
, 但需要在发出后 2 分钟内撤回
.
* [Bot] 撤回群员的消息需要管理员权限
, 可在任意时间撤回
.
*
* @throws PermissionDeniedException 当 [Bot] 无权限操作时
* @see Bot.recall
*/
@JvmSynthetic
suspend
inline
fun
Bot
.
recall
(
message
:
MessageChain
)
=
this
.
recall
(
message
[
MessageSource
])
suspend
inline
fun
Bot
.
recall
(
message
:
MessageChain
)
=
this
.
recall
(
message
.
source
)
/**
* 在一段时间后撤回这条消息.
* 将根据 [MessageSource.groupId] 判断消息是群消息还是好友消息.
* 在一段时间后撤回这个消息源所指代的消息.
*
* @param millis 延迟的时间, 单位为毫秒
* @param coroutineContext 额外的 [CoroutineContext]
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
View file @
2b49a758
...
...
@@ -109,8 +109,7 @@ sealed class MessageRecallEvent : BotEvent {
* 消息 id.
* @see MessageSource.id
*/
@ExperimentalMessageSource
abstract
val
messageId
:
Long
abstract
val
messageId
:
Int
/**
* 原发送时间
...
...
@@ -122,8 +121,7 @@ sealed class MessageRecallEvent : BotEvent {
*/
data class
FriendRecall
(
override
val
bot
:
Bot
,
@ExperimentalMessageSource
override
val
messageId
:
Long
,
override
val
messageId
:
Int
,
override
val
messageTime
:
Int
,
/**
* 撤回操作人, 可能为 [Bot.uin] 或好友的 [QQ.id]
...
...
@@ -137,8 +135,7 @@ sealed class MessageRecallEvent : BotEvent {
data class
GroupRecall
(
override
val
bot
:
Bot
,
override
val
authorId
:
Long
,
@ExperimentalMessageSource
override
val
messageId
:
Long
,
override
val
messageId
:
Int
,
override
val
messageTime
:
Int
,
/**
* 操作人. 为 null 时则为 [Bot] 操作.
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
View file @
2b49a758
...
...
@@ -13,7 +13,6 @@ import kotlinx.coroutines.Job
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.data.*
import
net.mamoe.mirai.message.data.MessageSource
import
net.mamoe.mirai.utils.MiraiExperimentalAPI
import
net.mamoe.mirai.utils.SinceMirai
import
net.mamoe.mirai.utils.WeakRef
...
...
@@ -69,21 +68,6 @@ interface LowLevelBotAPIAccessor {
@LowLevelAPI
suspend
fun
_lowLevelQueryGroupMemberList
(
groupUin
:
Long
,
groupCode
:
Long
,
ownerId
:
Long
):
Sequence
<
MemberInfo
>
/**
* 撤回一条由机器人发送给好友的消息
* @param messageId [MessageSource.id]
*/
@MiraiExperimentalAPI
(
"还未实现"
)
@LowLevelAPI
suspend
fun
_lowLevelRecallFriendMessage
(
friendId
:
Long
,
messageId
:
Long
,
time
:
Long
)
/**
* 撤回一条群里的消息. 可以是机器人发送也可以是其他群员发送.
* @param messageId [MessageSource.id]
*/
@LowLevelAPI
suspend
fun
_lowLevelRecallGroupMessage
(
groupId
:
Long
,
messageId
:
Long
)
/**
* 获取群公告列表
* @param page 页码
...
...
@@ -131,20 +115,3 @@ interface LowLevelBotAPIAccessor {
@MiraiExperimentalAPI
suspend
fun
_lowLevelGetGroupActiveData
(
groupId
:
Long
):
GroupActiveData
}
/**
* 撤回一条群里的消息. 可以是机器人发送也可以是其他群员发送.
*/
@Suppress
(
"FunctionName"
)
@MiraiExperimentalAPI
@LowLevelAPI
suspend
fun
LowLevelBotAPIAccessor
.
_lowLevelRecallGroupMessage
(
groupId
:
Long
,
messageSequenceId
:
Int
,
messageRandom
:
Int
)
{
this
.
_lowLevelRecallGroupMessage
(
groupId
,
messageSequenceId
.
toLong
().
shl
(
32
)
or
messageRandom
.
toLong
().
and
(
0
xFFFFFFFFL
)
)
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
View file @
2b49a758
...
...
@@ -13,6 +13,8 @@ import net.mamoe.mirai.Bot
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.event.BroadcastControllable
import
net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.message.data.OnlineMessageSource
import
net.mamoe.mirai.message.data.source
import
net.mamoe.mirai.utils.getValue
import
net.mamoe.mirai.utils.unsafeWeakRef
...
...
@@ -23,6 +25,7 @@ class FriendMessage(
override
val
sender
:
QQ
by
sender
.
unsafeWeakRef
()
override
val
bot
:
Bot
get
()
=
sender
.
bot
override
val
subject
:
QQ
get
()
=
sender
override
val
source
:
OnlineMessageSource
.
Incoming
.
FromFriend
get
()
=
message
.
source
as
OnlineMessageSource
.
Incoming
.
FromFriend
override
fun
toString
():
String
=
"FriendMessage(sender=${sender.id}, message=$message)"
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
View file @
2b49a758
...
...
@@ -15,6 +15,8 @@ import net.mamoe.mirai.contact.Member
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.event.Event
import
net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.message.data.OnlineMessageSource
import
net.mamoe.mirai.message.data.source
import
net.mamoe.mirai.utils.getValue
import
net.mamoe.mirai.utils.unsafeWeakRef
...
...
@@ -34,6 +36,8 @@ class GroupMessage(
override
val
subject
:
Group
get
()
=
group
override
val
source
:
OnlineMessageSource
.
Incoming
.
FromGroup
get
()
=
message
.
source
as
OnlineMessageSource
.
Incoming
.
FromGroup
inline
fun
Long
.
member
():
Member
=
group
[
this
]
override
fun
toString
():
String
=
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
View file @
2b49a758
...
...
@@ -177,8 +177,7 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
@JvmName
(
"reply2"
)
suspend
inline
fun
MessageChain
.
quoteReply
():
MessageReceipt
<
TSubject
>
=
quoteReply
(
this
)
@ExperimentalMessageSource
inline
fun
MessageChain
.
quote
():
QuoteReplyToSend
=
this
.
quote
(
sender
)
open
val
source
:
OnlineMessageSource
.
Incoming
get
()
=
message
.
source
as
OnlineMessageSource
.
Incoming
// endregion
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt
View file @
2b49a758
...
...
@@ -7,14 +7,17 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"NOTHING_TO_INLINE"
)
package
net.mamoe.mirai.message
import
kotlinx.coroutines.Job
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.*
import
net.mamoe.mirai.message.data.*
import
net.mamoe.mirai.recallIn
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
import
kotlin.jvm.JvmSynthetic
/**
...
...
@@ -29,19 +32,17 @@ import kotlin.jvm.JvmSynthetic
* @see QQ.sendMessage 发送群消息, 返回回执(此对象)
*
* @see MessageReceipt.sourceId 源 id
* @see MessageReceipt.sourceSequenceId 源序列号
* @see MessageReceipt.sourceTime 源时间
*/
expect
open
class
MessageReceipt
<
out
C
:
Contact
>
@OptIn
(
ExperimentalMessageSource
::
class
)
constructor
(
source
:
MessageSource
,
source
:
OnlineMessageSource
.
Outgoing
,
target
:
C
,
botAsMember
:
Member
?
)
{
/**
* 指代发送出去的消息
*/
@ExperimentalMessageSource
val
source
:
MessageSource
val
source
:
OnlineMessageSource
.
Outgoing
/**
* 发送目标, 为 [Group] 或 [QQ]
...
...
@@ -52,66 +53,65 @@ expect open class MessageReceipt<out C : Contact> @OptIn(ExperimentalMessageSour
* 是否为发送给群的消息的回执
*/
val
isToGroup
:
Boolean
}
/**
* 撤回这条消息. [recall] 或 [recallIn] 只能被调用一次.
*
* @see Bot.recall
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
*/
suspend
fun
recall
()
/**
* 撤回这条消息. [recall] 或 [recallIn] 只能被调用一次.
*
* @see Bot.recall
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
*/
suspend
inline
fun
MessageReceipt
<*>.
recall
()
{
return
target
.
bot
.
recall
(
source
)
}
/**
* 在一段时间后撤回这条消息.. [recall] 或 [recallIn] 只能被调用一次.
*
* @param millis 延迟时间, 单位为毫秒
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
*/
fun
recallIn
(
millis
:
Long
):
Job
/**
* 在一段时间后撤回这条消息. [recall] 或 [recallIn] 只能被调用一次.
*
* @param timeMillis 延迟时间, 单位为毫秒
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
*/
inline
fun
MessageReceipt
<*>.
recallIn
(
timeMillis
:
Long
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
):
Job
=
source
.
recallIn
(
timeMillis
,
coroutineContext
)
/**
* [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable] 然后引用这条消息.
* @see MessageChain.quote 引用一条消息
*/
open
suspend
fun
quote
():
QuoteReplyToSend
/**
* 引用这条消息, 但不会 [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable].
* 在 sequenceId 可用前就发送这条消息则会导致一个异常.
* 当且仅当用于存储而不用于发送时使用这个方法.
*
* @see MessageChain.quote 引用一条消息
*/
@LowLevelAPI
@Suppress
(
"FunctionName"
)
fun
_unsafeQuote
():
QuoteReplyToSend
/**
* 引用这条消息.
* @see MessageChain.quote 引用一条消息
*/
@JvmSynthetic
inline
fun
MessageReceipt
<*>.
quote
():
QuoteReply
=
this
.
source
.
quote
()
/**
* 引用这条消息并回复.
* @see MessageChain.quote 引用一条消息
*/
suspend
fun
quoteReply
(
message
:
MessageChain
)
/**
* 引用这条消息并回复.
* @see MessageChain.quote 引用一条消息
*/
@JvmSynthetic
suspend
inline
fun
<
C
:
Contact
>
MessageReceipt
<
C
>.
quoteReply
(
message
:
Message
):
MessageReceipt
<
C
>
{
@Suppress
(
"UNCHECKED_CAST"
)
return
target
.
sendMessage
(
this
.
quote
()
+
message
)
as
MessageReceipt
<
C
>
}
/**
* 获取源消息 [MessageSource.id]
*
* @see MessageSource.id
* 引用这条消息并回复.
* @see MessageChain.quote 引用一条消息
*/
@
get
:
JvmSynthetic
@ExperimentalMessageSource
inline
val
MessageReceipt
<*>.
sourceId
:
Long
get
()
=
this
.
source
.
id
@JvmSynthetic
suspend
inline
fun
<
C
:
Contact
>
MessageReceipt
<
C
>.
quoteReply
(
message
:
String
):
MessageReceipt
<
C
>
{
return
this
.
quoteReply
(
message
.
toMessage
())
}
/**
* 获取源消息 [MessageSource.
sequenceI
d]
* 获取源消息 [MessageSource.
i
d]
*
* @see MessageSource.
sequenceI
d
* @see MessageSource.
i
d
*/
@
get
:
JvmSynthetic
@ExperimentalMessageSource
inline
val
MessageReceipt
<*>.
sourceSequenceId
:
Int
get
()
=
this
.
source
.
sequenceId
inline
val
MessageReceipt
<*>.
sourceId
:
Int
get
()
=
this
.
source
.
id
/**
* 获取源消息 [MessageSource.time]
...
...
@@ -119,15 +119,6 @@ inline val MessageReceipt<*>.sourceSequenceId: Int
* @see MessageSource.time
*/
@
get
:
JvmSynthetic
@ExperimentalMessageSource
inline
val
MessageReceipt
<*>.
sourceTime
:
Long
inline
val
MessageReceipt
<*>.
sourceTime
:
Int
get
()
=
this
.
source
.
time
suspend
inline
fun
MessageReceipt
<*>.
quoteReply
(
message
:
Message
)
{
return
this
.
quoteReply
(
message
.
asMessageChain
())
}
suspend
inline
fun
MessageReceipt
<*>.
quoteReply
(
message
:
String
)
{
return
this
.
quoteReply
(
message
.
toMessage
().
asMessageChain
())
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
View file @
2b49a758
...
...
@@ -9,11 +9,21 @@
@
file
:
JvmMultifileClass
@
file
:
JvmName
(
"MessageUtils"
)
@
file
:
Suppress
(
"NOTHING_TO_INLINE"
)
package
net.mamoe.mirai.message.data
import
kotlinx.coroutines.Job
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.contact.*
import
net.mamoe.mirai.message.ContactMessage
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.recallIn
import
net.mamoe.mirai.utils.LazyProperty
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.SinceMirai
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
import
kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
import
kotlin.jvm.JvmSynthetic
...
...
@@ -33,108 +43,189 @@ annotation class ExperimentalMessageSource
*
* @see Bot.recall 撤回一条消息
* @see MessageSource.quote 引用这条消息, 创建 [MessageChain]
*
* @see OnlineMessageSource 在线消息的 [MessageSource]
* @see OfflineMessageSource 离线消息的 [MessageSource]
*/
@
ExperimentalMessageSource
interface
MessageSource
:
Message
,
MessageMetadata
{
@
SinceMirai
(
"0.33.0"
)
sealed
class
MessageSource
:
Message
,
MessageMetadata
{
companion
object
Key
:
Message
.
Key
<
MessageSource
>
/**
* 在 Mirai 中使用的 id.
* 高 32 位为 [sequenceId],
* 低 32 位为 [messageRandom]
*
* @see ensureSequenceIdAvailable 确保 sequenceId 可用
* 所属 [Bot]
*/
val
id
:
Long
abstract
val
bot
:
Bot
/**
* 等待 [sequenceId] 获取, 确保其可用.
*
* 这个方法 3 秒超时, 抛出 [IllegalStateException], 则表明原消息发送失败.
* 消息 id.
*/
suspend
fun
ensureSequenceIdAvailable
()
abstract
val
id
:
Int
// random
/**
* 发送时间, 单位为秒. 撤回好友消息时可能需要
*/
val
time
:
Long
abstract
val
time
:
Int
/**
* 发送人. 可
以为机器人自己 (`BotAsQQ`, `BotAsMember`)
* 发送人. 可
能为机器人自己, 好友的 id, 或群 id
*/
val
senderId
:
Long
abstract
val
senderId
:
Long
/**
*
消息发送对象, 可以为一个群的 `uin` (非 `id`)或一个好友, 或机器人自己
*
发送目标. 可能为机器人自己, 好友的 id, 或群 id
*/
val
toUin
:
Long
abstract
val
targetId
:
Long
// groupCode / friendUin
/**
*
当群消息时为群 id, [Group.id], 好友消息时为 0
*
原消息内容.
*/
val
groupId
:
Long
@LazyProperty
abstract
val
originalMessage
:
MessageChain
final
override
fun
toString
():
String
=
""
}
// ONLINE
/**
* 在线消息的 [MessageSource].
* 拥有对象化的 [sender], [target], 也可以直接 [recall] 和 [quote]
*
* ### 来源
* **必定是一个发出去的消息或接收到的消息的 [MessageChain] 中的一个元数据 [MessageMetadata].**
*
* #### 机器人主动发送消息
* 当机器人 [主动发出消息][Member.sendMessage], 将会得到一个 [消息回执][MessageReceipt].
* 此回执的 [消息源][MessageReceipt.source] 即为一个 [外向消息源][OnlineMessageSource.Outgoing], 代表着刚刚发出的那条消息的来源.
*
* #### 机器人接受消息
* 当机器人接收一条消息 [ContactMessage], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源.
*/
@SinceMirai
(
"0.33.0"
)
sealed
class
OnlineMessageSource
:
MessageSource
()
{
/**
* 原消息内容
* 消息发送人. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群员][Member].
* 即类型必定为 [Bot], [QQ] 或 [Member]
*/
@LazyProperty
val
originalMessage
:
MessageChain
abstract
val
sender
:
Any
/**
* 固定返回空字符串 ("")
* 消息发送目标. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群][Group].
* 即类型必定为 [Bot], [QQ] 或 [Group]
*/
override
fun
toString
():
String
abstract
val
target
:
Any
/**
* 消息主体. 群消息时为 [Group]. 好友消息时为 [QQ].
* 不论是机器人接收的消息还是发送的消息, 此属性都指向机器人能进行回复的目标.
*/
abstract
val
subject
:
Contact
// Group or QQ
/**
* 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource]
*/
sealed
class
Outgoing
:
OnlineMessageSource
()
{
abstract
override
val
sender
:
Bot
abstract
override
val
target
:
Contact
final
override
val
senderId
:
Long
get
()
=
sender
.
id
final
override
val
targetId
:
Long
get
()
=
target
.
id
abstract
class
ToFriend
:
Outgoing
()
{
abstract
override
val
target
:
QQ
final
override
val
subject
:
QQ
get
()
=
target
// final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.id})"
}
abstract
class
ToGroup
:
Outgoing
()
{
abstract
override
val
target
:
Group
final
override
val
subject
:
Group
get
()
=
target
// final override fun toString(): String = "OnlineMessageSource.ToGroup(group=${target.id})"
}
}
/**
* 接收到的一条消息的 [MessageSource]
*/
sealed
class
Incoming
:
OnlineMessageSource
()
{
abstract
override
val
sender
:
QQ
// out QQ
abstract
override
val
target
:
Bot
final
override
val
senderId
:
Long
get
()
=
sender
.
id
final
override
val
targetId
:
Long
get
()
=
target
.
id
abstract
class
FromFriend
:
Incoming
()
{
abstract
override
val
sender
:
QQ
final
override
val
subject
:
QQ
get
()
=
sender
// final override fun toString(): String = "OnlineMessageSource.FromFriend(from=${sender.id})"
}
abstract
class
FromGroup
:
Incoming
()
{
abstract
override
val
sender
:
Member
final
override
val
subject
:
Group
get
()
=
sender
.
group
val
group
:
Group
get
()
=
sender
.
group
// final override fun toString(): String = "OnlineMessageSource.FromGroup(group=${group.id}, sender=${sender.id})"
}
}
}
/**
* 序列号. 若是机器人发出去的消息, 请先 [确保 sequenceId 可用][MessageSource.ensureSequenceIdAvailable]
* @see MessageSource.id
* 引用这条消息
*/
@ExperimentalMessageSource
@
get
:
JvmSynthetic
inline
val
MessageSource
.
sequenceId
:
Int
get
()
=
(
this
.
id
shr
32
).
toInt
()
fun
OnlineMessageSource
.
quote
():
QuoteReply
{
@OptIn
(
MiraiInternalAPI
::
class
)
return
QuoteReply
(
this
)
}
/**
* 消息随机数. 由服务器或客户端指定后不能更改. 它是消息 id 的一部分.
* @see MessageSource.id
* 引用这条消息
*/
@ExperimentalMessageSource
@
get
:
JvmSynthetic
inline
val
MessageSource
.
messageRandom
:
Int
get
()
=
this
.
id
.
toInt
()
fun
MessageChain
.
quote
():
QuoteReply
{
@OptIn
(
MiraiInternalAPI
::
class
)
return
QuoteReply
(
this
.
source
)
}
// For MessageChain
/**
* 撤回这条消息
*/
suspend
inline
fun
OnlineMessageSource
.
recall
()
=
bot
.
recall
(
this
)
/**
* 消息 id.
*
* 仅接收到的消息才可以获取这个 id.
*
* @see MessageSource.id
* 撤回这条消息
*/
@ExperimentalMessageSource
@
get
:
JvmSynthetic
inline
val
MessageChain
.
id
:
Long
get
()
=
this
[
MessageSource
].
id
inline
fun
MessageSource
.
recallIn
(
timeMillis
:
Long
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
):
Job
=
bot
.
recallIn
(
this
,
timeMillis
,
coroutineContext
)
// OFFLINE
/**
* 消息序列号, 可能来自服务器也可以发送时赋值, 不唯一.
*
* 仅接收到的消息才可以获取这个序列号.
* 由一条消息中的 [QuoteReply] 得到的 [MessageSource].
* 此消息源可能来自一条与机器人无关的消息. 因此无法提供对象化的 `sender` 或 `target` 获取.
*/
@SinceMirai
(
"0.33.0"
)
abstract
class
OfflineMessageSource
:
MessageSource
()
{
// final override fun toString(): String = "OfflineMessageSource(sender=$senderId, target=$targetId)"
}
// TODO: 2020/4/4 可能要分群和好友
// For MessageChain
/**
* 消息 id.
* 仅从服务器接收的消息才可以获取 id
*
* @see MessageSource.id
*/
@ExperimentalMessageSource
@
get
:
JvmSynthetic
inline
val
MessageChain
.
sequenceI
d
:
Int
get
()
=
this
.
getOrNull
(
MessageSource
)
?.
sequenceId
?:
error
(
"Only MessageChain from server has sequenceId"
)
inline
val
MessageChain
.
i
d
:
Int
get
()
=
this
.
source
.
id
/**
*
消息随机数. 由服务器或客户端指定后不能更改. 它是消息 id 的一部分.
*
@see MessageSource.id
*
获取这条消息源
*
仅从服务器接收的消息才可以获取消息源
*/
@ExperimentalMessageSource
@
get
:
JvmSynthetic
inline
val
MessageChain
.
messageRandom
:
Int
get
()
=
this
.
getOrNull
(
MessageSource
)
?.
messageRandom
?:
error
(
"Only MessageChain from server has sequenceId"
)
inline
val
MessageChain
.
source
:
MessageSource
get
()
=
this
[
MessageSource
]
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt
View file @
2b49a758
...
...
@@ -12,66 +12,24 @@
package
net.mamoe.mirai.message.data
import
net.mamoe.mirai.contact.Member
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.utils.
MiraiInternalAPI
import
net.mamoe.mirai.utils.
SinceMirai
import
kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
/**
*
从服务器接收的或客户端构造用来发送的群内的或好友的
引用回复.
* 引用回复.
*
* 可以引用一条群消息并发送给一个好友, 或是引用好友消息发送给群.
* 可以引用自己发出的消息. 详见 [MessageReceipt.quote]
*
*
总是使用 [quote] 来构造这个实例.
*
@see MessageSource 获取更多信息
*/
open
class
QuoteReply
@OptIn
(
ExperimentalMessageSource
::
class
)
@MiraiInternalAPI
constructor
(
val
source
:
MessageSource
)
:
Message
,
MessageMetadata
{
@SinceMirai
(
"0.33.0"
)
class
QuoteReply
(
val
source
:
MessageSource
)
:
Message
,
MessageMetadata
{
// TODO: 2020/4/4 Metadata or Content?
companion
object
Key
:
Message
.
Key
<
QuoteReply
>
final
override
fun
toString
():
String
=
"[mirai:quote]"
}
/**
* 用于发送的引用回复.
* 总是使用 [quote] 来构造实例.
*/
@OptIn
(
MiraiInternalAPI
::
class
,
ExperimentalMessageSource
::
class
)
sealed
class
QuoteReplyToSend
@MiraiInternalAPI
constructor
(
source
:
MessageSource
)
:
QuoteReply
(
source
)
{
class
ToGroup
(
source
:
MessageSource
,
val
sender
:
QQ
)
:
QuoteReplyToSend
(
source
)
{
fun
createAt
():
At
=
At
(
sender
as
Member
)
}
class
ToFriend
(
source
:
MessageSource
)
:
QuoteReplyToSend
(
source
)
}
/**
* 引用这条消息.
* @see sender 消息发送人.
*/
@ExperimentalMessageSource
@OptIn
(
MiraiInternalAPI
::
class
)
fun
MessageChain
.
quote
(
sender
:
QQ
?):
QuoteReplyToSend
{
this
.
firstOrNull
<
MessageSource
>()
?.
let
{
return
it
.
quote
(
sender
)
}
error
(
"cannot find MessageSource"
)
}
/**
* 引用这条消息.
* @see from 消息来源. 若是好友发送
*/
@ExperimentalMessageSource
@OptIn
(
MiraiInternalAPI
::
class
)
fun
MessageSource
.
quote
(
from
:
QQ
?):
QuoteReplyToSend
{
return
if
(
this
.
groupId
!=
0L
)
{
check
(
from
is
Member
)
{
"sender must be Member to quote a GroupMessage"
}
QuoteReplyToSend
.
ToGroup
(
this
,
from
)
}
else
QuoteReplyToSend
.
ToFriend
(
this
)
override
fun
toString
():
String
=
"[mirai:quote]"
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt
View file @
2b49a758
...
...
@@ -7,10 +7,7 @@ import kotlinx.coroutines.io.ByteReadChannel
import
net.mamoe.mirai.contact.*
import
net.mamoe.mirai.data.AddFriendResult
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.data.ExperimentalMessageSource
import
net.mamoe.mirai.message.data.Image
import
net.mamoe.mirai.message.data.MessageChain
import
net.mamoe.mirai.message.data.MessageSource
import
net.mamoe.mirai.message.data.*
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.LoginFailedException
import
net.mamoe.mirai.utils.*
...
...
@@ -166,7 +163,6 @@ actual abstract class Bot actual constructor() : CoroutineScope, LowLevelBotAPIA
* @see _lowLevelRecallFriendMessage 低级 API
* @see _lowLevelRecallGroupMessage 低级 API
*/
@ExperimentalMessageSource
@JvmSynthetic
actual
abstract
suspend
fun
recall
(
source
:
MessageSource
)
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
View file @
2b49a758
...
...
@@ -7,10 +7,14 @@ import kotlinx.coroutines.Job
import
kotlinx.coroutines.runBlocking
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.JavaFriendlyAPI
import
net.mamoe.mirai.LowLevelAPI
import
net.mamoe.mirai.contact.*
import
net.mamoe.mirai.message.data.*
import
net.mamoe.mirai.recallIn
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.Member
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.message.data.ExperimentalMessageSource
import
net.mamoe.mirai.message.data.Message
import
net.mamoe.mirai.message.data.OnlineMessageSource
import
net.mamoe.mirai.message.data.QuoteReply
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.getValue
import
net.mamoe.mirai.utils.unsafeWeakRef
...
...
@@ -31,7 +35,7 @@ import net.mamoe.mirai.utils.unsafeWeakRef
@OptIn
(
MiraiInternalAPI
::
class
)
actual
open
class
MessageReceipt
<
out
C
:
Contact
>
@OptIn
(
ExperimentalMessageSource
::
class
)
actual
constructor
(
actual
val
source
:
MessageSource
,
actual
val
source
:
OnlineMessageSource
.
Outgoing
,
target
:
C
,
private
val
botAsMember
:
Member
?
)
{
...
...
@@ -51,87 +55,33 @@ actual constructor(
private
val
_isRecalled
=
atomic
(
false
)
/**
* 撤回这条消息. [recall] 或 [recallIn] 只能被调用一次.
*
* @see Bot.recall
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
*/
@OptIn
(
ExperimentalMessageSource
::
class
)
actual
suspend
fun
recall
()
{
@Suppress
(
"BooleanLiteralArgument"
)
if
(
_isRecalled
.
compareAndSet
(
false
,
true
))
{
target
.
bot
.
recall
(
source
)
}
else
error
(
"message is already or planned to be recalled"
)
}
/**
* 在一段时间后撤回这条消息.. [recall] 或 [recallIn] 只能被调用一次.
*
* @param millis 延迟时间, 单位为毫秒
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
*/
actual
fun
recallIn
(
millis
:
Long
):
Job
{
@Suppress
(
"BooleanLiteralArgument"
)
if
(
_isRecalled
.
compareAndSet
(
false
,
true
))
{
return
when
(
val
contact
=
target
)
{
is
QQ
,
is
Group
->
contact
.
bot
.
recallIn
(
source
,
millis
)
else
->
error
(
"Unknown contact type"
)
}
}
else
error
(
"message is already or planned to be recalled"
)
}
/**
* [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable] 然后引用这条消息.
* @see MessageChain.quote 引用一条消息
*/
@OptIn
(
ExperimentalMessageSource
::
class
)
actual
open
suspend
fun
quote
():
QuoteReplyToSend
{
this
.
source
.
ensureSequenceIdAvailable
()
@OptIn
(
LowLevelAPI
::
class
)
return
_unsafeQuote
()
}
/**
* 引用这条消息, 但不会 [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable].
* 在 sequenceId 可用前就发送这条消息则会导致一个异常.
* 当且仅当用于存储而不用于发送时使用这个方法.
*
* @see MessageChain.quote 引用一条消息
*/
@OptIn
(
ExperimentalMessageSource
::
class
)
@LowLevelAPI
@Suppress
(
"FunctionName"
)
actual
fun
_unsafeQuote
():
QuoteReplyToSend
{
return
this
.
source
.
quote
(
botAsMember
as
?
QQ
)
}
/**
* 引用这条消息并回复.
* @see MessageChain.quote 引用一条消息
*/
@JvmSynthetic
actual
suspend
fun
quoteReply
(
message
:
MessageChain
)
{
target
.
sendMessage
(
this
.
quote
()
+
message
)
@JavaFriendlyAPI
@JvmName
(
"quoteReply"
)
fun
__quoteReplyBlockingForJava__
(
message
:
Message
):
MessageReceipt
<
C
>
{
return
runBlocking
{
return
@
runBlocking
quoteReply
(
message
)
}
}
@JavaFriendlyAPI
@JvmName
(
"quoteReply"
)
fun
__quoteReplyBlockingForJava__
(
message
:
Message
)
{
runBlocking
{
quoteReply
(
message
)
}
fun
__quoteReplyBlockingForJava__
(
message
:
String
):
MessageReceipt
<
C
>
{
r
eturn
r
unBlocking
{
quoteReply
(
message
)
}
}
@JavaFriendlyAPI
@JvmName
(
"recall"
)
fun
__recallBlockingForJava__
()
{
runBlocking
{
recall
()
}
return
runBlocking
{
recall
()
}
}
@JavaFriendlyAPI
@JvmName
(
"recall"
)
fun
__recallInBlockingForJava__
(
timeMillis
:
Long
):
Job
{
return
recallIn
(
timeMillis
=
timeMillis
)
}
@JavaFriendlyAPI
@JvmName
(
"quote"
)
fun
__quoteBlockingForJava__
()
{
r
unBlocking
{
quote
()
}
fun
__quoteBlockingForJava__
()
:
QuoteReply
{
r
eturn
this
.
quote
()
}
}
\ No newline at end of file
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