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
96610b30
Commit
96610b30
authored
Apr 20, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support CustomMessage
parent
1258746e
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
305 additions
and
25 deletions
+305
-25
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/convension.kt
...in/kotlin/net/mamoe/mirai/qqandroid/message/convension.kt
+58
-19
mirai-core-qqandroid/src/commonTest/kotlin/samples/CustomMessageSamples.kt
...oid/src/commonTest/kotlin/samples/CustomMessageSamples.kt
+34
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt
...in/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt
+1
-1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CustomMessage.kt
...Main/kotlin/net.mamoe.mirai/message/data/CustomMessage.kt
+196
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
...commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
+1
-4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/impl.kt
...rc/commonMain/kotlin/net.mamoe.mirai/message/data/impl.kt
+15
-1
No files found.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/convension.kt
View file @
96610b30
...
@@ -34,6 +34,7 @@ private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持
...
@@ -34,6 +34,7 @@ private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持
private
val
UNSUPPORTED_POKE_MESSAGE_PLAIN
=
PlainText
(
"[戳一戳]请使用最新版手机QQ体验新功能。"
)
private
val
UNSUPPORTED_POKE_MESSAGE_PLAIN
=
PlainText
(
"[戳一戳]请使用最新版手机QQ体验新功能。"
)
private
val
UNSUPPORTED_FLASH_MESSAGE_PLAIN
=
PlainText
(
"[闪照]请使用新版手机QQ查看闪照。"
)
private
val
UNSUPPORTED_FLASH_MESSAGE_PLAIN
=
PlainText
(
"[闪照]请使用新版手机QQ查看闪照。"
)
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
@OptIn
(
MiraiInternalAPI
::
class
,
MiraiExperimentalAPI
::
class
)
@OptIn
(
MiraiInternalAPI
::
class
,
MiraiExperimentalAPI
::
class
)
internal
fun
MessageChain
.
toRichTextElems
(
forGroup
:
Boolean
,
withGeneralFlags
:
Boolean
):
MutableList
<
ImMsgBody
.
Elem
>
{
internal
fun
MessageChain
.
toRichTextElems
(
forGroup
:
Boolean
,
withGeneralFlags
:
Boolean
):
MutableList
<
ImMsgBody
.
Elem
>
{
val
elements
=
mutableListOf
<
ImMsgBody
.
Elem
>()
val
elements
=
mutableListOf
<
ImMsgBody
.
Elem
>()
...
@@ -87,6 +88,15 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
...
@@ -87,6 +88,15 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
when
(
it
)
{
when
(
it
)
{
is
PlainText
->
elements
.
add
(
ImMsgBody
.
Elem
(
text
=
ImMsgBody
.
Text
(
str
=
it
.
stringValue
)))
is
PlainText
->
elements
.
add
(
ImMsgBody
.
Elem
(
text
=
ImMsgBody
.
Text
(
str
=
it
.
stringValue
)))
is
CustomMessage
->
{
@Suppress
(
"UNCHECKED_CAST"
)
elements
.
add
(
ImMsgBody
.
Elem
(
customElem
=
ImMsgBody
.
CustomElem
(
enumType
=
MIRAI_CUSTOM_ELEM_TYPE
,
data
=
CustomMessage
.
serialize
(
it
.
getFactory
()
as
CustomMessage
.
Factory
<
CustomMessage
>,
it
)
))
)
}
is
At
->
{
is
At
->
{
elements
.
add
(
ImMsgBody
.
Elem
(
text
=
it
.
toJceData
()))
elements
.
add
(
ImMsgBody
.
Elem
(
text
=
it
.
toJceData
()))
elements
.
add
(
ImMsgBody
.
Elem
(
text
=
ImMsgBody
.
Text
(
str
=
" "
)))
elements
.
add
(
ImMsgBody
.
Elem
(
text
=
ImMsgBody
.
Text
(
str
=
" "
)))
...
@@ -243,20 +253,23 @@ internal inline fun <reified R> Iterable<*>.firstIsInstanceOrNull(): R? {
...
@@ -243,20 +253,23 @@ internal inline fun <reified R> Iterable<*>.firstIsInstanceOrNull(): R? {
return
null
return
null
}
}
internal
val
MIRAI_CUSTOM_ELEM_TYPE
=
"mirai"
.
hashCode
()
// 103904510
@Suppress
(
"INVISIBLE_REFERENCE"
,
"INVISIBLE_MEMBER"
)
@Suppress
(
"INVISIBLE_REFERENCE"
,
"INVISIBLE_MEMBER"
)
@OptIn
(
MiraiInternalAPI
::
class
,
LowLevelAPI
::
class
)
@OptIn
(
MiraiInternalAPI
::
class
,
LowLevelAPI
::
class
,
ExperimentalStdlibApi
::
class
)
internal
fun
List
<
ImMsgBody
.
Elem
>.
joinToMessageChain
(
groupIdOrZero
:
Long
,
bot
:
Bot
,
message
:
MessageChainBuilder
)
{
internal
fun
List
<
ImMsgBody
.
Elem
>.
joinToMessageChain
(
groupIdOrZero
:
Long
,
bot
:
Bot
,
list
:
MessageChainBuilder
)
{
// (this._miraiContentToString())
// (this._miraiContentToString())
this
.
forEach
{
element
->
this
.
forEach
{
element
->
when
{
when
{
element
.
srcMsg
!=
null
->
element
.
srcMsg
!=
null
->
{
message
.
add
(
QuoteReply
(
OfflineMessageSourceImplBySourceMsg
(
element
.
srcMsg
,
bot
,
groupIdOrZero
)))
list
.
add
(
QuoteReply
(
OfflineMessageSourceImplBySourceMsg
(
element
.
srcMsg
,
bot
,
groupIdOrZero
)))
element
.
notOnlineImage
!=
null
->
message
.
add
(
OnlineFriendImageImpl
(
element
.
notOnlineImage
))
}
element
.
customFace
!=
null
->
message
.
add
(
OnlineGroupImageImpl
(
element
.
customFace
))
element
.
notOnlineImage
!=
null
->
list
.
add
(
OnlineFriendImageImpl
(
element
.
notOnlineImage
))
element
.
face
!=
null
->
message
.
add
(
Face
(
element
.
face
.
index
))
element
.
customFace
!=
null
->
list
.
add
(
OnlineGroupImageImpl
(
element
.
customFace
))
element
.
face
!=
null
->
list
.
add
(
Face
(
element
.
face
.
index
))
element
.
text
!=
null
->
{
element
.
text
!=
null
->
{
if
(
element
.
text
.
attr6Buf
.
isEmpty
())
{
if
(
element
.
text
.
attr6Buf
.
isEmpty
())
{
message
.
add
(
element
.
text
.
str
.
toMessage
())
list
.
add
(
element
.
text
.
str
.
toMessage
())
}
else
{
}
else
{
val
id
:
Long
val
id
:
Long
element
.
text
.
attr6Buf
.
read
{
element
.
text
.
attr6Buf
.
read
{
...
@@ -264,9 +277,9 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
...
@@ -264,9 +277,9 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
id
=
readUInt
().
toLong
()
id
=
readUInt
().
toLong
()
}
}
if
(
id
==
0L
)
{
if
(
id
==
0L
)
{
message
.
add
(
AtAll
)
list
.
add
(
AtAll
)
}
else
{
}
else
{
message
.
add
(
At
.
_lowLevelConstructAtInstance
(
id
,
element
.
text
.
str
))
list
.
add
(
At
.
_lowLevelConstructAtInstance
(
id
,
element
.
text
.
str
))
}
}
}
}
}
}
...
@@ -279,7 +292,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
...
@@ -279,7 +292,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
else
->
error
(
"unknown compression flag=${element.lightApp.data[0]}"
)
else
->
error
(
"unknown compression flag=${element.lightApp.data[0]}"
)
}
}
}
}
message
.
add
(
LightApp
(
content
))
list
.
add
(
LightApp
(
content
))
}
}
element
.
richMsg
!=
null
->
{
element
.
richMsg
!=
null
->
{
val
content
=
runWithBugReport
(
"解析 richMsg"
,
{
element
.
richMsg
.
template1
.
toUHexString
()
})
{
val
content
=
runWithBugReport
(
"解析 richMsg"
,
{
element
.
richMsg
.
template1
.
toUHexString
()
})
{
...
@@ -301,7 +314,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
...
@@ -301,7 +314,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
/**
/**
* [JsonMessage]
* [JsonMessage]
*/
*/
1
->
message
.
add
(
JsonMessage
(
content
))
1
->
list
.
add
(
JsonMessage
(
content
))
/**
/**
* [LongMessage], [ForwardMessage]
* [LongMessage], [ForwardMessage]
*/
*/
...
@@ -309,17 +322,17 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
...
@@ -309,17 +322,17 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
val
resId
=
this
.
firstIsInstanceOrNull
<
ImMsgBody
.
GeneralFlags
>()
?.
longTextResid
val
resId
=
this
.
firstIsInstanceOrNull
<
ImMsgBody
.
GeneralFlags
>()
?.
longTextResid
if
(
resId
!=
null
)
{
if
(
resId
!=
null
)
{
message
.
add
(
LongMessage
(
content
,
resId
))
list
.
add
(
LongMessage
(
content
,
resId
))
}
else
{
}
else
{
message
.
add
(
ForwardMessage
(
content
))
list
.
add
(
ForwardMessage
(
content
))
}
}
}
}
// 104 新群员入群的消息
// 104 新群员入群的消息
else
->
{
else
->
{
if
(
element
.
richMsg
.
serviceId
==
60
||
content
.
startsWith
(
"<?"
))
{
if
(
element
.
richMsg
.
serviceId
==
60
||
content
.
startsWith
(
"<?"
))
{
message
.
add
(
XmlMessage
(
element
.
richMsg
.
serviceId
,
content
))
list
.
add
(
XmlMessage
(
element
.
richMsg
.
serviceId
,
content
))
}
else
message
.
add
(
ServiceMessage
(
element
.
richMsg
.
serviceId
,
content
))
}
else
list
.
add
(
ServiceMessage
(
element
.
richMsg
.
serviceId
,
content
))
}
}
}
}
}
}
...
@@ -328,19 +341,45 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
...
@@ -328,19 +341,45 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
||
element
.
generalFlags
!=
null
->
{
||
element
.
generalFlags
!=
null
->
{
}
}
element
.
customElem
!=
null
->
{
element
.
customElem
.
data
.
read
{
kotlin
.
runCatching
{
CustomMessage
.
deserialize
(
this
)
}.
fold
(
onFailure
=
{
if
(
it
is
CustomMessage
.
Key
.
CustomMessageFullDataDeserializeInternalException
)
{
bot
.
logger
.
error
(
"Internal error: "
+
"exception while deserializing CustomMessage head data,"
+
" data=${element.customElem.data.toUHexString()}"
,
it
)
}
else
{
it
as
CustomMessage
.
Key
.
CustomMessageFullDataDeserializeUserException
bot
.
logger
.
error
(
"User error: "
+
"exception while deserializing CustomMessage body,"
+
" body=${it.body.toUHexString()}"
,
it
)
}
},
onSuccess
=
{
if
(
it
!=
null
)
{
list
.
add
(
it
)
}
}
)
}
}
element
.
commonElem
!=
null
->
{
element
.
commonElem
!=
null
->
{
when
(
element
.
commonElem
.
serviceType
)
{
when
(
element
.
commonElem
.
serviceType
)
{
2
->
{
2
->
{
val
proto
=
element
.
commonElem
.
pbElem
.
loadAs
(
HummerCommelem
.
MsgElemInfoServtype2
.
serializer
())
val
proto
=
element
.
commonElem
.
pbElem
.
loadAs
(
HummerCommelem
.
MsgElemInfoServtype2
.
serializer
())
message
.
add
(
PokeMessage
(
proto
.
pokeType
,
proto
.
vaspokeId
))
list
.
add
(
PokeMessage
(
proto
.
pokeType
,
proto
.
vaspokeId
))
}
}
3
->
{
3
->
{
val
proto
=
element
.
commonElem
.
pbElem
.
loadAs
(
HummerCommelem
.
MsgElemInfoServtype3
.
serializer
())
val
proto
=
element
.
commonElem
.
pbElem
.
loadAs
(
HummerCommelem
.
MsgElemInfoServtype3
.
serializer
())
if
(
proto
.
flashTroopPic
!=
null
)
{
if
(
proto
.
flashTroopPic
!=
null
)
{
message
.
add
(
GroupFlashImage
(
OnlineGroupImageImpl
(
proto
.
flashTroopPic
)))
list
.
add
(
GroupFlashImage
(
OnlineGroupImageImpl
(
proto
.
flashTroopPic
)))
}
}
if
(
proto
.
flashC2cPic
!=
null
)
{
if
(
proto
.
flashC2cPic
!=
null
)
{
message
.
add
(
FriendFlashImage
(
OnlineFriendImageImpl
(
proto
.
flashC2cPic
)))
list
.
add
(
FriendFlashImage
(
OnlineFriendImageImpl
(
proto
.
flashC2cPic
)))
}
}
}
}
}
}
...
...
mirai-core-qqandroid/src/commonTest/kotlin/samples/CustomMessageSamples.kt
0 → 100644
View file @
96610b30
/*
* 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_API_USAGE"
,
"EXPERIMENTAL_OVERRIDE"
)
package
samples
import
kotlinx.serialization.Serializable
import
net.mamoe.mirai.message.data.CustomMessage
import
net.mamoe.mirai.message.data.CustomMessageMetadata
/**
* 定义一个自定义消息类型.
* 在消息链中加入这个元素, 即可像普通元素一样发送和接收 (自动解析).
*/
@Serializable
data class
CustomMessageIdentifier
(
val
identifier1
:
Long
,
val
custom
:
String
)
:
CustomMessageMetadata
()
{
// 可使用 JsonSerializerFactory 或 ProtoBufSerializerFactory
companion
object
Factory
:
CustomMessage
.
ProtoBufSerializerFactory
<
CustomMessageIdentifier
>(
"myMessage.CustomMessageIdentifier"
)
override
fun
getFactory
():
Factory
=
Factory
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt
View file @
96610b30
...
@@ -57,7 +57,7 @@ internal constructor(
...
@@ -57,7 +57,7 @@ internal constructor(
@OptIn
(
MiraiExperimentalAPI
::
class
)
@OptIn
(
MiraiExperimentalAPI
::
class
)
override
fun
toString
():
String
{
override
fun
toString
():
String
{
return
tail
.
toString
()
+
left
.
toString
()
return
left
.
toString
()
+
tail
.
toString
()
}
}
override
fun
contentToString
():
String
{
override
fun
contentToString
():
String
{
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CustomMessage.kt
0 → 100644
View file @
96610b30
/*
* 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.message.data
import
kotlinx.io.core.*
import
kotlinx.serialization.KSerializer
import
kotlinx.serialization.Serializable
import
kotlinx.serialization.UnstableDefault
import
kotlinx.serialization.json.Json
import
kotlinx.serialization.json.JsonConfiguration
import
kotlinx.serialization.protobuf.ProtoBuf
import
kotlinx.serialization.protobuf.ProtoId
import
net.mamoe.mirai.utils.*
/**
* 自定义消息
*
* 它不会显示在消息文本中, 也不会被其他客户端识别.
* 只有 mirai 才能识别这些消息.
*
* 目前在回复时无法通过 [originalMessage] 获取自定义类型消息
*
* **实现方法**:
*
* @sample samples.CustomMessageIdentifier 实现示例
*/
@SinceMirai
(
"0.38.0"
)
@MiraiExperimentalAPI
sealed
class
CustomMessage
:
SingleMessage
{
/**
* 获取这个消息的工厂
*/
abstract
fun
getFactory
():
Factory
<
out
CustomMessage
>
/**
* 序列化和反序列化此消息的工厂, 将会自动注册.
* 应实现为 `object`.
*
* @see JsonSerializerFactory 使用 [Json] 作为序列模式的 [Factory]
* @see ProtoBufSerializerFactory 使用 [ProtoBuf] 作为序列模式的 [Factory]
*/
@MiraiExperimentalAPI
abstract
class
Factory
<
M
:
CustomMessage
>(
/**
* 此类型消息的名称.
* 在发往服务器时使用此名称.
* 应确保唯一且不变.
*/
final
override
val
typeName
:
String
)
:
Message
.
Key
<
M
>
{
init
{
@Suppress
(
"LeakingThis"
)
register
(
this
)
}
/**
* 序列化此消息.
*/
@Throws
(
Exception
::
class
)
abstract
fun
serialize
(
message
:
@UnsafeVariance
M
):
ByteArray
/**
* 从 [input] 读取此消息.
*/
@Throws
(
Exception
::
class
)
abstract
fun
deserialize
(
input
:
ByteArray
):
@UnsafeVariance
M
}
/**
* 使用 [ProtoBuf] 作为序列模式的 [Factory].
* 推荐使用此工厂
*/
abstract
class
ProtoBufSerializerFactory
<
M
:
CustomMessage
>(
typeName
:
String
)
:
Factory
<
M
>(
typeName
)
{
/**
* 得到 [M] 的 [KSerializer].
*/
abstract
fun
serializer
():
KSerializer
<
M
>
override
fun
serialize
(
message
:
M
):
ByteArray
=
ProtoBuf
.
dump
(
serializer
(),
message
)
override
fun
deserialize
(
input
:
ByteArray
):
M
=
ProtoBuf
.
load
(
serializer
(),
input
)
}
/**
* 使用 [Json] 作为序列模式的 [Factory]
* 推荐在调试时使用此工厂
*/
abstract
class
JsonSerializerFactory
<
M
:
CustomMessage
>(
typeName
:
String
)
:
Factory
<
M
>(
typeName
)
{
/**
* 得到 [M] 的 [KSerializer].
*/
abstract
fun
serializer
():
KSerializer
<
M
>
@OptIn
(
UnstableDefault
::
class
)
open
val
json
=
Json
(
JsonConfiguration
.
Default
)
override
fun
serialize
(
message
:
M
):
ByteArray
=
json
.
stringify
(
serializer
(),
message
).
toByteArray
()
override
fun
deserialize
(
input
:
ByteArray
):
M
=
json
.
parse
(
serializer
(),
String
(
input
))
}
companion
object
Key
:
Message
.
Key
<
CustomMessage
>
{
override
val
typeName
:
String
get
()
=
"CustomMessage"
private
val
factories
:
LockFreeLinkedList
<
Factory
<
*
>>
=
LockFreeLinkedList
()
internal
fun
register
(
factory
:
Factory
<
out
CustomMessage
>)
{
factories
.
removeIf
{
it
::
class
== factory::class }
val exist = factories.as
Sequence
().
firstOrNull
{
it
.
typeName
==
factory
.
typeName
}
if
(
exist
!=
null
)
{
error
(
"CustomMessage.Factory typeName ${factory.typeName} is already registered by ${exist::class.qualifiedName}"
)
}
factories
.
addLast
(
factory
)
}
@Serializable
class
CustomMessageFullData
(
@ProtoId
(
1
)
val
miraiVersionFlag
:
Int
,
@ProtoId
(
2
)
val
typeName
:
String
,
@ProtoId
(
3
)
val
data
:
ByteArray
)
class
CustomMessageFullDataDeserializeInternalException
(
cause
:
Throwable
?)
:
RuntimeException
(
cause
)
class
CustomMessageFullDataDeserializeUserException
(
val
body
:
ByteArray
,
cause
:
Throwable
?)
:
RuntimeException
(
cause
)
internal
fun
deserialize
(
fullData
:
ByteReadPacket
):
CustomMessage
?
{
val
msg
=
kotlin
.
runCatching
{
val
length
=
fullData
.
readInt
()
if
(
fullData
.
remaining
!=
length
.
toLong
())
{
return
null
}
ProtoBuf
.
load
(
CustomMessageFullData
.
serializer
(),
fullData
.
readBytes
(
length
))
}.
getOrElse
{
throw
CustomMessageFullDataDeserializeInternalException
(
it
)
}
return
kotlin
.
runCatching
{
when
(
msg
.
miraiVersionFlag
)
{
1
->
factories
.
asSequence
().
firstOrNull
{
it
.
typeName
==
msg
.
typeName
}
?.
deserialize
(
msg
.
data
)
else
->
null
}
}.
getOrElse
{
throw
CustomMessageFullDataDeserializeUserException
(
msg
.
data
,
it
)
}
}
internal
fun
<
M
:
CustomMessage
>
serialize
(
factory
:
Factory
<
M
>,
message
:
M
):
ByteArray
=
buildPacket
{
ProtoBuf
.
dump
(
CustomMessageFullData
.
serializer
(),
CustomMessageFullData
(
miraiVersionFlag
=
1
,
typeName
=
factory
.
typeName
,
data
=
factory
.
serialize
(
message
)
)).
let
{
data
->
writeInt
(
data
.
size
)
writeFully
(
data
)
}
}.
readBytes
()
}
}
/**
* 自定义消息元数据.
*
* @see CustomMessage 查看更多信息
* @see ConstrainSingle 可实现此接口以保证消息链中只存在一个元素
*/
@SinceMirai
(
"0.38.0"
)
@MiraiExperimentalAPI
abstract
class
CustomMessageMetadata
:
CustomMessage
(),
MessageMetadata
{
companion
object
Key
:
Message
.
Key
<
CustomMessageMetadata
>
{
override
val
typeName
:
String
get
()
=
"CustomMessageMetadata"
}
open
fun
customToString
():
ByteArray
=
customToStringImpl
(
this
.
getFactory
())
final
override
fun
toString
():
String
=
"[mirai:custom:${getFactory().typeName}:${String(customToString())}]"
final
override
fun
contentToString
():
String
=
""
}
@OptIn
(
MiraiExperimentalAPI
::
class
)
internal
fun
<
T
:
CustomMessageMetadata
>
T
.
customToStringImpl
(
factory
:
CustomMessage
.
Factory
<
*
>):
ByteArray
{
@Suppress
(
"UNCHECKED_CAST"
)
return
(
factory
as
CustomMessage
.
Factory
<
T
>).
serialize
(
this
)
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt
View file @
96610b30
...
@@ -13,7 +13,6 @@ package net.mamoe.mirai.message.data
...
@@ -13,7 +13,6 @@ package net.mamoe.mirai.message.data
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.utils.MiraiExperimentalAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.PlannedRemoval
import
net.mamoe.mirai.utils.PlannedRemoval
import
net.mamoe.mirai.utils.SinceMirai
import
net.mamoe.mirai.utils.SinceMirai
...
@@ -280,11 +279,9 @@ interface MessageMetadata : SingleMessage {
...
@@ -280,11 +279,9 @@ interface MessageMetadata : SingleMessage {
/**
/**
* 约束一个 [MessageChain] 中只存在这一种类型的元素. 新元素将会替换旧元素, 保持原顺序.
* 约束一个 [MessageChain] 中只存在这一种类型的元素. 新元素将会替换旧元素, 保持原顺序.
*
* 实现此接口的元素将会在连接时自动处理替换.
* **MiraiExperimentalAPI**: 此 API 可能在将来版本修改
*/
*/
@SinceMirai
(
"0.34.0"
)
@SinceMirai
(
"0.34.0"
)
@MiraiExperimentalAPI
interface
ConstrainSingle
<
out
M
:
Message
>
:
MessageMetadata
{
interface
ConstrainSingle
<
out
M
:
Message
>
:
MessageMetadata
{
val
key
:
Message
.
Key
<
M
>
val
key
:
Message
.
Key
<
M
>
}
}
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/impl.kt
View file @
96610b30
...
@@ -202,7 +202,21 @@ internal fun <M : Message> MessageChain.firstOrNullImpl(key: Message.Key<M>): M?
...
@@ -202,7 +202,21 @@ internal fun <M : Message> MessageChain.firstOrNullImpl(key: Message.Key<M>): M?
FlashImage
->
firstIsInstanceOrNull
<
FlashImage
>()
FlashImage
->
firstIsInstanceOrNull
<
FlashImage
>()
GroupFlashImage
->
firstIsInstanceOrNull
<
GroupFlashImage
>()
GroupFlashImage
->
firstIsInstanceOrNull
<
GroupFlashImage
>()
FriendFlashImage
->
firstIsInstanceOrNull
<
FriendFlashImage
>()
FriendFlashImage
->
firstIsInstanceOrNull
<
FriendFlashImage
>()
else
->
null
CustomMessage
->
firstIsInstanceOrNull
()
CustomMessageMetadata
->
firstIsInstanceOrNull
()
else
->
{
this
.
forEach
{
message
->
if
(
message
is
CustomMessage
)
{
@Suppress
(
"UNCHECKED_CAST"
)
if
(
message
.
getFactory
()
==
key
)
{
return
message
as
?
M
?:
error
(
"cannot cast ${message::class.qualifiedName}. Make sure CustomMessage.getFactory returns a factory that has a generic type which is the same as the type of your CustomMessage"
)
}
}
}
null
}
}
as
M
?
}
as
M
?
/**
/**
...
...
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