Commit 8ca4357e authored by Him188's avatar Him188 Committed by GitHub

Merge pull request #304 from mamoe/1.0.0

1.0.0
parents 01d16253 2d9db234
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
#### `OfflineMessageSource` 构造 #### `OfflineMessageSource` 构造
可使用 DSL 构造离线消息, 修改其发送人, 发送时间, 发送内容等. 这对于跨群转发等情况十分有用. 可使用 DSL 构造离线消息, 修改其发送人, 发送时间, 发送内容等. 这对于跨群转发等情况十分有用.
[OfflineMessageSource.kt: Line 90](mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/OfflineMessageSource.kt#L90) [MessageSourceBuilder.kt: Line 90](mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSourceBuilder.kt#L90)
DSL 总览: DSL 总览:
``` ```
val source: OfflineMessageSource = bot.buildMessageSource { val source: OfflineMessageSource = bot.buildMessageSource {
......
...@@ -6,7 +6,7 @@ import kotlin.math.pow ...@@ -6,7 +6,7 @@ import kotlin.math.pow
buildscript { buildscript {
repositories { repositories {
mavenLocal() mavenLocal()
maven(url = "https://mirrors.huaweicloud.com/repository/maven") // maven(url = "https://mirrors.huaweicloud.com/repository/maven")
maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
jcenter() jcenter()
google() google()
...@@ -51,7 +51,7 @@ allprojects { ...@@ -51,7 +51,7 @@ allprojects {
version = Versions.Mirai.version version = Versions.Mirai.version
repositories { repositories {
maven(url = "https://mirrors.huaweicloud.com/repository/maven") // maven(url = "https://mirrors.huaweicloud.com/repository/maven")
maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
jcenter() jcenter()
google() google()
......
...@@ -24,6 +24,8 @@ object Versions { ...@@ -24,6 +24,8 @@ object Versions {
const val dokka = "0.10.1" const val dokka = "0.10.1"
} }
const val jcekt = "1.0.0"
object Android { object Android {
const val androidGradlePlugin = "3.5.3" const val androidGradlePlugin = "3.5.3"
} }
......
...@@ -51,6 +51,7 @@ kotlin { ...@@ -51,6 +51,7 @@ kotlin {
api(kotlin("stdlib", Versions.Kotlin.stdlib)) api(kotlin("stdlib", Versions.Kotlin.stdlib))
api(kotlinx("serialization-runtime-common", Versions.Kotlin.serialization)) api(kotlinx("serialization-runtime-common", Versions.Kotlin.serialization))
api(kotlinx("serialization-protobuf-common", Versions.Kotlin.serialization)) api(kotlinx("serialization-protobuf-common", Versions.Kotlin.serialization))
api("moe.him188:jcekt-common:${Versions.jcekt}")
api("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}") api("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}")
api(kotlinx("io", Versions.Kotlin.io)) api(kotlinx("io", Versions.Kotlin.io))
api(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo)) api(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo))
...@@ -86,6 +87,7 @@ kotlin { ...@@ -86,6 +87,7 @@ kotlin {
dependencies { dependencies {
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
// api(kotlinx("coroutines-debug", "1.3.5")) // api(kotlinx("coroutines-debug", "1.3.5"))
api("moe.him188:jcekt:${Versions.jcekt}")
api(kotlinx("serialization-runtime", Versions.Kotlin.serialization)) api(kotlinx("serialization-runtime", Versions.Kotlin.serialization))
//api(kotlinx("serialization-protobuf", Versions.Kotlin.serialization)) //api(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
......
...@@ -15,7 +15,6 @@ import io.ktor.client.HttpClient ...@@ -15,7 +15,6 @@ import io.ktor.client.HttpClient
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.request.forms.MultiPartFormDataContent import io.ktor.client.request.forms.MultiPartFormDataContent
import io.ktor.client.request.forms.formData import io.ktor.client.request.forms.formData
import io.ktor.client.statement.HttpResponse
import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.io.ByteReadChannel import kotlinx.coroutines.io.ByteReadChannel
...@@ -51,7 +50,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList ...@@ -51,7 +50,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import net.mamoe.mirai.qqandroid.utils.encodeToString import net.mamoe.mirai.qqandroid.utils.encodeToString
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.utils.toReadPacket
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import kotlin.collections.asSequence import kotlin.collections.asSequence
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
...@@ -190,7 +188,7 @@ internal class QQAndroidBot constructor( ...@@ -190,7 +188,7 @@ internal class QQAndroidBot constructor(
} }
} }
group.checkBotPermissionOperator() group.checkBotPermission(MemberPermission.ADMINISTRATOR)
} }
override suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) { override suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) {
...@@ -351,10 +349,6 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -351,10 +349,6 @@ internal abstract class QQAndroidBotBase constructor(
return sequence return sequence
} }
override suspend fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult {
TODO("not implemented")
}
@Suppress("RemoveExplicitTypeArguments") // false positive @Suppress("RemoveExplicitTypeArguments") // false positive
override suspend fun recall(source: MessageSource) { override suspend fun recall(source: MessageSource) {
check(source is MessageSourceInternal) check(source is MessageSourceInternal)
...@@ -375,7 +369,7 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -375,7 +369,7 @@ internal abstract class QQAndroidBotBase constructor(
else -> error("stub") else -> error("stub")
} }
if (this.id != source.fromId) { if (this.id != source.fromId) {
group.checkBotPermissionOperator() group.checkBotPermission(MemberPermission.ADMINISTRATOR)
} }
MessageRecallEvent.GroupRecall( MessageRecallEvent.GroupRecall(
this, this,
...@@ -679,8 +673,8 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -679,8 +673,8 @@ internal abstract class QQAndroidBotBase constructor(
response.proto.uint32UpIp.zip(response.proto.uint32UpPort), response.proto.uint32UpIp.zip(response.proto.uint32UpPort),
response.proto.msgSig, response.proto.msgSig,
MiraiPlatformUtils.md5(body), MiraiPlatformUtils.md5(body),
body.toReadPacket(), @Suppress("INVISIBLE_REFERENCE")
body.size.toLong().and(0xFFFF_FFFF), // don't use toLongUnsigned: Overload resolution ambiguity net.mamoe.mirai.utils.internal.asReusableInput0(body), // don't use toLongUnsigned: Overload resolution ambiguity
"group long message", "group long message",
27 27
) )
...@@ -766,13 +760,6 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -766,13 +760,6 @@ internal abstract class QQAndroidBotBase constructor(
} }
} }
@Suppress("DeprecatedCallableAddReplaceWith")
@PlannedRemoval("1.0.0")
@Deprecated("use your own Http clients, this is going to be removed in 1.0.0", level = DeprecationLevel.WARNING)
override suspend fun openChannel(image: Image): ByteReadChannel {
return MiraiPlatformUtils.Http.get<HttpResponse>(queryImageUrl(image)).content.toKotlinByteReadChannel()
}
/** /**
* 获取 获取群公告 所需的 bkn 参数 * 获取 获取群公告 所需的 bkn 参数
* */ * */
......
...@@ -87,6 +87,10 @@ internal class FriendImpl( ...@@ -87,6 +87,10 @@ internal class FriendImpl(
@JvmSynthetic @JvmSynthetic
@OptIn(MiraiInternalAPI::class, ExperimentalStdlibApi::class, ExperimentalTime::class) @OptIn(MiraiInternalAPI::class, ExperimentalStdlibApi::class, ExperimentalTime::class)
override suspend fun uploadImage(image: ExternalImage): Image = try { override suspend fun uploadImage(image: ExternalImage): Image = try {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
if (image.input is net.mamoe.mirai.utils.internal.DeferredReusableInput) {
image.input.init(bot.configuration.fileCacheStrategy)
}
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) { if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup") throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
} }
...@@ -96,14 +100,15 @@ internal class FriendImpl( ...@@ -96,14 +100,15 @@ internal class FriendImpl(
srcUin = bot.id.toInt(), srcUin = bot.id.toInt(),
dstUin = id.toInt(), dstUin = id.toInt(),
fileId = 0, fileId = 0,
fileMd5 = image.md5, fileMd5 = @Suppress("INVISIBLE_MEMBER") image.md5,
fileSize = image.inputSize.toInt(), fileSize = @Suppress("INVISIBLE_MEMBER")
fileName = image.md5.toUHexString("") + "." + ExternalImage.defaultFormatName, image.input.size.toInt(),
fileName = @Suppress("INVISIBLE_MEMBER") image.md5.toUHexString("") + "." + ExternalImage.defaultFormatName,
imgOriginal = 1 imgOriginal = 1
) )
).sendAndExpect<LongConn.OffPicUp.Response>() ).sendAndExpect<LongConn.OffPicUp.Response>()
@Suppress("UNCHECKED_CAST", "DEPRECATION") // bug @Suppress("UNCHECKED_CAST", "DEPRECATION", "INVISIBLE_MEMBER")
return when (response) { return when (response) {
is LongConn.OffPicUp.Response.FileExists -> net.mamoe.mirai.message.data.OfflineFriendImage(response.resourceId) is LongConn.OffPicUp.Response.FileExists -> net.mamoe.mirai.message.data.OfflineFriendImage(response.resourceId)
.also { .also {
...@@ -111,7 +116,7 @@ internal class FriendImpl( ...@@ -111,7 +116,7 @@ internal class FriendImpl(
} }
is LongConn.OffPicUp.Response.RequireUpload -> { is LongConn.OffPicUp.Response.RequireUpload -> {
bot.network.logger.verbose { bot.network.logger.verbose {
"[Http] Uploading friend image, size=${image.inputSize.sizeToString()}" "[Http] Uploading friend image, size=${image.input.size.sizeToString()}"
} }
val time = measureTime { val time = measureTime {
...@@ -120,13 +125,12 @@ internal class FriendImpl( ...@@ -120,13 +125,12 @@ internal class FriendImpl(
bot.id, bot.id,
null, null,
imageInput = image.input, imageInput = image.input,
inputSize = image.inputSize,
uKeyHex = response.uKey.toUHexString("") uKeyHex = response.uKey.toUHexString("")
) )
} }
bot.network.logger.verbose { bot.network.logger.verbose {
"[Http] Uploading friend image: succeed at ${(image.inputSize.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s" "[Http] Uploading friend image: succeed at ${(image.input.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s"
} }
/* /*
...@@ -151,6 +155,7 @@ internal class FriendImpl( ...@@ -151,6 +155,7 @@ internal class FriendImpl(
} }
} }
} finally { } finally {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
(image.input as? Closeable)?.close() (image.input as? Closeable)?.close()
} }
} }
\ No newline at end of file
...@@ -109,7 +109,8 @@ internal class GroupImpl( ...@@ -109,7 +109,8 @@ internal class GroupImpl(
override var name: String override var name: String
get() = _name get() = _name
set(newValue) { set(newValue) {
checkBotPermissionOperator()
checkBotPermission(MemberPermission.ADMINISTRATOR)
if (_name != newValue) { if (_name != newValue) {
val oldValue = _name val oldValue = _name
_name = newValue _name = newValue
...@@ -131,7 +132,7 @@ internal class GroupImpl( ...@@ -131,7 +132,7 @@ internal class GroupImpl(
override var entranceAnnouncement: String override var entranceAnnouncement: String
get() = _announcement get() = _announcement
set(newValue) { set(newValue) {
checkBotPermissionOperator() checkBotPermission(MemberPermission.ADMINISTRATOR)
if (_announcement != newValue) { if (_announcement != newValue) {
val oldValue = _announcement val oldValue = _announcement
_announcement = newValue _announcement = newValue
...@@ -152,7 +153,7 @@ internal class GroupImpl( ...@@ -152,7 +153,7 @@ internal class GroupImpl(
override var isAllowMemberInvite: Boolean override var isAllowMemberInvite: Boolean
get() = _allowMemberInvite get() = _allowMemberInvite
set(newValue) { set(newValue) {
checkBotPermissionOperator() checkBotPermission(MemberPermission.ADMINISTRATOR)
if (_allowMemberInvite != newValue) { if (_allowMemberInvite != newValue) {
val oldValue = _allowMemberInvite val oldValue = _allowMemberInvite
_allowMemberInvite = newValue _allowMemberInvite = newValue
...@@ -186,7 +187,8 @@ internal class GroupImpl( ...@@ -186,7 +187,8 @@ internal class GroupImpl(
override var isConfessTalkEnabled: Boolean override var isConfessTalkEnabled: Boolean
get() = _confessTalk get() = _confessTalk
set(newValue) { set(newValue) {
checkBotPermissionOperator()
checkBotPermission(MemberPermission.ADMINISTRATOR)
if (_confessTalk != newValue) { if (_confessTalk != newValue) {
val oldValue = _confessTalk val oldValue = _confessTalk
_confessTalk = newValue _confessTalk = newValue
...@@ -207,7 +209,8 @@ internal class GroupImpl( ...@@ -207,7 +209,8 @@ internal class GroupImpl(
override var isMuteAll: Boolean override var isMuteAll: Boolean
get() = _muteAll get() = _muteAll
set(newValue) { set(newValue) {
checkBotPermissionOperator()
checkBotPermission(MemberPermission.ADMINISTRATOR)
if (_muteAll != newValue) { if (_muteAll != newValue) {
val oldValue = _muteAll val oldValue = _muteAll
_muteAll = newValue _muteAll = newValue
...@@ -403,6 +406,10 @@ internal class GroupImpl( ...@@ -403,6 +406,10 @@ internal class GroupImpl(
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
@JvmSynthetic @JvmSynthetic
override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage = try { override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage = try {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
if (image.input is net.mamoe.mirai.utils.internal.DeferredReusableInput) {
image.input.init(bot.configuration.fileCacheStrategy)
}
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) { if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup") throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
} }
...@@ -412,7 +419,7 @@ internal class GroupImpl( ...@@ -412,7 +419,7 @@ internal class GroupImpl(
uin = bot.id, uin = bot.id,
groupCode = id, groupCode = id,
md5 = image.md5, md5 = image.md5,
size = image.inputSize size = image.input.size.toInt()
).sendAndExpect() ).sendAndExpect()
@Suppress("UNCHECKED_CAST") // bug @Suppress("UNCHECKED_CAST") // bug
...@@ -432,7 +439,7 @@ internal class GroupImpl( ...@@ -432,7 +439,7 @@ internal class GroupImpl(
bot, bot,
response.uploadIpList.zip(response.uploadPortList), response.uploadIpList.zip(response.uploadPortList),
response.uKey, response.uKey,
image, image.input,
kind = "group image", kind = "group image",
commandId = 2 commandId = 2
) )
......
...@@ -105,7 +105,7 @@ internal class MemberImpl constructor( ...@@ -105,7 +105,7 @@ internal class MemberImpl constructor(
get() = _nameCard get() = _nameCard
set(newValue) { set(newValue) {
if (id != bot.id) { if (id != bot.id) {
group.checkBotPermissionOperator() group.checkBotPermission(MemberPermission.ADMINISTRATOR)
} }
if (_nameCard != newValue) { if (_nameCard != newValue) {
val oldValue = _nameCard val oldValue = _nameCard
...@@ -118,7 +118,7 @@ internal class MemberImpl constructor( ...@@ -118,7 +118,7 @@ internal class MemberImpl constructor(
newValue newValue
).sendWithoutExpect() ).sendWithoutExpect()
} }
MemberCardChangeEvent(oldValue, newValue, this@MemberImpl, null).broadcast() MemberCardChangeEvent(oldValue, newValue, this@MemberImpl).broadcast()
} }
} }
} }
...@@ -205,6 +205,7 @@ internal class MemberImpl constructor( ...@@ -205,6 +205,7 @@ internal class MemberImpl constructor(
check(response.success) { "kick failed: ${response.ret}" } check(response.success) { "kick failed: ${response.ret}" }
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
group.members.delegate.removeIf { it.id == this@MemberImpl.id } group.members.delegate.removeIf { it.id == this@MemberImpl.id }
MemberLeaveEvent.Kick(this@MemberImpl, null).broadcast() MemberLeaveEvent.Kick(this@MemberImpl, null).broadcast()
} }
......
...@@ -59,17 +59,17 @@ internal fun Contact.logMessageSent(message: Message) { ...@@ -59,17 +59,17 @@ internal fun Contact.logMessageSent(message: Message) {
} }
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class) @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
internal fun ContactMessage.logMessageReceived() { internal fun MessageEvent.logMessageReceived() {
when (this) { when (this) {
is GroupMessage -> bot.logger.verbose { is GroupMessageEvent -> bot.logger.verbose {
"[${group.name.singleLine()}(${group.id})] ${senderName.singleLine()}(${sender.id}) -> ${message.toString() "[${group.name.singleLine()}(${group.id})] ${senderName.singleLine()}(${sender.id}) -> ${message.toString()
.singleLine()}" .singleLine()}"
} }
is TempMessage -> bot.logger.verbose { is TempMessageEvent -> bot.logger.verbose {
"[${group.name.singleLine()}(${group.id})] ${senderName.singleLine()}(Temp ${sender.id}) -> ${message.toString() "[${group.name.singleLine()}(${group.id})] ${senderName.singleLine()}(Temp ${sender.id}) -> ${message.toString()
.singleLine()}" .singleLine()}"
} }
is FriendMessage -> bot.logger.verbose { is FriendMessageEvent -> bot.logger.verbose {
"${sender.nick.singleLine()}(${sender.id}) -> ${message.toString().singleLine()}" "${sender.nick.singleLine()}(${sender.id}) -> ${message.toString().singleLine()}"
} }
} }
......
...@@ -358,10 +358,10 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B ...@@ -358,10 +358,10 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
="" a_actionData="" url=""/></msg> ="" a_actionData="" url=""/></msg>
*/ */
/** /**
* [JsonMessage] * json?
*/ */
1 -> @Suppress("DEPRECATION_ERROR") 1 -> @Suppress("DEPRECATION_ERROR")
list.add(JsonMessage(content)) list.add(ServiceMessage(1, content))
/** /**
* [LongMessage], [ForwardMessage] * [LongMessage], [ForwardMessage]
*/ */
...@@ -381,7 +381,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B ...@@ -381,7 +381,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
else -> { else -> {
if (element.richMsg.serviceId == 60 || content.startsWith("<?")) { if (element.richMsg.serviceId == 60 || content.startsWith("<?")) {
@Suppress("DEPRECATION_ERROR") // bin comp @Suppress("DEPRECATION_ERROR") // bin comp
list.add(XmlMessage(element.richMsg.serviceId, content)) list.add(ServiceMessage(element.richMsg.serviceId, content))
} else list.add(ServiceMessage(element.richMsg.serviceId, content)) } else list.add(ServiceMessage(element.richMsg.serviceId, content))
} }
} }
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.qqandroid.network package net.mamoe.mirai.qqandroid.network
import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.AtomicRef
...@@ -20,7 +22,7 @@ import kotlinx.io.core.use ...@@ -20,7 +22,7 @@ import kotlinx.io.core.use
import net.mamoe.mirai.event.* import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.event.events.BotOnlineEvent import net.mamoe.mirai.event.events.BotOnlineEvent
import net.mamoe.mirai.message.ContactMessage import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.UnsupportedSMSLoginException import net.mamoe.mirai.network.UnsupportedSMSLoginException
import net.mamoe.mirai.network.WrongPasswordException import net.mamoe.mirai.network.WrongPasswordException
...@@ -44,6 +46,7 @@ import net.mamoe.mirai.qqandroid.utils.io.useBytes ...@@ -44,6 +46,7 @@ import net.mamoe.mirai.qqandroid.utils.io.useBytes
import net.mamoe.mirai.qqandroid.utils.retryCatching import net.mamoe.mirai.qqandroid.utils.retryCatching
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.jvm.JvmField
import kotlin.jvm.Volatile import kotlin.jvm.Volatile
import kotlin.time.ExperimentalTime import kotlin.time.ExperimentalTime
...@@ -204,6 +207,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo ...@@ -204,6 +207,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
private val _pendingEnabled = atomic(true) private val _pendingEnabled = atomic(true)
internal val pendingEnabled get() = _pendingEnabled.value internal val pendingEnabled get() = _pendingEnabled.value
@JvmField
@Volatile @Volatile
internal var pendingIncomingPackets: LockFreeLinkedList<KnownPacketFactories.IncomingPacket<*>>? = internal var pendingIncomingPackets: LockFreeLinkedList<KnownPacketFactories.IncomingPacket<*>>? =
LockFreeLinkedList() LockFreeLinkedList()
...@@ -491,7 +495,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo ...@@ -491,7 +495,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
is Packet.NoLog -> { is Packet.NoLog -> {
// nothing to do // nothing to do
} }
is ContactMessage -> packet.logMessageReceived() is MessageEvent -> packet.logMessageReceived()
is Event -> bot.logger.verbose { "Event: ${packet.toString().singleLine()}" } is Event -> bot.logger.verbose { "Event: ${packet.toString().singleLine()}" }
else -> logger.verbose { "Packet: ${packet.toString().singleLine()}" } else -> logger.verbose { "Packet: ${packet.toString().singleLine()}" }
} }
......
...@@ -77,6 +77,10 @@ internal open class QQAndroidClient( ...@@ -77,6 +77,10 @@ internal open class QQAndroidClient(
val device: DeviceInfo = SystemDeviceInfo(context), val device: DeviceInfo = SystemDeviceInfo(context),
bot: QQAndroidBot bot: QQAndroidBot
) { ) {
@Suppress("INVISIBLE_MEMBER")
val subAppId: Long
get() = bot.configuration.protocol.id
internal val serverList: MutableList<Pair<String, Int>> = DefaultServerList.toMutableList() internal val serverList: MutableList<Pair<String, Int>> = DefaultServerList.toMutableList()
val keys: Map<String, ByteArray> by lazy { val keys: Map<String, ByteArray> by lazy {
...@@ -368,7 +372,7 @@ internal class WLoginSigInfo( ...@@ -368,7 +372,7 @@ internal class WLoginSigInfo(
val deviceToken: ByteArray val deviceToken: ByteArray
) { ) {
override fun toString(): String { override fun toString(): String {
return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1?.toUHexString()}, noPicSig=${noPicSig?.toUHexString()}, G=${G.toUHexString()}, dpwd=${dpwd.toUHexString()}, randSeed=${randSeed.toUHexString()}, simpleInfo=$simpleInfo, appPri=$appPri, a2ExpiryTime=$a2ExpiryTime, loginBitmap=$loginBitmap, tgt=${tgt.toUHexString()}, a2CreationTime=$a2CreationTime, tgtKey=${tgtKey.toUHexString()}, userStSig=$userStSig, userStKey=${userStKey.toUHexString()}, userStWebSig=$userStWebSig, userA5=$userA5, userA8=$userA8, lsKey=$lsKey, sKey=$sKey, userSig64=$userSig64, openId=${openId.toUHexString()}, openKey=$openKey, vKey=$vKey, accessToken=$accessToken, d2=$d2, d2Key=${d2Key.toUHexString()}, sid=$sid, aqSig=$aqSig, psKey=${psKeyMap.toString()}, superKey=${superKey.toUHexString()}, payToken=${payToken.toUHexString()}, pf=${pf.toUHexString()}, pfKey=${pfKey.toUHexString()}, da2=${da2.toUHexString()}, wtSessionTicket=$wtSessionTicket, wtSessionTicketKey=${wtSessionTicketKey.toUHexString()}, deviceToken=${deviceToken.toUHexString()})" return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1?.toUHexString()}, noPicSig=${noPicSig?.toUHexString()}, G=${G.toUHexString()}, dpwd=${dpwd.toUHexString()}, randSeed=${randSeed.toUHexString()}, simpleInfo=$simpleInfo, appPri=$appPri, a2ExpiryTime=$a2ExpiryTime, loginBitmap=$loginBitmap, tgt=${tgt.toUHexString()}, a2CreationTime=$a2CreationTime, tgtKey=${tgtKey.toUHexString()}, userStSig=$userStSig, userStKey=${userStKey.toUHexString()}, userStWebSig=$userStWebSig, userA5=$userA5, userA8=$userA8, lsKey=$lsKey, sKey=$sKey, userSig64=$userSig64, openId=${openId.toUHexString()}, openKey=$openKey, vKey=$vKey, accessToken=$accessToken, d2=$d2, d2Key=${d2Key.toUHexString()}, sid=$sid, aqSig=$aqSig, psKey=$psKeyMap, superKey=${superKey.toUHexString()}, payToken=${payToken.toUHexString()}, pf=${pf.toUHexString()}, pfKey=${pfKey.toUHexString()}, da2=${da2.toUHexString()}, wtSessionTicket=$wtSessionTicket, wtSessionTicketKey=${wtSessionTicketKey.toUHexString()}, deviceToken=${deviceToken.toUHexString()})"
} }
} }
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
package net.mamoe.mirai.qqandroid.network.highway package net.mamoe.mirai.qqandroid.network.highway
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
...@@ -20,22 +22,24 @@ import io.ktor.utils.io.ByteWriteChannel ...@@ -20,22 +22,24 @@ import io.ktor.utils.io.ByteWriteChannel
import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.io.InputStream
import kotlinx.io.core.Input
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import kotlinx.io.core.readAvailable
import kotlinx.io.core.use import kotlinx.io.core.use
import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.InternalSerializationApi
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import net.mamoe.mirai.qqandroid.utils.* import net.mamoe.mirai.qqandroid.utils.PlatformSocket
import net.mamoe.mirai.qqandroid.utils.SocketException
import net.mamoe.mirai.qqandroid.utils.addSuppressedMirai
import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import net.mamoe.mirai.qqandroid.utils.io.withUse import net.mamoe.mirai.qqandroid.utils.io.withUse
import net.mamoe.mirai.utils.* import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.internal.ReusableInput
import net.mamoe.mirai.utils.verbose
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.time.ExperimentalTime import kotlin.time.ExperimentalTime
...@@ -47,8 +51,7 @@ internal suspend fun HttpClient.postImage( ...@@ -47,8 +51,7 @@ internal suspend fun HttpClient.postImage(
htcmd: String, htcmd: String,
uin: Long, uin: Long,
groupcode: Long?, groupcode: Long?,
imageInput: Any, // Input from kotlinx.io, InputStream from kotlinx.io MPP, ByteReadChannel from ktor imageInput: ReusableInput,
inputSize: Long,
uKeyHex: String uKeyHex: String
): Boolean = post<HttpStatusCode> { ): Boolean = post<HttpStatusCode> {
url { url {
...@@ -63,7 +66,7 @@ internal suspend fun HttpClient.postImage( ...@@ -63,7 +66,7 @@ internal suspend fun HttpClient.postImage(
parameters["term"] = "pc" parameters["term"] = "pc"
parameters["ver"] = "5603" parameters["ver"] = "5603"
parameters["filesize"] = inputSize.toString() parameters["filesize"] = imageInput.size.toString()
parameters["range"] = 0.toString() parameters["range"] = 0.toString()
parameters["ukey"] = uKeyHex parameters["ukey"] = uKeyHex
...@@ -72,63 +75,46 @@ internal suspend fun HttpClient.postImage( ...@@ -72,63 +75,46 @@ internal suspend fun HttpClient.postImage(
body = object : OutgoingContent.WriteChannelContent() { body = object : OutgoingContent.WriteChannelContent() {
override val contentType: ContentType = ContentType.Image.Any override val contentType: ContentType = ContentType.Image.Any
override val contentLength: Long = inputSize override val contentLength: Long = imageInput.size
@OptIn(MiraiExperimentalAPI::class) @OptIn(MiraiExperimentalAPI::class)
override suspend fun writeTo(channel: ByteWriteChannel) { override suspend fun writeTo(channel: ByteWriteChannel) {
ByteArrayPool.useInstance { buffer: ByteArray -> imageInput.writeTo(channel)
when (imageInput) {
is Input -> {
var size: Int
while (imageInput.readAvailable(buffer).also { size = it } > 0) {
channel.writeFully(buffer, 0, size)
channel.flush()
}
}
is ByteReadChannel -> imageInput.copyAndClose(channel)
is InputStream -> {
var size: Int
while (imageInput.read(buffer).also { size = it } > 0) {
channel.writeFully(buffer, 0, size)
channel.flush()
}
}
else -> error("unsupported imageInput: ${imageInput::class.simpleName}")
}
}
} }
} }
} == HttpStatusCode.OK } == HttpStatusCode.OK
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class) @OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
internal object HighwayHelper { internal object HighwayHelper {
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
suspend fun uploadImageToServers( suspend fun uploadImageToServers(
bot: QQAndroidBot, bot: QQAndroidBot,
servers: List<Pair<Int, Int>>, servers: List<Pair<Int, Int>>,
uKey: ByteArray, uKey: ByteArray,
image: ExternalImage, image: ReusableInput,
kind: String, kind: String,
commandId: Int commandId: Int
) = uploadImageToServers(bot, servers, uKey, image.md5, image.input, image.inputSize, kind, commandId) ) = uploadImageToServers(bot, servers, uKey, image.md5, image, kind, commandId)
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
suspend fun uploadImageToServers( suspend fun uploadImageToServers(
bot: QQAndroidBot, bot: QQAndroidBot,
servers: List<Pair<Int, Int>>, servers: List<Pair<Int, Int>>,
uKey: ByteArray, uKey: ByteArray,
md5: ByteArray, md5: ByteArray,
input: Any, input: ReusableInput,
inputSize: Long,
kind: String, kind: String,
commandId: Int commandId: Int
) = servers.retryWithServers( ) = servers.retryWithServers(
(inputSize * 1000 / 1024 / 10).coerceAtLeast(5000), (input.size * 1000 / 1024 / 10).coerceAtLeast(5000),
onFail = { onFail = {
throw IllegalStateException("cannot upload $kind, failed on all servers.", it) throw IllegalStateException("cannot upload $kind, failed on all servers.", it)
} }
) { ip, port -> ) { ip, port ->
bot.network.logger.verbose { bot.network.logger.verbose {
"[Highway] Uploading $kind to ${ip}:$port, size=${inputSize.sizeToString()}" "[Highway] Uploading $kind to ${ip}:$port, size=${input.size.sizeToString()}"
} }
val time = measureTime { val time = measureTime {
...@@ -137,7 +123,6 @@ internal object HighwayHelper { ...@@ -137,7 +123,6 @@ internal object HighwayHelper {
serverIp = ip, serverIp = ip,
serverPort = port, serverPort = port,
imageInput = input, imageInput = input,
inputSize = inputSize.toInt(),
fileMd5 = md5, fileMd5 = md5,
ticket = uKey, ticket = uKey,
commandId = commandId commandId = commandId
...@@ -145,22 +130,21 @@ internal object HighwayHelper { ...@@ -145,22 +130,21 @@ internal object HighwayHelper {
} }
bot.network.logger.verbose { bot.network.logger.verbose {
"[Highway] Uploading $kind: succeed at ${(inputSize.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s" "[Highway] Uploading $kind: succeed at ${(input.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s"
} }
} }
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@OptIn(InternalCoroutinesApi::class) @OptIn(InternalCoroutinesApi::class)
internal suspend fun uploadImage( internal suspend fun uploadImage(
client: QQAndroidClient, client: QQAndroidClient,
serverIp: String, serverIp: String,
serverPort: Int, serverPort: Int,
ticket: ByteArray, ticket: ByteArray,
imageInput: Any, imageInput: ReusableInput,
inputSize: Int,
fileMd5: ByteArray, fileMd5: ByteArray,
commandId: Int // group=2, friend=1 commandId: Int // group=2, friend=1
) { ) {
require(imageInput is Input || imageInput is InputStream || imageInput is ByteReadChannel) { "unsupported imageInput: ${imageInput::class.simpleName}" }
require(fileMd5.size == 16) { "bad md5. Required size=16, got ${fileMd5.size}" } require(fileMd5.size == 16) { "bad md5. Required size=16, got ${fileMd5.size}" }
// require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" } // require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" }
// require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" } // require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" }
...@@ -177,22 +161,24 @@ internal object HighwayHelper { ...@@ -177,22 +161,24 @@ internal object HighwayHelper {
socket.use { socket.use {
createImageDataPacketSequence( createImageDataPacketSequence(
client = client, client = client,
appId = client.subAppId.toInt(),
command = "PicUp.DataUp", command = "PicUp.DataUp",
commandId = commandId, commandId = commandId,
ticket = ticket, ticket = ticket,
data = imageInput, data = imageInput,
dataSize = inputSize,
fileMd5 = fileMd5 fileMd5 = fileMd5
).collect { ).withUse {
socket.send(it) flow.collect {
//0A 3C 08 01 12 0A 31 39 39 34 37 30 31 30 32 31 1A 0C 50 69 63 55 70 2E 44 61 74 61 55 70 20 E9 A7 05 28 00 30 BD DB 8B 80 02 38 80 20 40 02 4A 0A 38 2E 32 2E 30 2E 31 32 39 36 50 84 10 12 3D 08 00 10 FD 08 18 00 20 FD 08 28 C6 01 38 00 42 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 4A 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 50 89 92 A2 FB 06 58 00 60 00 18 53 20 01 28 00 30 04 3A 00 40 E6 B7 F7 D9 80 2E 48 00 50 00 socket.send(it)
//0A 3C 08 01 12 0A 31 39 39 34 37 30 31 30 32 31 1A 0C 50 69 63 55 70 2E 44 61 74 61 55 70 20 E9 A7 05 28 00 30 BD DB 8B 80 02 38 80 20 40 02 4A 0A 38 2E 32 2E 30 2E 31 32 39 36 50 84 10 12 3D 08 00 10 FD 08 18 00 20 FD 08 28 C6 01 38 00 42 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 4A 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 50 89 92 A2 FB 06 58 00 60 00 18 53 20 01 28 00 30 04 3A 00 40 E6 B7 F7 D9 80 2E 48 00 50 00
socket.read().withUse {
discardExact(1) socket.read().withUse {
val headLength = readInt() discardExact(1)
discardExact(4) val headLength = readInt()
val proto = readProtoBuf(CSDataHighwayHead.RspDataHighwayHead.serializer(), length = headLength) discardExact(4)
check(proto.errorCode == 0) { "highway transfer failed, error ${proto.errorCode}" } val proto = readProtoBuf(CSDataHighwayHead.RspDataHighwayHead.serializer(), length = headLength)
check(proto.errorCode == 0) { "highway transfer failed, error ${proto.errorCode}" }
}
} }
} }
} }
......
...@@ -11,12 +11,7 @@ ...@@ -11,12 +11,7 @@
package net.mamoe.mirai.qqandroid.network.highway package net.mamoe.mirai.qqandroid.network.highway
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.io.InputStream
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.InternalSerializationApi
...@@ -25,41 +20,34 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead ...@@ -25,41 +20,34 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.utils.ByteArrayPool import net.mamoe.mirai.qqandroid.utils.ByteArrayPool
import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import net.mamoe.mirai.qqandroid.utils.io.chunkedFlow
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.internal.ChunkedFlowSession
import net.mamoe.mirai.utils.internal.ChunkedInput
import net.mamoe.mirai.utils.internal.ReusableInput
import net.mamoe.mirai.utils.internal.map
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class) @OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
internal fun createImageDataPacketSequence( internal fun createImageDataPacketSequence(
// RequestDataTrans // RequestDataTrans
client: QQAndroidClient, client: QQAndroidClient,
command: String, command: String,
appId: Int = 537062845, appId: Int,
dataFlag: Int = 4096, dataFlag: Int = 4096,
commandId: Int, commandId: Int,
localId: Int = 2052, localId: Int = 2052,
ticket: ByteArray, ticket: ByteArray,
data: ReusableInput,
data: Any,
dataSize: Int,
fileMd5: ByteArray, fileMd5: ByteArray,
sizePerPacket: Int = ByteArrayPool.BUFFER_SIZE sizePerPacket: Int = ByteArrayPool.BUFFER_SIZE
): Flow<ByteReadPacket> { ): ChunkedFlowSession<ByteReadPacket> {
ByteArrayPool.checkBufferSize(sizePerPacket) ByteArrayPool.checkBufferSize(sizePerPacket)
require(data is Input || data is InputStream || data is ByteReadChannel) { "unsupported data: ${data::class.simpleName}" }
// require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" } // require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" }
require(data !is ByteReadPacket || data.remaining.toInt() == dataSize) { "bad input. given dataSize=$dataSize, but actual readRemaining=${(data as ByteReadPacket).remaining}" }
val flow = when (data) { val session: ChunkedFlowSession<ChunkedInput> = data.chunkedFlow(sizePerPacket)
is ByteReadPacket -> data.chunkedFlow(sizePerPacket)
is Input -> data.chunkedFlow(sizePerPacket)
is ByteReadChannel -> data.chunkedFlow(sizePerPacket)
is InputStream -> data.chunkedFlow(sizePerPacket)
else -> error("unreachable code")
}
var offset = 0L var offset = 0L
return flow.map { chunkedInput -> return session.map { chunkedInput ->
buildPacket { buildPacket {
val head = CSDataHighwayHead.ReqDataHighwayHead( val head = CSDataHighwayHead.ReqDataHighwayHead(
msgBasehead = CSDataHighwayHead.DataHighwayHead( msgBasehead = CSDataHighwayHead.DataHighwayHead(
...@@ -82,7 +70,7 @@ internal fun createImageDataPacketSequence( ...@@ -82,7 +70,7 @@ internal fun createImageDataPacketSequence(
// cacheAddr = 812157193, // cacheAddr = 812157193,
datalength = chunkedInput.bufferSize, datalength = chunkedInput.bufferSize,
dataoffset = offset, dataoffset = offset,
filesize = dataSize.toLong(), filesize = data.size,
serviceticket = ticket, serviceticket = ticket,
md5 = MiraiPlatformUtils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize), md5 = MiraiPlatformUtils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize),
fileMd5 = fileMd5, fileMd5 = fileMd5,
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.network.Packet import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Serializable @Serializable
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Serializable @Serializable
......
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Serializable @Serializable
......
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Serializable @Serializable
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
internal class OnlinePushPack { internal class OnlinePushPack {
......
...@@ -10,10 +10,10 @@ ...@@ -10,10 +10,10 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.network.Packet import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Suppress("ArrayInDataClass") @Suppress("ArrayInDataClass")
......
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Serializable @Serializable
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
private val EMPTY_MAP = mapOf<String, String>() private val EMPTY_MAP = mapOf<String, String>()
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Serializable @Serializable
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Serializable @Serializable
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
@Serializable @Serializable
......
...@@ -197,6 +197,7 @@ internal object KnownPacketFactories { ...@@ -197,6 +197,7 @@ internal object KnownPacketFactories {
readString(readInt() - 4)// uinAccount readString(readInt() - 4)// uinAccount
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
ByteArrayPool.useInstance(this.remaining.toInt()) { data -> ByteArrayPool.useInstance(this.remaining.toInt()) { data ->
val size = this.readAvailable(data) val size = this.readAvailable(data)
...@@ -219,6 +220,7 @@ internal object KnownPacketFactories { ...@@ -219,6 +220,7 @@ internal object KnownPacketFactories {
it as IncomingPacket<T> it as IncomingPacket<T>
if (it.packetFactory is IncomingPacketFactory<T> && it.packetFactory.canBeCached && bot.network.pendingEnabled) { if (it.packetFactory is IncomingPacketFactory<T> && it.packetFactory.canBeCached && bot.network.pendingEnabled) {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
bot.network.pendingIncomingPackets?.addLast(it.also { bot.network.pendingIncomingPackets?.addLast(it.also {
it.consumer = consumer it.consumer = consumer
it.flag2 = flag2 it.flag2 = flag2
...@@ -384,6 +386,7 @@ internal object KnownPacketFactories { ...@@ -384,6 +386,7 @@ internal object KnownPacketFactories {
} }
0 -> { 0 -> {
val data = if (bot.client.loginState == 0) { val data = if (bot.client.loginState == 0) {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
ByteArrayPool.useInstance(this.remaining.toInt()) { byteArrayBuffer -> ByteArrayPool.useInstance(this.remaining.toInt()) { byteArrayBuffer ->
val size = (this.remaining - 1).toInt() val size = (this.remaining - 1).toInt()
this.readFully(byteArrayBuffer, 0, size) this.readFully(byteArrayBuffer, 0, size)
......
...@@ -83,7 +83,7 @@ internal fun BytePacketBuilder.t18( ...@@ -83,7 +83,7 @@ internal fun BytePacketBuilder.t18(
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
internal fun BytePacketBuilder.t106( internal fun BytePacketBuilder.t106(
appId: Long = 16L, appId: Long = 16L,
subAppId: Long = 537062845L, subAppId: Long,
appClientVersion: Int = 0, appClientVersion: Int = 0,
uin: Long, uin: Long,
n5_always_1: Int = 1, n5_always_1: Int = 1,
...@@ -159,7 +159,7 @@ internal fun BytePacketBuilder.t116( ...@@ -159,7 +159,7 @@ internal fun BytePacketBuilder.t116(
internal fun BytePacketBuilder.t100( internal fun BytePacketBuilder.t100(
appId: Long = 16, appId: Long = 16,
subAppId: Long = 537062845, subAppId: Long,
appClientVersion: Int appClientVersion: Int
) { ) {
writeShort(0x100) writeShort(0x100)
......
...@@ -100,7 +100,7 @@ internal class TroopManagement { ...@@ -100,7 +100,7 @@ internal class TroopManagement {
serviceType = 7, serviceType = 7,
result = 0, result = 0,
bodybuffer = Oidb0x88d.ReqBody( bodybuffer = Oidb0x88d.ReqBody(
appid = 537062845, appid = client.subAppId.toInt(),
stzreqgroupinfo = listOf( stzreqgroupinfo = listOf(
Oidb0x88d.ReqGroupInfo( Oidb0x88d.ReqGroupInfo(
stgroupinfo = Oidb0x88d.GroupInfo( stgroupinfo = Oidb0x88d.GroupInfo(
......
...@@ -17,6 +17,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x388 ...@@ -17,6 +17,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x388
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket 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.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.toLongUnsigned
import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
import kotlin.random.Random import kotlin.random.Random
...@@ -27,9 +28,6 @@ internal fun getRandomString(length: Int): String = ...@@ -27,9 +28,6 @@ internal fun getRandomString(length: Int): String =
private val defaultRanges: Array<CharRange> = arrayOf('a'..'z', 'A'..'Z', '0'..'9') private val defaultRanges: Array<CharRange> = arrayOf('a'..'z', 'A'..'Z', '0'..'9')
internal fun getRandomString(length: Int, charRange: CharRange): String =
String(CharArray(length) { charRange.random() })
internal fun getRandomString(length: Int, vararg charRanges: CharRange): String = internal fun getRandomString(length: Int, vararg charRanges: CharRange): String =
String(CharArray(length) { charRanges[Random.Default.nextInt(0..charRanges.lastIndex)].random() }) String(CharArray(length) { charRanges[Random.Default.nextInt(0..charRanges.lastIndex)].random() })
...@@ -41,7 +39,7 @@ internal class ImgStore { ...@@ -41,7 +39,7 @@ internal class ImgStore {
uin: Long, uin: Long,
groupCode: Long, groupCode: Long,
md5: ByteArray, md5: ByteArray,
size: Long, size: Int,
picWidth: Int = 0, // not orthodox picWidth: Int = 0, // not orthodox
picHeight: Int = 0, // not orthodox picHeight: Int = 0, // not orthodox
picType: Int = 1000, picType: Int = 1000,
...@@ -63,7 +61,7 @@ internal class ImgStore { ...@@ -63,7 +61,7 @@ internal class ImgStore {
groupCode = groupCode, groupCode = groupCode,
srcUin = uin, srcUin = uin,
fileMd5 = md5, fileMd5 = md5,
fileSize = size, fileSize = size.toLongUnsigned(),
fileId = fileId, fileId = fileId,
fileName = filename, fileName = filename,
picWidth = picWidth, picWidth = picWidth,
......
...@@ -30,8 +30,8 @@ import net.mamoe.mirai.event.events.BotJoinGroupEvent ...@@ -30,8 +30,8 @@ import net.mamoe.mirai.event.events.BotJoinGroupEvent
import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.event.events.MemberJoinEvent import net.mamoe.mirai.event.events.MemberJoinEvent
import net.mamoe.mirai.getFriendOrNull import net.mamoe.mirai.getFriendOrNull
import net.mamoe.mirai.message.FriendMessage import net.mamoe.mirai.message.FriendMessageEvent
import net.mamoe.mirai.message.TempMessage import net.mamoe.mirai.message.TempMessageEvent
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.PttMessage import net.mamoe.mirai.message.data.PttMessage
import net.mamoe.mirai.message.data.Voice import net.mamoe.mirai.message.data.Voice
...@@ -228,6 +228,7 @@ internal class MessageSvc { ...@@ -228,6 +228,7 @@ internal class MessageSvc {
// 新群 // 新群
val newGroup = msg.getNewGroup(bot) ?: return@mapNotNull null val newGroup = msg.getNewGroup(bot) ?: return@mapNotNull null
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
bot.groups.delegate.addLast(newGroup) bot.groups.delegate.addLast(newGroup)
return@mapNotNull BotJoinGroupEvent(newGroup) return@mapNotNull BotJoinGroupEvent(newGroup)
} else { } else {
...@@ -237,6 +238,7 @@ internal class MessageSvc { ...@@ -237,6 +238,7 @@ internal class MessageSvc {
return@mapNotNull null return@mapNotNull null
} }
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
return@mapNotNull MemberJoinEvent.Invite(group.newMember(msg.getNewMemberInfo()) return@mapNotNull MemberJoinEvent.Invite(group.newMember(msg.getNewMemberInfo())
.also { group.members.delegate.addLast(it) }) .also { group.members.delegate.addLast(it) })
} }
...@@ -250,6 +252,7 @@ internal class MessageSvc { ...@@ -250,6 +252,7 @@ internal class MessageSvc {
if (group.members.contains(msg.msgHead.authUin)) { if (group.members.contains(msg.msgHead.authUin)) {
return@mapNotNull null return@mapNotNull null
} }
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
return@mapNotNull MemberJoinEvent.Active(group.newMember(msg.getNewMemberInfo()) return@mapNotNull MemberJoinEvent.Active(group.newMember(msg.getNewMemberInfo())
.also { group.members.delegate.addLast(it) }) .also { group.members.delegate.addLast(it) })
} }
...@@ -276,7 +279,7 @@ internal class MessageSvc { ...@@ -276,7 +279,7 @@ internal class MessageSvc {
friend.lastMessageSequence.loop { instant -> friend.lastMessageSequence.loop { instant ->
if (msg.msgHead.msgSeq > instant) { if (msg.msgHead.msgSeq > instant) {
if (friend.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) { if (friend.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) {
return@mapNotNull FriendMessage( return@mapNotNull FriendMessageEvent(
friend, friend,
msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true), msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true),
msg.msgHead.msgTime msg.msgHead.msgTime
...@@ -307,7 +310,7 @@ internal class MessageSvc { ...@@ -307,7 +310,7 @@ internal class MessageSvc {
member.lastMessageSequence.loop { instant -> member.lastMessageSequence.loop { instant ->
if (msg.msgHead.msgSeq > instant) { if (msg.msgHead.msgSeq > instant) {
if (member.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) { if (member.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) {
return@mapNotNull TempMessage( return@mapNotNull TempMessageEvent(
member, member,
msg.toMessageChain( msg.toMessageChain(
bot, bot,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE") @file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
...@@ -23,7 +23,7 @@ import net.mamoe.mirai.event.broadcast ...@@ -23,7 +23,7 @@ import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.* import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.getFriendOrNull import net.mamoe.mirai.getFriendOrNull
import net.mamoe.mirai.getGroupOrNull import net.mamoe.mirai.getGroupOrNull
import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.GroupMessageEvent
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.contact.* import net.mamoe.mirai.qqandroid.contact.*
import net.mamoe.mirai.qqandroid.message.contextualBugReportException import net.mamoe.mirai.qqandroid.message.contextualBugReportException
...@@ -87,7 +87,8 @@ internal class OnlinePush { ...@@ -87,7 +87,8 @@ internal class OnlinePush {
} }
} }
val group = bot.getGroupOrNull(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) as GroupImpl? ?: return null // 机器人还正在进群 val group =
bot.getGroupOrNull(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) as GroupImpl? ?: return null // 机器人还正在进群
val sender = if (anonymous != null) { val sender = if (anonymous != null) {
group.newAnonymous(anonymous.anonNick.encodeToString()) group.newAnonymous(anonymous.anonNick.encodeToString())
} else { } else {
...@@ -108,12 +109,12 @@ internal class OnlinePush { ...@@ -108,12 +109,12 @@ internal class OnlinePush {
} }
val flags = extraInfo?.flags ?: 0 val flags = extraInfo?.flags ?: 0
return GroupMessage( return GroupMessageEvent(
senderName = name.also { senderName = name.also {
if (it != sender.nameCard) { if (it != sender.nameCard) {
val origin = sender._nameCard val origin = sender._nameCard
sender._nameCard = name sender._nameCard = name
MemberCardChangeEvent(origin, name, sender, sender).broadcast() // 不知道operator MemberCardChangeEvent(origin, name, sender).broadcast()
} }
}, },
sender = sender, sender = sender,
...@@ -562,7 +563,7 @@ internal class OnlinePush { ...@@ -562,7 +563,7 @@ internal class OnlinePush {
if (new == old) return@mapNotNull null if (new == old) return@mapNotNull null
member._nameCard = new member._nameCard = new
return@mapNotNull MemberCardChangeEvent(old, new, member, null) return@mapNotNull MemberCardChangeEvent(old, new, member)
} }
2 -> { 2 -> {
if (info.value.singleOrNull()?.toInt() != 0) { if (info.value.singleOrNull()?.toInt() != 0) {
......
...@@ -24,11 +24,8 @@ import net.mamoe.mirai.qqandroid.utils.io.serialization.jceRequestSBuffer ...@@ -24,11 +24,8 @@ import net.mamoe.mirai.qqandroid.utils.io.serialization.jceRequestSBuffer
import net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket import net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeJceStruct import net.mamoe.mirai.qqandroid.utils.io.serialization.writeJceStruct
import net.mamoe.mirai.qqandroid.utils.toByteArray import net.mamoe.mirai.qqandroid.utils.toByteArray
import net.mamoe.mirai.utils.SinceMirai
internal class ProfileService { internal class ProfileService {
@SinceMirai("0.37.0")
object GroupMngReq : OutgoingPacketFactory<GroupMngReq.GroupMngReqResponse>("ProfileService.GroupMngReq") { object GroupMngReq : OutgoingPacketFactory<GroupMngReq.GroupMngReqResponse>("ProfileService.GroupMngReq") {
data class GroupMngReqResponse(val errorCode: Int, val errorMessage: String) : Packet data class GroupMngReqResponse(val errorCode: Int, val errorMessage: String) : Packet
......
...@@ -70,6 +70,7 @@ internal class ConfigPushSvc { ...@@ -70,6 +70,7 @@ internal class ConfigPushSvc {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): PushReqResponse? { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): PushReqResponse? {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
ByteArrayPool.useInstance(this.remaining.toInt()) { buffer -> ByteArrayPool.useInstance(this.remaining.toInt()) { buffer ->
val length = this.readAvailable(buffer) val length = this.readAvailable(buffer)
......
...@@ -25,7 +25,7 @@ internal class Heartbeat { ...@@ -25,7 +25,7 @@ internal class Heartbeat {
operator fun invoke( operator fun invoke(
client: QQAndroidClient client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client, 0, key = NO_ENCRYPT) { ): OutgoingPacket = buildLoginOutgoingPacket(client, 0, key = NO_ENCRYPT) {
writeSsoPacket(client, 537062845, commandName, sequenceId = it) { writeSsoPacket(client, client.subAppId, commandName, sequenceId = it) {
} }
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.login package net.mamoe.mirai.qqandroid.network.protocol.packet.login
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.Packet import net.mamoe.mirai.qqandroid.network.Packet
...@@ -88,8 +89,6 @@ internal class StatSvc { ...@@ -88,8 +89,6 @@ internal class StatSvc {
override fun toString(): String = "Response(StatSvc.register)" override fun toString(): String = "Response(StatSvc.register)"
} }
private const val subAppId = 537062845L
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
operator fun invoke( operator fun invoke(
client: QQAndroidClient, client: QQAndroidClient,
...@@ -101,7 +100,7 @@ internal class StatSvc { ...@@ -101,7 +100,7 @@ internal class StatSvc {
key = client.wLoginSigInfo.d2Key key = client.wLoginSigInfo.d2Key
) { sequenceId -> ) { sequenceId ->
writeSsoPacket( writeSsoPacket(
client, subAppId = subAppId, commandName = commandName, client, subAppId = client.subAppId, commandName = commandName,
extraData = client.wLoginSigInfo.tgt.toReadPacket(), sequenceId = sequenceId extraData = client.wLoginSigInfo.tgt.toReadPacket(), sequenceId = sequenceId
) { ) {
writeJceStruct( writeJceStruct(
...@@ -153,7 +152,7 @@ internal class StatSvc { ...@@ -153,7 +152,7 @@ internal class StatSvc {
var44.strVendorName = ROMUtil.getRomName(); var44.strVendorName = ROMUtil.getRomName();
var44.strVendorOSName = ROMUtil.getRomVersion(20); var44.strVendorOSName = ROMUtil.getRomVersion(20);
*/ */
bytes_0x769_reqbody = ProtoBufWithNullableSupport.dump( bytes_0x769_reqbody = ProtoBuf.dump(
Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody( Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody(
rpt_config_list = listOf( rpt_config_list = listOf(
Oidb0x769.ConfigSeq( Oidb0x769.ConfigSeq(
......
...@@ -32,8 +32,6 @@ internal class WtLogin { ...@@ -32,8 +32,6 @@ internal class WtLogin {
@Suppress("FunctionName") @Suppress("FunctionName")
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) @OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
internal object Login : OutgoingPacketFactory<Login.LoginPacketResponse>("wtlogin.login") { internal object Login : OutgoingPacketFactory<Login.LoginPacketResponse>("wtlogin.login") {
private const val subAppId = 537062845L
/** /**
* 提交验证码 * 提交验证码
*/ */
...@@ -42,7 +40,7 @@ internal class WtLogin { ...@@ -42,7 +40,7 @@ internal class WtLogin {
client: QQAndroidClient, client: QQAndroidClient,
ticket: String ticket: String
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) { writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(2) // subCommand writeShort(2) // subCommand
writeShort(4) // count of TLVs writeShort(4) // count of TLVs
...@@ -59,7 +57,7 @@ internal class WtLogin { ...@@ -59,7 +57,7 @@ internal class WtLogin {
captchaSign: ByteArray, captchaSign: ByteArray,
captchaAnswer: String captchaAnswer: String
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) { writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(2) // subCommand writeShort(2) // subCommand
writeShort(4) // count of TLVs writeShort(4) // count of TLVs
...@@ -78,7 +76,7 @@ internal class WtLogin { ...@@ -78,7 +76,7 @@ internal class WtLogin {
client: QQAndroidClient, client: QQAndroidClient,
t402: ByteArray t402: ByteArray
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) { writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(20) // subCommand writeShort(20) // subCommand
writeShort(4) // count of TLVs, probably ignored by server? writeShort(4) // count of TLVs, probably ignored by server?
...@@ -100,7 +98,7 @@ internal class WtLogin { ...@@ -100,7 +98,7 @@ internal class WtLogin {
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket( writeSsoPacket(
client, client,
subAppId, client.subAppId,
commandName, commandName,
sequenceId = sequenceId, sequenceId = sequenceId,
unknownHex = "01 00 00 00 00 00 00 00 00 00 01 00" unknownHex = "01 00 00 00 00 00 00 00 00 00 01 00"
...@@ -126,13 +124,12 @@ internal class WtLogin { ...@@ -126,13 +124,12 @@ internal class WtLogin {
*/ */
object SubCommand9 { object SubCommand9 {
private const val appId = 16L private const val appId = 16L
private const val subAppId = 537062845L
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class) @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
operator fun invoke( operator fun invoke(
client: QQAndroidClient client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) { writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(9) // subCommand writeShort(9) // subCommand
writeShort(17) // count of TLVs, probably ignored by server? writeShort(17) // count of TLVs, probably ignored by server?
...@@ -142,7 +139,7 @@ internal class WtLogin { ...@@ -142,7 +139,7 @@ internal class WtLogin {
t1(client.uin, client.device.ipAddress) t1(client.uin, client.device.ipAddress)
t106( t106(
appId, appId,
subAppId /* maybe 1*/, client.subAppId /* maybe 1*/,
client.appClientVersion, client.appClientVersion,
client.uin, client.uin,
1, 1,
...@@ -166,7 +163,7 @@ internal class WtLogin { ...@@ -166,7 +163,7 @@ internal class WtLogin {
if (ConfigManager.get_loginWithPicSt()) appIdList = longArrayOf(1600000226L) if (ConfigManager.get_loginWithPicSt()) appIdList = longArrayOf(1600000226L)
*/ */
t116(client.miscBitMap, client.subSigMap) t116(client.miscBitMap, client.subSigMap)
t100(appId, subAppId, client.appClientVersion) t100(appId, client.subAppId, client.appClientVersion)
t107(0) t107(0)
// t108(byteArrayOf()) // t108(byteArrayOf())
......
package net.mamoe.mirai.qqandroid.utils package net.mamoe.mirai.qqandroid.utils
import kotlinx.io.pool.DefaultPool
import kotlinx.io.pool.ObjectPool import kotlinx.io.pool.ObjectPool
/** /**
* 缓存 [ByteArray] 实例的 [ObjectPool] * 缓存 [ByteArray] 实例的 [ObjectPool]
*/ */
internal object ByteArrayPool : DefaultPool<ByteArray>(256) { @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
/** internal typealias ByteArrayPool = net.mamoe.mirai.utils.internal.ByteArrayPool
* 每一个 [ByteArray] 的大小 \ No newline at end of file
*/
const val BUFFER_SIZE: Int = 8192 * 8
override fun produceInstance(): ByteArray = ByteArray(BUFFER_SIZE)
override fun clearInstance(instance: ByteArray): ByteArray = instance
fun checkBufferSize(size: Int) {
require(size <= BUFFER_SIZE) { "sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE" }
}
fun checkBufferSize(size: Long) {
require(size <= BUFFER_SIZE) { "sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE" }
}
/**
* 请求一个大小至少为 [requestedSize] 的 [ByteArray] 实例.
*/ // 不要写为扩展函数. 它需要优先于 kotlinx.io 的扩展函数 resolve
inline fun <R> useInstance(requestedSize: Int = 0, block: (ByteArray) -> R): R {
if (requestedSize > BUFFER_SIZE) {
return ByteArray(requestedSize).run(block)
}
val instance = borrow()
try {
return block(instance)
} finally {
recycle(instance)
}
}
}
\ No newline at end of file
...@@ -27,6 +27,7 @@ import kotlin.jvm.JvmMultifileClass ...@@ -27,6 +27,7 @@ import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
internal inline fun <R> ByteReadPacket.useBytes( internal inline fun <R> ByteReadPacket.useBytes(
n: Int = remaining.toInt(),//not that safe but adequate n: Int = remaining.toInt(),//not that safe but adequate
block: (data: ByteArray, length: Int) -> R block: (data: ByteArray, length: Int) -> R
......
package net.mamoe.mirai.qqandroid.utils.io.serialization
import kotlinx.io.core.Input
import kotlinx.io.core.Output
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialFormat
import kotlinx.serialization.SerializationStrategy
internal interface IOFormat : SerialFormat {
fun <T> dumpTo(serializer: SerializationStrategy<T>, ojb: T, output: Output)
fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T
}
...@@ -23,10 +23,13 @@ import kotlinx.serialization.modules.SerialModule ...@@ -23,10 +23,13 @@ import kotlinx.serialization.modules.SerialModule
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import kotlinx.serialization.protobuf.ProtoNumberType import kotlinx.serialization.protobuf.ProtoNumberType
import kotlinx.serialization.protobuf.ProtoType import kotlinx.serialization.protobuf.ProtoType
import moe.him188.jcekt.JceId
import net.mamoe.mirai.qqandroid.utils.io.serialization.ProtoBufWithNullableSupport.Varint.encodeVarint import net.mamoe.mirai.qqandroid.utils.io.serialization.ProtoBufWithNullableSupport.Varint.encodeVarint
internal typealias ProtoDesc = Pair<Int, ProtoNumberType> internal typealias ProtoDesc = Pair<Int, ProtoNumberType>
internal fun getSerialId(desc: SerialDescriptor, index: Int): Int? = desc.findAnnotation<JceId>(index)?.id
internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefault: Boolean = false): ProtoDesc { internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefault: Boolean = false): ProtoDesc {
val idx = getSerialId(desc, index) ?: (if (zeroBasedDefault) index else index + 1) val idx = getSerialId(desc, index) ?: (if (zeroBasedDefault) index else index + 1)
val format = desc.findAnnotation<ProtoType>(index)?.type val format = desc.findAnnotation<ProtoType>(index)?.type
......
# io.serialization
**序列化支持**
包含:
- QQ 的 JceStruct 相关的全自动序列化和反序列化: [Jce.kt](jce/JceNew.kt)
- Protocol Buffers 的 optional 支持: [ProtoBufWithNullableSupport.kt](ProtoBufWithNullableSupport.kt)
其中, `ProtoBufWithNullableSupport` 的绝大部分源码来自 `kotlinx.serialization`. 原著权归该项目作者所有.
Mirai 所做的修改已经标记上了 `MIRAI MODIFY START`
\ No newline at end of file
/*
* 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.utils.io.serialization.jce
import kotlinx.io.core.*
import net.mamoe.mirai.qqandroid.utils.io.readString
import net.mamoe.mirai.qqandroid.utils.io.serialization.JceCharset
import net.mamoe.mirai.qqandroid.utils.toUHexString
/**
* Jce Input. 需要手动管理 head.
*/
internal class JceInput(
val input: Input, val charset: JceCharset
) {
private var _head: JceHead? = null
val currentHead: JceHead get() = _head ?: throw EOFException("No current JceHead available")
val currentHeadOrNull: JceHead? get() = _head
init {
prepareNextHead()
}
/**
* 读取下一个 [JceHead] 并保存. 可通过 [currentHead] 获取这个 [JceHead].
*
* @return 是否成功读取. 返回 `false` 时代表 [Input.endOfInput]
*/
fun prepareNextHead(): Boolean {
return readNextHeadButDoNotAssignTo_Head().also { _head = it; } != null
}
fun nextHead(): JceHead {
if (!prepareNextHead()) {
throw EOFException("No more JceHead available")
}
return currentHead
}
/**
* 直接读取下一个 [JceHead] 并返回.
* 返回 `null` 则代表 [Input.endOfInput]
*/
@Suppress("FunctionName")
@OptIn(ExperimentalUnsignedTypes::class)
private fun readNextHeadButDoNotAssignTo_Head(): JceHead? {
if (input.endOfInput) {
return null
}
val var2 = input.readUByte()
val type = var2 and 15u
var tag = var2.toUInt() shr 4
if (tag == 15u) {
tag = input.readUByte().toUInt()
}
return JceHead(
tag = tag.toInt(),
type = type.toByte()
)
}
/**
* 使用这个 [JceHead].
* [block] 结束后将会 [准备下一个 [JceHead]][prepareNextHead]
*/
inline fun <R> useHead(crossinline block: (JceHead) -> R): R {
return currentHead.let(block).also { prepareNextHead() }
}
/**
* 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则返回 `null`
*/
inline fun <R> skipToHeadAndUseIfPossibleOrNull(tag: Int, crossinline block: (JceHead) -> R): R? {
return skipToHeadOrNull(tag)?.let(block).also { prepareNextHead() }
}
/**
* 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则抛出异常
*/
inline fun <R : Any> skipToHeadAndUseIfPossibleOrFail(
tag: Int,
crossinline message: () -> String = { "tag not found: $tag" },
crossinline block: (JceHead) -> R
): R {
return checkNotNull<R>(skipToHeadAndUseIfPossibleOrNull(tag, block), message)
}
tailrec fun skipToHeadOrNull(tag: Int): JceHead? {
val current: JceHead = currentHeadOrNull ?: return null // no backing field
return when {
current.tag > tag -> null // tag 大了,即找不到
current.tag == tag -> current // 满足需要.
else -> { // tag 小了
skipField(current.type)
check(prepareNextHead()) { "cannot skip to tag $tag, early EOF" }
skipToHeadOrNull(tag)
}
}
}
inline fun skipToHeadOrFail(
tag: Int,
message: () -> String = { "head not found: $tag" }
): JceHead {
return checkNotNull(skipToHeadOrNull(tag), message)
}
@OptIn(ExperimentalUnsignedTypes::class)
@PublishedApi
internal fun skipField(type: Byte): Unit {
JceDecoder.println {
"skipping ${JceHead.findJceTypeName(
type
)}"
}
when (type) {
Jce.BYTE -> this.input.discardExact(1)
Jce.SHORT -> this.input.discardExact(2)
Jce.INT -> this.input.discardExact(4)
Jce.LONG -> this.input.discardExact(8)
Jce.FLOAT -> this.input.discardExact(4)
Jce.DOUBLE -> this.input.discardExact(8)
Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
Jce.STRING4 -> this.input.discardExact(this.input.readInt())
Jce.MAP -> { // map
JceDecoder.structureHierarchy++
var count: Int = 0
nextHead() // avoid shadowing, don't remove
repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping map" }) {
readJceIntValue(it).also { count = it * 2 }
} * 2) {
skipField(currentHead.type)
if (it != count - 1) { // don't read last head
nextHead()
}
}
JceDecoder.structureHierarchy--
}
Jce.LIST -> { // list
JceDecoder.structureHierarchy++
var count: Int = 0
nextHead() // avoid shadowing, don't remove
repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping list" }) { head ->
readJceIntValue(head).also { count = it }
}) {
skipField(currentHead.type)
if (it != count - 1) { // don't read last head
nextHead()
}
}
JceDecoder.structureHierarchy--
}
Jce.STRUCT_BEGIN -> {
JceDecoder.structureHierarchy++
var head: JceHead
do {
head = nextHead()
skipField(head.type)
} while (head.type != Jce.STRUCT_END)
JceDecoder.structureHierarchy--
}
Jce.STRUCT_END, Jce.ZERO_TYPE -> {
}
Jce.SIMPLE_LIST -> {
JceDecoder.structureHierarchy++
var head = nextHead()
check(head.type == Jce.BYTE) { "bad simple list element type: " + head.type }
check(head.tag == 0) { "simple list element tag must be 0, but was ${head.tag}" }
head = nextHead()
check(head.tag == 0) { "tag for size for simple list must be 0, but was ${head.tag}" }
this.input.discardExact(readJceIntValue(head))
JceDecoder.structureHierarchy--
}
else -> error("invalid type: $type")
}
}
// region readers
fun readJceIntValue(head: JceHead): Int {
//println("readJceIntValue: $head")
return when (head.type) {
Jce.ZERO_TYPE -> 0
Jce.BYTE -> input.readByte().toInt()
Jce.SHORT -> input.readShort().toInt()
Jce.INT -> input.readInt()
else -> error("type mismatch: $head, remaining=${input.readBytes().toUHexString()}")
}
}
fun readJceShortValue(head: JceHead): Short {
return when (head.type) {
Jce.ZERO_TYPE -> 0
Jce.BYTE -> input.readByte().toShort()
Jce.SHORT -> input.readShort()
else -> error("type mismatch: $head")
}
}
fun readJceLongValue(head: JceHead): Long {
return when (head.type) {
Jce.ZERO_TYPE -> 0
Jce.BYTE -> input.readByte().toLong()
Jce.SHORT -> input.readShort().toLong()
Jce.INT -> input.readInt().toLong()
Jce.LONG -> input.readLong()
else -> error("type mismatch ${head.type}")
}
}
fun readJceByteValue(head: JceHead): Byte {
//println("readJceByteValue: $head")
return when (head.type) {
Jce.ZERO_TYPE -> 0
Jce.BYTE -> input.readByte()
else -> error("type mismatch: $head")
}
}
fun readJceFloatValue(head: JceHead): Float {
return when (head.type) {
Jce.ZERO_TYPE -> 0f
Jce.FLOAT -> input.readFloat()
else -> error("type mismatch: $head")
}
}
@OptIn(ExperimentalUnsignedTypes::class)
fun readJceStringValue(head: JceHead): String {
//println("readJceStringValue: $head")
return when (head.type) {
Jce.STRING1 -> input.readString(input.readUByte().toInt(), charset = charset.kotlinCharset)
Jce.STRING4 -> input.readString(
input.readUInt().toInt().also { require(it in 1 until 104857600) { "bad string length: $it" } },
charset = charset.kotlinCharset
)
else -> error("type mismatch: $head, expecting 6 or 7 (for string)")
}
}
fun readJceDoubleValue(head: JceHead): Double {
return when (head.type.toInt()) {
12 -> 0.0
4 -> input.readFloat().toDouble()
5 -> input.readDouble()
else -> error("type mismatch: $head")
}
}
fun readJceBooleanValue(head: JceHead): Boolean {
return readJceByteValue(head) == 1.toByte()
}
}
/*
* 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.utils.io.serialization.jce
import kotlinx.io.core.*
import kotlinx.serialization.BinaryFormat
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialFormat
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.modules.EmptyModule
import kotlinx.serialization.modules.SerialModule
import net.mamoe.mirai.qqandroid.utils.io.serialization.IOFormat
import net.mamoe.mirai.qqandroid.utils.io.serialization.JceCharset
import net.mamoe.mirai.qqandroid.utils.io.serialization.JceOld
import net.mamoe.mirai.qqandroid.utils.toReadPacket
/**
* Jce 数据结构序列化和反序列化器.
*
* @author Him188
*/
internal class Jce(
override val context: SerialModule,
val charset: JceCharset
) : SerialFormat, IOFormat, BinaryFormat {
override fun <T> dumpTo(serializer: SerializationStrategy<T>, ojb: T, output: Output) {
output.writePacket(JceOld.byCharSet(this.charset).dumpAsPacket(serializer, ojb))
}
override fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T {
return JceDecoder(
JceInput(
input,
charset
), context
).decodeSerializableValue(deserializer)
}
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
return buildPacket { dumpTo(serializer, value, this) }.readBytes()
}
override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
return load(deserializer, bytes.toReadPacket())
}
companion object {
val UTF_8 = Jce(
EmptyModule,
JceCharset.UTF8
)
val GBK = Jce(
EmptyModule,
JceCharset.GBK
)
fun byCharSet(c: JceCharset): Jce {
return if (c == JceCharset.UTF8) UTF_8 else GBK
}
internal const val BYTE: Byte = 0
internal const val DOUBLE: Byte = 5
internal const val FLOAT: Byte = 4
internal const val INT: Byte = 2
internal const val JCE_MAX_STRING_LENGTH = 104857600
internal const val LIST: Byte = 9
internal const val LONG: Byte = 3
internal const val MAP: Byte = 8
internal const val SHORT: Byte = 1
internal const val SIMPLE_LIST: Byte = 13
internal const val STRING1: Byte = 6
internal const val STRING4: Byte = 7
internal const val STRUCT_BEGIN: Byte = 10
internal const val STRUCT_END: Byte = 11
internal const val ZERO_TYPE: Byte = 12
}
}
\ No newline at end of file
/*
* 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.utils.io.serialization.jce
import kotlinx.io.core.Output
import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.SerialInfo
/**
* 标注 JCE 序列化时使用的 ID
*/
@SerialInfo
@Target(AnnotationTarget.PROPERTY)
internal annotation class JceId(val id: Int)
/**
* 类中元素的 tag
*
* 保留这个结构, 为将来增加功能的兼容性.
*/
@PublishedApi
internal abstract class JceTag {
abstract val id: Int
internal var isSimpleByteArray: Boolean = false
}
internal object JceTagListElement : JceTag() {
override val id: Int get() = 0
override fun toString(): String {
return "JceTagListElement"
}
}
internal object JceTagMapEntryKey : JceTag() {
override val id: Int get() = 0
override fun toString(): String {
return "JceTagMapEntryKey"
}
}
internal object JceTagMapEntryValue : JceTag() {
override val id: Int get() = 1
override fun toString(): String {
return "JceTagMapEntryValue"
}
}
internal data class JceTagCommon(
override val id: Int
) : JceTag()
internal fun JceHead.checkType(type: Byte, message: String, tag: JceTag, descriptor: SerialDescriptor) {
check(this.type == type) {
"type mismatch. " +
"Expected ${JceHead.findJceTypeName(type)}, " +
"actual ${JceHead.findJceTypeName(this.type)} for $message. " +
"Tag info: " +
"id=${tag.id}, " +
"name=${descriptor.getElementName(tag.id)} " +
"in ${descriptor.serialName}" }
}
@PublishedApi
internal fun Output.writeJceHead(type: Byte, tag: Int) {
if (tag < 15) {
writeByte(((tag shl 4) or type.toInt()).toByte())
return
}
if (tag < 256) {
writeByte((type.toInt() or 0xF0).toByte())
writeByte(tag.toByte())
return
}
error("tag is too large: $tag")
}
@OptIn(ExperimentalUnsignedTypes::class)
inline class JceHead(private val value: Long) {
constructor(tag: Int, type: Byte) : this(tag.toLong().shl(32) or type.toLong())
val tag: Int get() = (value ushr 32).toInt()
val type: Byte get() = value.toUInt().toByte()
override fun toString(): String {
return "JceHead(tag=$tag, type=$type(${findJceTypeName(type)}))"
}
companion object {
fun findJceTypeName(type: Byte): String {
return when (type) {
Jce.BYTE -> "Byte"
Jce.DOUBLE -> "Double"
Jce.FLOAT -> "Float"
Jce.INT -> "Int"
Jce.LIST -> "List"
Jce.LONG -> "Long"
Jce.MAP -> "Map"
Jce.SHORT -> "Short"
Jce.SIMPLE_LIST -> "SimpleList"
Jce.STRING1 -> "String1"
Jce.STRING4 -> "String4"
Jce.STRUCT_BEGIN -> "StructBegin"
Jce.STRUCT_END -> "StructEnd"
Jce.ZERO_TYPE -> "Zero"
else -> error("illegal jce type: $type")
}
}
}
}
\ No newline at end of file
...@@ -16,15 +16,14 @@ import kotlinx.io.core.* ...@@ -16,15 +16,14 @@ import kotlinx.io.core.*
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialDescriptor import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.SerializationStrategy
import moe.him188.jcekt.Jce
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion2 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion2
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.JceStruct
import net.mamoe.mirai.qqandroid.utils.io.ProtoBuf import net.mamoe.mirai.qqandroid.utils.io.ProtoBuf
import net.mamoe.mirai.qqandroid.utils.io.readPacketExact import net.mamoe.mirai.qqandroid.utils.io.readPacketExact
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce
import net.mamoe.mirai.qqandroid.utils.read import net.mamoe.mirai.qqandroid.utils.read
import net.mamoe.mirai.utils.MiraiInternalAPI
import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
...@@ -34,25 +33,21 @@ internal fun <T : JceStruct> ByteArray.loadWithUniPacket( ...@@ -34,25 +33,21 @@ internal fun <T : JceStruct> ByteArray.loadWithUniPacket(
): T = this.read { readUniPacket(deserializer, name) } ): T = this.read { readUniPacket(deserializer, name) }
internal fun <T : JceStruct> ByteArray.loadAs( internal fun <T : JceStruct> ByteArray.loadAs(
deserializer: DeserializationStrategy<T>, deserializer: DeserializationStrategy<T>
c: JceCharset = JceCharset.UTF8 ): T = this.read { Jce.UTF_8.load(deserializer, this) }
): T = this.read { Jce.byCharSet(c).load(deserializer, this) }
internal fun <T : JceStruct> BytePacketBuilder.writeJceStruct( internal fun <T : JceStruct> BytePacketBuilder.writeJceStruct(
serializer: SerializationStrategy<T>, serializer: SerializationStrategy<T>,
struct: T, struct: T
charset: JceCharset = JceCharset.UTF8
) { ) {
Jce.byCharSet(charset).dumpTo(serializer, struct, this) Jce.UTF_8.dumpTo(serializer, struct, this)
} }
internal fun <T : JceStruct> ByteReadPacket.readJceStruct( internal fun <T : JceStruct> ByteReadPacket.readJceStruct(
serializer: DeserializationStrategy<T>, serializer: DeserializationStrategy<T>,
charset: JceCharset = JceCharset.UTF8,
length: Int = this.remaining.toInt() length: Int = this.remaining.toInt()
): T { ): T {
@OptIn(MiraiInternalAPI::class) return Jce.UTF_8.load(serializer, this.readPacketExact(length))
return Jce.byCharSet(charset).load(serializer, this.readPacketExact(length))
} }
/** /**
...@@ -103,9 +98,8 @@ private fun <R> ByteReadPacket.decodeUniRequestPacketAndDeserialize(name: String ...@@ -103,9 +98,8 @@ private fun <R> ByteReadPacket.decodeUniRequestPacketAndDeserialize(name: String
} }
internal fun <T : JceStruct> T.toByteArray( internal fun <T : JceStruct> T.toByteArray(
serializer: SerializationStrategy<T>, serializer: SerializationStrategy<T>
c: JceCharset = JceCharset.UTF8 ): ByteArray = Jce.UTF_8.dump(serializer, this)
): ByteArray = Jce.byCharSet(c).dump(serializer, this)
internal fun <T : ProtoBuf> BytePacketBuilder.writeProtoBuf(serializer: SerializationStrategy<T>, v: T) { internal fun <T : ProtoBuf> BytePacketBuilder.writeProtoBuf(serializer: SerializationStrategy<T>, v: T) {
this.writeFully(v.toByteArray(serializer)) this.writeFully(v.toByteArray(serializer))
...@@ -140,19 +134,12 @@ internal fun <T : JceStruct> jceRequestSBuffer( ...@@ -140,19 +134,12 @@ internal fun <T : JceStruct> jceRequestSBuffer(
name: String, name: String,
serializer: SerializationStrategy<T>, serializer: SerializationStrategy<T>,
jceStruct: T jceStruct: T
): ByteArray = jceRequestSBuffer(name, serializer, jceStruct, JceCharset.UTF8)
internal fun <T : JceStruct> jceRequestSBuffer(
name: String,
serializer: SerializationStrategy<T>,
jceStruct: T,
charset: JceCharset
): ByteArray { ): ByteArray {
return RequestDataVersion3( return RequestDataVersion3(
mapOf( mapOf(
name to JCE_STRUCT_HEAD_OF_TAG_0 + jceStruct.toByteArray(serializer) + JCE_STRUCT_TAIL_OF_TAG_0 name to JCE_STRUCT_HEAD_OF_TAG_0 + jceStruct.toByteArray(serializer) + JCE_STRUCT_TAIL_OF_TAG_0
) )
).toByteArray(RequestDataVersion3.serializer(), charset) ).toByteArray(RequestDataVersion3.serializer())
} }
private val JCE_STRUCT_HEAD_OF_TAG_0 = byteArrayOf(0x0A) private val JCE_STRUCT_HEAD_OF_TAG_0 = byteArrayOf(0x0A)
......
...@@ -42,7 +42,6 @@ actual abstract class Group : Contact(), CoroutineScope { ...@@ -42,7 +42,6 @@ actual abstract class Group : Contact(), CoroutineScope {
/** /**
* 群设置 * 群设置
*/ */
@SinceMirai("0.30.0")
actual abstract val settings: GroupSettings actual abstract val settings: GroupSettings
/** /**
......
...@@ -15,10 +15,8 @@ package net.mamoe.mirai ...@@ -15,10 +15,8 @@ package net.mamoe.mirai
import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent
import net.mamoe.mirai.event.events.MemberJoinRequestEvent import net.mamoe.mirai.event.events.MemberJoinRequestEvent
import net.mamoe.mirai.event.events.NewFriendRequestEvent import net.mamoe.mirai.event.events.NewFriendRequestEvent
...@@ -29,7 +27,6 @@ import net.mamoe.mirai.network.LoginFailedException ...@@ -29,7 +27,6 @@ import net.mamoe.mirai.network.LoginFailedException
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmName
import kotlin.jvm.JvmStatic import kotlin.jvm.JvmStatic
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
...@@ -48,6 +45,8 @@ suspend inline fun <B : Bot> B.alsoLogin(): B = also { login() } ...@@ -48,6 +45,8 @@ suspend inline fun <B : Bot> B.alsoLogin(): B = also { login() }
* *
* @see Contact 联系人 * @see Contact 联系人
* @see kotlinx.coroutines.isActive 判断 [Bot] 是否正常运行中. (在线, 且没有被 [close]) * @see kotlinx.coroutines.isActive 判断 [Bot] 是否正常运行中. (在线, 且没有被 [close])
*
* @see BotFactory 构造 [Bot] 的工厂, [Bot] 唯一的构造方式.
*/ */
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME")
@OptIn(MiraiInternalAPI::class, LowLevelAPI::class) @OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
...@@ -56,6 +55,7 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -56,6 +55,7 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
/** /**
* 复制一份此时的 [Bot] 实例列表. * 复制一份此时的 [Bot] 实例列表.
*/ */
@PlannedRemoval("1.2.0")
@Deprecated("use botInstances instead", replaceWith = ReplaceWith("botInstances")) @Deprecated("use botInstances instead", replaceWith = ReplaceWith("botInstances"))
@JvmStatic @JvmStatic
val instances: List<WeakRef<Bot>> val instances: List<WeakRef<Bot>>
...@@ -64,7 +64,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -64,7 +64,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
/** /**
* 复制一份此时的 [Bot] 实例列表. * 复制一份此时的 [Bot] 实例列表.
*/ */
@SinceMirai("0.39.1")
@JvmStatic @JvmStatic
val botInstances: List<Bot> val botInstances: List<Bot>
get() = BotImpl.instances.asSequence().mapNotNull { it.get() }.toList() get() = BotImpl.instances.asSequence().mapNotNull { it.get() }.toList()
...@@ -72,7 +71,7 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -72,7 +71,7 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
/** /**
* 遍历每一个 [Bot] 实例 * 遍历每一个 [Bot] 实例
*/ */
inline fun forEachInstance(block: (Bot) -> Unit) = BotImpl.forEachInstance(block) fun forEachInstance(block: (Bot) -> Unit) = BotImpl.forEachInstance(block)
/** /**
* 获取一个 [Bot] 实例, 找不到则 [NoSuchElementException] * 获取一个 [Bot] 实例, 找不到则 [NoSuchElementException]
...@@ -89,20 +88,14 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -89,20 +88,14 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
*/ */
abstract val context: Context abstract val context: Context
@PlannedRemoval("1.0.0")
@Deprecated("use id instead", replaceWith = ReplaceWith("id"))
abstract val uin: Long
/** /**
* QQ 号码. 实际类型为 uint * QQ 号码. 实际类型为 uint
*/ */
@SinceMirai("0.32.0")
abstract override val id: Long abstract override val id: Long
/** /**
* 昵称 * 昵称
*/ */
@SinceMirai("0.33.1")
abstract val nick: String abstract val nick: String
/** /**
...@@ -113,7 +106,7 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -113,7 +106,7 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
// region contacts // region contacts
/** /**
* [QQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewFriend] 实例 * [User.id] 与 [Bot.id] 相同的 [_lowLevelNewFriend] 实例
*/ */
abstract val selfQQ: Friend abstract val selfQQ: Friend
...@@ -179,6 +172,8 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -179,6 +172,8 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
/** /**
* 获取图片下载链接 * 获取图片下载链接
*
* @see Image.queryUrl [Image] 的扩展函数
*/ */
@JvmSynthetic @JvmSynthetic
abstract suspend fun queryImageUrl(image: Image): String abstract suspend fun queryImageUrl(image: Image): String
...@@ -193,7 +188,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -193,7 +188,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
* @param targetUin 为用户时为 [Friend.id], 为群时需使用 [Group.calculateGroupUinByGroupCode] 计算 * @param targetUin 为用户时为 [Friend.id], 为群时需使用 [Group.calculateGroupUinByGroupCode] 计算
*/ */
@MiraiExperimentalAPI @MiraiExperimentalAPI
@SinceMirai("0.39.0")
abstract fun constructMessageSource( abstract fun constructMessageSource(
kind: OfflineMessageSource.Kind, kind: OfflineMessageSource.Kind,
fromUin: Long, targetUin: Long, fromUin: Long, targetUin: Long,
...@@ -201,35 +195,12 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -201,35 +195,12 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
originalMessage: MessageChain originalMessage: MessageChain
): OfflineMessageSource ): OfflineMessageSource
/**
* 获取图片下载链接并开始下载.
*
* @see ByteReadChannel.copyAndClose
* @see ByteReadChannel.copyTo
*/
@PlannedRemoval("1.0.0")
@Deprecated("use your own Http clients, this is going to be removed in 1.0.0", level = DeprecationLevel.WARNING)
@MiraiExperimentalAPI
@JvmSynthetic
abstract suspend fun openChannel(image: Image): ByteReadChannel
/**
* 添加一个好友
*
* @param message 若需要验证请求时的验证消息.
* @param remark 好友备注
*/
@JvmSynthetic
@MiraiExperimentalAPI("未支持")
abstract suspend fun addFriend(id: Long, message: String? = null, remark: String? = null): AddFriendResult
/** /**
* 通过好友验证 * 通过好友验证
* *
* @param event 好友验证的事件对象 * @param event 好友验证的事件对象
*/ */
@SinceMirai("0.35.0")
@JvmSynthetic @JvmSynthetic
abstract suspend fun acceptNewFriendRequest(event: NewFriendRequestEvent) abstract suspend fun acceptNewFriendRequest(event: NewFriendRequestEvent)
...@@ -239,7 +210,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -239,7 +210,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
* @param event 好友验证的事件对象 * @param event 好友验证的事件对象
* @param blackList 拒绝后是否拉入黑名单 * @param blackList 拒绝后是否拉入黑名单
*/ */
@SinceMirai("0.35.0")
@JvmSynthetic @JvmSynthetic
abstract suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean = false) abstract suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean = false)
...@@ -248,7 +218,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -248,7 +218,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
* *
* @param event 加群验证的事件对象 * @param event 加群验证的事件对象
*/ */
@SinceMirai("0.35.0")
@JvmSynthetic @JvmSynthetic
abstract suspend fun acceptMemberJoinRequest(event: MemberJoinRequestEvent) abstract suspend fun acceptMemberJoinRequest(event: MemberJoinRequestEvent)
...@@ -258,7 +227,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -258,7 +227,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
* @param event 加群验证的事件对象 * @param event 加群验证的事件对象
* @param blackList 拒绝后是否拉入黑名单 * @param blackList 拒绝后是否拉入黑名单
*/ */
@SinceMirai("0.35.0")
@JvmSynthetic @JvmSynthetic
abstract suspend fun rejectMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean = false) abstract suspend fun rejectMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean = false)
...@@ -268,7 +236,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -268,7 +236,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
* @param event 加群验证的事件对象 * @param event 加群验证的事件对象
* @param blackList 忽略后是否拉入黑名单 * @param blackList 忽略后是否拉入黑名单
*/ */
@SinceMirai("0.35.0")
@JvmSynthetic @JvmSynthetic
abstract suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean = false) abstract suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean = false)
...@@ -277,7 +244,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -277,7 +244,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
* *
* @param event 邀请入群的事件对象 * @param event 邀请入群的事件对象
*/ */
@SinceMirai("0.39.4")
@JvmSynthetic @JvmSynthetic
abstract suspend fun acceptInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent) abstract suspend fun acceptInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent)
...@@ -287,7 +253,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -287,7 +253,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
* @param event 邀请入群的事件对象 * @param event 邀请入群的事件对象
*/ */
@JvmSynthetic @JvmSynthetic
@SinceMirai("0.39.4")
abstract suspend fun ignoreInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent) abstract suspend fun ignoreInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent)
// endregion // endregion
...@@ -313,19 +278,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -313,19 +278,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
*/ */
@MiraiInternalAPI @MiraiInternalAPI
abstract val network: BotNetworkHandler abstract val network: BotNetworkHandler
@PlannedRemoval("1.0.0.")
@get:JvmName("getSelfQQ")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
val selfQQDeprecated: QQ
get() = selfQQ
@PlannedRemoval("1.0.0.")
@JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun getFriendDeprecated(id: Long): QQ = this.getFriend(id)
} }
/** /**
......
...@@ -14,15 +14,16 @@ package net.mamoe.mirai ...@@ -14,15 +14,16 @@ package net.mamoe.mirai
import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.Context import net.mamoe.mirai.utils.Context
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
/** /**
* 构造 [Bot] 的工厂. * 构造 [Bot] 的工厂. 这是 [Bot] 唯一的构造方式.
* *
* 在协议模块中有各自的实现. * `mirai-core-qqandroid`: `QQAndroid`
* - `mirai-core-timpc`: `TIMPC` *
* - `mirai-core-qqandroid`: `QQAndroid` * 在 JVM, 请查看 `BotFactoryJvm`
*/ */
interface BotFactory { expect interface BotFactory {
/** /**
* 使用指定的 [配置][configuration] 构造 [Bot] 实例 * 使用指定的 [配置][configuration] 构造 [Bot] 实例
*/ */
...@@ -49,6 +50,7 @@ interface BotFactory { ...@@ -49,6 +50,7 @@ interface BotFactory {
/** /**
* 使用指定的 [配置][configuration] 构造 [Bot] 实例 * 使用指定的 [配置][configuration] 构造 [Bot] 实例
*/ */
@JvmSynthetic
inline fun BotFactory.Bot( inline fun BotFactory.Bot(
context: Context, context: Context,
qq: Long, qq: Long,
...@@ -59,6 +61,7 @@ inline fun BotFactory.Bot( ...@@ -59,6 +61,7 @@ inline fun BotFactory.Bot(
/** /**
* 使用指定的 [配置][configuration] 构造 [Bot] 实例 * 使用指定的 [配置][configuration] 构造 [Bot] 实例
*/ */
@JvmSynthetic
inline fun BotFactory.Bot( inline fun BotFactory.Bot(
context: Context, context: Context,
qq: Long, qq: Long,
......
...@@ -24,6 +24,8 @@ import net.mamoe.mirai.network.closeAndJoin ...@@ -24,6 +24,8 @@ import net.mamoe.mirai.network.closeAndJoin
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.internal.retryCatching import net.mamoe.mirai.utils.internal.retryCatching
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
/* /*
* 泛型 N 不需要向外(接口)暴露. * 泛型 N 不需要向外(接口)暴露.
...@@ -46,10 +48,6 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -46,10 +48,6 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
override val context: Context by context.unsafeWeakRef() override val context: Context by context.unsafeWeakRef()
@Deprecated("use id instead", replaceWith = ReplaceWith("id"))
override val uin: Long
get() = this.id
final override val logger: MiraiLogger by lazy { configuration.botLoggerSupplier(this) } final override val logger: MiraiLogger by lazy { configuration.botLoggerSupplier(this) }
init { init {
...@@ -60,7 +58,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -60,7 +58,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
@PublishedApi @PublishedApi
internal val instances: LockFreeLinkedList<WeakRef<Bot>> = LockFreeLinkedList() internal val instances: LockFreeLinkedList<WeakRef<Bot>> = LockFreeLinkedList()
inline fun forEachInstance(block: (Bot) -> Unit) = instances.forEach { fun forEachInstance(block: (Bot) -> Unit) = instances.forEach {
it.get()?.let(block) it.get()?.let(block)
} }
...@@ -90,6 +88,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -90,6 +88,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
@Throws(LoginFailedException::class) // only @Throws(LoginFailedException::class) // only
protected abstract suspend fun relogin(cause: Throwable?) protected abstract suspend fun relogin(cause: Throwable?)
@OptIn(ExperimentalTime::class)
@Suppress("unused") @Suppress("unused")
private val offlineListener: Listener<BotOfflineEvent> = private val offlineListener: Listener<BotOfflineEvent> =
this@BotImpl.subscribeAlways(concurrency = Listener.ConcurrencyKind.LOCKED) { event -> this@BotImpl.subscribeAlways(concurrency = Listener.ConcurrencyKind.LOCKED) { event ->
...@@ -107,37 +106,39 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -107,37 +106,39 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
} }
bot.logger.info { "Connection dropped by server or lost, retrying login" } bot.logger.info { "Connection dropped by server or lost, retrying login" }
tailrec suspend fun reconnect() { val time = measureTime {
retryCatching<Unit>(configuration.reconnectionRetryTimes, tailrec suspend fun reconnect() {
except = LoginFailedException::class) { tryCount, _ -> retryCatching<Unit>(configuration.reconnectionRetryTimes,
if (tryCount != 0) { except = LoginFailedException::class) { tryCount, _ ->
delay(configuration.reconnectPeriodMillis) if (tryCount != 0) {
} delay(configuration.reconnectPeriodMillis)
network.withConnectionLock { }
/** network.withConnectionLock {
* [BotImpl.relogin] only, no [BotNetworkHandler.init] /**
*/ * [BotImpl.relogin] only, no [BotNetworkHandler.init]
@OptIn(ThisApiMustBeUsedInWithConnectionLockBlock::class) */
relogin((event as? BotOfflineEvent.Dropped)?.cause) @OptIn(ThisApiMustBeUsedInWithConnectionLockBlock::class)
} relogin((event as? BotOfflineEvent.Dropped)?.cause)
logger.info { "Reconnected successfully" } }
BotReloginEvent(bot, (event as? BotOfflineEvent.Dropped)?.cause).broadcast() BotReloginEvent(bot, (event as? BotOfflineEvent.Dropped)?.cause).broadcast()
return return
}.getOrElse { }.getOrElse {
if (it is LoginFailedException && !it.killBot) { if (it is LoginFailedException && !it.killBot) {
logger.info { "Cannot reconnect" }
logger.warning(it)
logger.info { "Retrying in 3s..." }
delay(3000)
return@getOrElse
}
logger.info { "Cannot reconnect" } logger.info { "Cannot reconnect" }
logger.warning(it) throw it
logger.info { "Retrying in 3s..." }
delay(3000)
return@getOrElse
} }
logger.info { "Cannot reconnect" } reconnect()
throw it
} }
reconnect() reconnect()
} }
reconnect() logger.info { "Reconnected successfully in ${time.inMilliseconds} ms" }
} }
is BotOfflineEvent.Active -> { is BotOfflineEvent.Active -> {
val msg = if (event.cause == null) { val msg = if (event.cause == null) {
......
...@@ -32,11 +32,11 @@ import kotlin.jvm.JvmSynthetic ...@@ -32,11 +32,11 @@ import kotlin.jvm.JvmSynthetic
/** /**
* 联系人. 虽然叫做联系人, 但他的子类有 [用户][User], 和 [群][Group]. * 联系对象, 即可以与 [Bot] 互动的对象. 包含 [用户][User], 和 [群][Group].
*/ */
abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot { abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot {
/** /**
* 这个联系所属 [Bot]. * 这个联系对象所属 [Bot].
*/ */
@WeakRefProperty @WeakRefProperty
abstract val bot: Bot abstract val bot: Bot
...@@ -44,10 +44,7 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot ...@@ -44,10 +44,7 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot
/** /**
* 可以是 QQ 号码或者群号码. * 可以是 QQ 号码或者群号码.
* *
* 对于 [QQ], `uin` 与 `id` 是相同的意思. * @see User.id
* 对于 [Group], `groupCode` 与 `id` 是相同的意思.
*
* @see QQ.id
* @see Group.id * @see Group.id
*/ */
abstract override val id: Long abstract override val id: Long
......
...@@ -11,8 +11,9 @@ ...@@ -11,8 +11,9 @@
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.LockFreeLinkedList
import kotlin.jvm.JvmName import net.mamoe.mirai.utils.asSequence
import kotlin.jvm.JvmField
/** /**
...@@ -20,9 +21,8 @@ import kotlin.jvm.JvmName ...@@ -20,9 +21,8 @@ import kotlin.jvm.JvmName
* *
* @see ContactList.asSequence * @see ContactList.asSequence
*/ */
@OptIn(MiraiInternalAPI::class)
@Suppress("unused") @Suppress("unused")
class ContactList<C : Contact>(@MiraiInternalAPI("Implementation may change in future release") val delegate: LockFreeLinkedList<C>) : class ContactList<C : Contact> internal constructor(@JvmField internal val delegate: LockFreeLinkedList<C>) :
Iterable<C> { Iterable<C> {
operator fun get(id: Long): C = operator fun get(id: Long): C =
delegate.asSequence().firstOrNull { it.id == id } ?: throw NoSuchElementException("Contact id $id") delegate.asSequence().firstOrNull { it.id == id } ?: throw NoSuchElementException("Contact id $id")
...@@ -41,31 +41,6 @@ class ContactList<C : Contact>(@MiraiInternalAPI("Implementation may change in f ...@@ -41,31 +41,6 @@ class ContactList<C : Contact>(@MiraiInternalAPI("Implementation may change in f
override fun iterator(): Iterator<C> { override fun iterator(): Iterator<C> {
return this.delegate.asSequence().iterator() return this.delegate.asSequence().iterator()
} }
@PlannedRemoval("1.0.0")
@Suppress("PropertyName")
@get:JvmName("getIdContentString")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
val _idContentString: String
get() = this.idContentString
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
inline fun forEach(block: (C) -> Unit) = delegate.forEach(block)
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun first(): C {
forEach { return it }
throw NoSuchElementException()
}
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun firstOrNull(): C? {
forEach { return it }
return null
}
} }
/** /**
...@@ -76,64 +51,17 @@ class ContactList<C : Contact>(@MiraiInternalAPI("Implementation may change in f ...@@ -76,64 +51,17 @@ class ContactList<C : Contact>(@MiraiInternalAPI("Implementation may change in f
* ``` * ```
*/ */
val ContactList<*>.idContentString: String val ContactList<*>.idContentString: String
get() = "[" + @OptIn(MiraiInternalAPI::class) buildString { delegate.forEach { append(it.id).append(", ") } }.dropLast( get() = "[" + buildString { delegate.forEach { append(it.id).append(", ") } }.dropLast(
2 2
) + "]" ) + "]"
@MiraiInternalAPI internal operator fun <C : Contact> LockFreeLinkedList<C>.get(id: Long): C {
operator fun <C : Contact> LockFreeLinkedList<C>.get(id: Long): C {
forEach { if (it.id == id) return it } forEach { if (it.id == id) return it }
throw NoSuchElementException("No such contact: $id") throw NoSuchElementException("No such contact: $id")
} }
@MiraiInternalAPI internal fun <C : Contact> LockFreeLinkedList<C>.getOrNull(id: Long): C? {
fun <C : Contact> LockFreeLinkedList<C>.getOrNull(id: Long): C? {
forEach { if (it.id == id) return it } forEach { if (it.id == id) return it }
return null return null
} }
\ No newline at end of file
@OptIn(MiraiInternalAPI::class)
@PlannedRemoval("1.0.0")
@Deprecated(
"use firstOrNull from stdlib",
replaceWith = ReplaceWith("this.asSequence().firstOrNull(filter)"),
level = DeprecationLevel.ERROR
)
inline fun <C : Contact> LockFreeLinkedList<C>.firstOrNull(filter: (C) -> Boolean): C? {
forEach { if (filter(it)) return it }
return null
}
@OptIn(MiraiInternalAPI::class)
@PlannedRemoval("1.0.0")
@Deprecated(
"use firstOrNull from stdlib",
replaceWith = ReplaceWith("firstOrNull(filter)"),
level = DeprecationLevel.ERROR
)
inline fun <C : Contact> LockFreeLinkedList<C>.filteringGetOrNull(filter: (C) -> Boolean): C? =
this.asSequence().firstOrNull(filter)
@PlannedRemoval("1.0.0")
@Deprecated("use Iterator.toList from stdlib", level = DeprecationLevel.HIDDEN)
fun <E : Contact> ContactList<E>.toList(): List<E> = toMutableList()
@PlannedRemoval("1.0.0")
@Deprecated("use Iterator.toMutableList from stdlib", level = DeprecationLevel.HIDDEN)
@OptIn(MiraiInternalAPI::class)
fun <E : Contact> ContactList<E>.toMutableList(): MutableList<E> = this.delegate.toMutableList()
@PlannedRemoval("1.0.0")
@Deprecated("use Iterator.toSet from stdlib", level = DeprecationLevel.HIDDEN)
fun <E : Contact> ContactList<E>.toSet(): Set<E> = toMutableSet()
@PlannedRemoval("1.0.0")
@Deprecated("use Iterator.toMutableSet from stdlib", level = DeprecationLevel.HIDDEN)
@OptIn(MiraiInternalAPI::class)
fun <E : Contact> ContactList<E>.toMutableSet(): MutableSet<E> = this.delegate.toMutableSet()
@PlannedRemoval("1.0.0")
@Deprecated("use Iterator.asSequence from stdlib", level = DeprecationLevel.HIDDEN)
@OptIn(MiraiInternalAPI::class)
fun <E : Contact> ContactList<E>.asSequence(): Sequence<E> = this.delegate.asSequence()
\ No newline at end of file
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.SinceMirai
/** /**
* 拥有 [id] 的对象. * 拥有 [id] 的对象.
...@@ -19,7 +18,6 @@ import net.mamoe.mirai.utils.SinceMirai ...@@ -19,7 +18,6 @@ import net.mamoe.mirai.utils.SinceMirai
* @see Contact * @see Contact
* @see Bot * @see Bot
*/ */
@SinceMirai("0.39.0")
interface ContactOrBot { interface ContactOrBot {
/** /**
* QQ 号或群号. * QQ 号或群号.
......
...@@ -12,14 +12,12 @@ ...@@ -12,14 +12,12 @@
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.utils.SinceMirai
/** /**
* 发送消息时消息过长抛出的异常. * 发送消息时消息过长抛出的异常.
* *
* @see Contact.sendMessage * @see Contact.sendMessage
*/ */
@SinceMirai("0.32.0")
class MessageTooLargeException( class MessageTooLargeException(
val target: Contact, val target: Contact,
/** /**
...@@ -38,7 +36,6 @@ class MessageTooLargeException( ...@@ -38,7 +36,6 @@ class MessageTooLargeException(
* *
* @see Group.sendMessage * @see Group.sendMessage
*/ */
@SinceMirai("0.33.0")
class BotIsBeingMutedException( class BotIsBeingMutedException(
val target: Group val target: Group
) : RuntimeException("bot is being muted, remaining ${target.botMuteRemaining} seconds") ) : RuntimeException("bot is being muted, remaining ${target.botMuteRemaining} seconds")
......
...@@ -16,21 +16,23 @@ import net.mamoe.mirai.Bot ...@@ -16,21 +16,23 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.message.FriendMessageEvent
import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.toMessage import net.mamoe.mirai.message.data.toMessage
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
/** /**
* 好友 对象. * 代表一位好友.
* 注意: 一个 [Friend] 实例并不是独立的, 它属于一个 [Bot].
* 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
* *
* 对于同一个 [Bot] 任何一个人的 [Friend] 实例都是单一的. * 一个 [Friend] 实例并不是独立的, 它属于一个 [Bot].
* 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取. * 对于同一个 [Bot], 任何一个人的 [Friend] 实例都是单一的.
* [Friend] 无法通过任何方式直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
*
* @see FriendMessageEvent
*/ */
@Suppress("DEPRECATION_ERROR") @Suppress("DEPRECATION_ERROR")
abstract class Friend : QQ(), CoroutineScope { abstract class Friend : User(), CoroutineScope {
/** /**
* 请求头像下载链接 * 请求头像下载链接
*/ */
......
...@@ -47,7 +47,6 @@ abstract class Group : Contact(), CoroutineScope { ...@@ -47,7 +47,6 @@ abstract class Group : Contact(), CoroutineScope {
/** /**
* 群设置 * 群设置
*/ */
@SinceMirai("0.30.0")
abstract val settings: GroupSettings abstract val settings: GroupSettings
/** /**
...@@ -80,7 +79,6 @@ abstract class Group : Contact(), CoroutineScope { ...@@ -80,7 +79,6 @@ abstract class Group : Contact(), CoroutineScope {
* 机器人在这个群里的权限 * 机器人在这个群里的权限
* *
* @see Group.checkBotPermission 检查 [Bot] 在这个群里的权限 * @see Group.checkBotPermission 检查 [Bot] 在这个群里的权限
* @see Group.checkBotPermissionOperator 要求 [Bot] 在这个群里的权限为 [管理员或群主][MemberPermission.isOperator]
* *
* @see BotGroupPermissionChangeEvent 机器人群员修改 * @see BotGroupPermissionChangeEvent 机器人群员修改
*/ */
...@@ -123,7 +121,6 @@ abstract class Group : Contact(), CoroutineScope { ...@@ -123,7 +121,6 @@ abstract class Group : Contact(), CoroutineScope {
* @return 退出成功时 true; 已经退出时 false * @return 退出成功时 true; 已经退出时 false
*/ */
@JvmSynthetic @JvmSynthetic
@SinceMirai("0.37.0")
abstract suspend fun quit(): Boolean abstract suspend fun quit(): Boolean
/** /**
...@@ -197,7 +194,6 @@ abstract class Group : Contact(), CoroutineScope { ...@@ -197,7 +194,6 @@ abstract class Group : Contact(), CoroutineScope {
@Suppress("FunctionName") @Suppress("FunctionName")
@JvmName("quit") @JvmName("quit")
@JavaFriendlyAPI @JavaFriendlyAPI
@SinceMirai("0.39.4")
fun __quitBlockingForJava__(): Boolean = runBlocking { quit() } fun __quitBlockingForJava__(): Boolean = runBlocking { quit() }
} }
...@@ -206,7 +202,6 @@ abstract class Group : Contact(), CoroutineScope { ...@@ -206,7 +202,6 @@ abstract class Group : Contact(), CoroutineScope {
* *
* @see Group.settings 获取群设置 * @see Group.settings 获取群设置
*/ */
@SinceMirai("0.30.0")
interface GroupSettings { interface GroupSettings {
/** /**
* 入群公告, 没有时为空字符串. * 入群公告, 没有时为空字符串.
......
...@@ -27,4 +27,4 @@ expect abstract class ContactJavaFriendlyAPI internal constructor() ...@@ -27,4 +27,4 @@ expect abstract class ContactJavaFriendlyAPI internal constructor()
@Suppress("DEPRECATION_ERROR") @Suppress("DEPRECATION_ERROR")
@MiraiInternalAPI @MiraiInternalAPI
@JavaFriendlyAPI @JavaFriendlyAPI
expect abstract class MemberJavaFriendlyAPI internal constructor() : QQ // 将来会改为 User expect abstract class MemberJavaFriendlyAPI internal constructor() : User
\ No newline at end of file \ No newline at end of file
...@@ -19,19 +19,19 @@ import net.mamoe.mirai.message.MessageReceipt ...@@ -19,19 +19,19 @@ import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.toMessage import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.PlannedRemoval
import net.mamoe.mirai.utils.SinceMirai
import net.mamoe.mirai.utils.WeakRefProperty import net.mamoe.mirai.utils.WeakRefProperty
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.ExperimentalTime import kotlin.time.ExperimentalTime
/** /**
* 群成员. * 代表一位群成员.
* *
* 群成员可能也是好友, 但他们在对象类型上不同. * 群成员可能也是好友, 但他们在对象类型上不同.
* 群成员可以通过 [asFriend] 得到相关好友对象. * 群成员可以通过 [asFriend] 得到相关好友对象.
*
* ## 与好友相关的操作
* [Member.isFriend] 判断此成员是否为好友
*/ */
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME")
@OptIn(MiraiInternalAPI::class, JavaFriendlyAPI::class) @OptIn(MiraiInternalAPI::class, JavaFriendlyAPI::class)
...@@ -95,12 +95,16 @@ abstract class Member : MemberJavaFriendlyAPI() { ...@@ -95,12 +95,16 @@ abstract class Member : MemberJavaFriendlyAPI() {
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常. * @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
* @return 机器人无权限时返回 `false` * @return 机器人无权限时返回 `false`
* *
* @see Member.isMuted 判断此成员是否正处于禁言状态中
* @see unmute 取消禁言此成员
*
* @see Int.minutesToSeconds * @see Int.minutesToSeconds
* @see Int.hoursToSeconds * @see Int.hoursToSeconds
* @see Int.daysToSeconds * @see Int.daysToSeconds
* *
* @see MemberMuteEvent 成员被禁言事件 * @see MemberMuteEvent 成员被禁言事件
* @throws PermissionDeniedException 无权限修改时 *
* @throws PermissionDeniedException 无权限修改时抛出
*/ */
@JvmSynthetic @JvmSynthetic
abstract suspend fun mute(durationSeconds: Int) abstract suspend fun mute(durationSeconds: Int)
...@@ -110,8 +114,11 @@ abstract class Member : MemberJavaFriendlyAPI() { ...@@ -110,8 +114,11 @@ abstract class Member : MemberJavaFriendlyAPI() {
* *
* 管理员可解除成员的禁言, 群主可解除管理员和群员的禁言. * 管理员可解除成员的禁言, 群主可解除管理员和群员的禁言.
* *
* @see MemberUnmuteEvent 成员被取消禁言事件. * @see Member.isMuted 判断此成员是否正处于禁言状态中
* @throws PermissionDeniedException 无权限修改时 *
* @see MemberUnmuteEvent 成员被取消禁言事件
*
* @throws PermissionDeniedException 无权限修改时抛出
*/ */
@JvmSynthetic @JvmSynthetic
abstract suspend fun unmute() abstract suspend fun unmute()
...@@ -164,26 +171,22 @@ abstract class Member : MemberJavaFriendlyAPI() { ...@@ -164,26 +171,22 @@ abstract class Member : MemberJavaFriendlyAPI() {
* *
* @throws IllegalStateException 当此成员不是好友时抛出 * @throws IllegalStateException 当此成员不是好友时抛出
*/ */
@SinceMirai("0.39.0")
fun Member.asFriend(): Friend = this.bot.getFriendOrNull(this.id) ?: error("$this is not a friend") fun Member.asFriend(): Friend = this.bot.getFriendOrNull(this.id) ?: error("$this is not a friend")
/** /**
* 得到此成员作为好友的对象. * 得到此成员作为好友的对象, 当此成员不是好友时返回 `null`
*/ */
@SinceMirai("0.39.0")
fun Member.asFriendOrNull(): Friend? = this.bot.getFriendOrNull(this.id) fun Member.asFriendOrNull(): Friend? = this.bot.getFriendOrNull(this.id)
/** /**
* 判断此成员是否为好友 * 判断此成员是否为好友
*/ */
@SinceMirai("0.39.0")
inline val Member.isFriend: Boolean inline val Member.isFriend: Boolean
get() = this.bot.friends.contains(this.id) get() = this.bot.friends.contains(this.id)
/** /**
* 如果此成员是好友, 则执行 [block] 并返回其返回值. 否则返回 `null` * 如果此成员是好友, 则执行 [block] 并返回其返回值. 否则返回 `null`
*/ */
@SinceMirai("0.39.0")
inline fun <R> Member.takeIfIsFriend(block: (Friend) -> R): R? { inline fun <R> Member.takeIfIsFriend(block: (Friend) -> R): R? {
return this.asFriendOrNull()?.let(block) return this.asFriendOrNull()?.let(block)
} }
...@@ -191,7 +194,7 @@ inline fun <R> Member.takeIfIsFriend(block: (Friend) -> R): R? { ...@@ -191,7 +194,7 @@ inline fun <R> Member.takeIfIsFriend(block: (Friend) -> R): R? {
/** /**
* 获取非空群名片或昵称. * 获取非空群名片或昵称.
* *
* 若 [群名片][Member.nameCard] 不为空则返回群名片, 为空则返回 [QQ.nick] * 若 [群名片][Member.nameCard] 不为空则返回群名片, 为空则返回 [User.nick]
*/ */
val Member.nameCardOrNick: String get() = this.nameCard.takeIf { it.isNotEmpty() } ?: this.nick val Member.nameCardOrNick: String get() = this.nameCard.takeIf { it.isNotEmpty() } ?: this.nick
...@@ -202,27 +205,15 @@ val Member.nameCardOrNick: String get() = this.nameCard.takeIf { it.isNotEmpty() ...@@ -202,27 +205,15 @@ val Member.nameCardOrNick: String get() = this.nameCard.takeIf { it.isNotEmpty()
* *
* 否则返回 [Member.nick] * 否则返回 [Member.nick]
*/ */
@SinceMirai("0.39.0")
val User.nameCardOrNick: String val User.nameCardOrNick: String
get() = when (this) { get() = when (this) {
is Member -> this.nameCardOrNick is Member -> this.nameCardOrNick
else -> this.nick else -> this.nick
} }
/**
* 判断改成员是否处于禁言状态.
*/
@JvmName("isMuted2") // make compiler happy
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.InlineOnly // val Member.isMuted 编译在 JVM 也会产生 `public boolean isMuted(Member receive)`
@PlannedRemoval("1.0.0")
@Deprecated("use property instead", ReplaceWith("this.isMuted"))
inline fun Member.isMuted(): Boolean = this.isMuted
/** /**
* 判断群成员是否处于禁言状态. * 判断群成员是否处于禁言状态.
*/ */
@SinceMirai("0.39.0")
val Member.isMuted: Boolean val Member.isMuted: Boolean
get() = muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt() get() = muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt()
......
...@@ -7,17 +7,24 @@ ...@@ -7,17 +7,24 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("NOTHING_TO_INLINE") @file:Suppress("NOTHING_TO_INLINE", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.SinceMirai import kotlin.internal.InlineOnly
/** /**
* 群成员的权限. * 群成员的权限.
* *
* 可通过 [compareTo] 判断是否有更高的权限. * 可通过 [compareTo] 判断是否有更高的权限.
*
* @see isOwner 判断权限是否为群主
* @see isOperator 判断权限是否为管理员或群主
*
* @see Member.isOwner 对 [Member] 的扩展函数, 判断此成员是否为群主
* @see Member.isOperator 对 [Member] 的扩展函数, 判断此成员是否为管理员或群主
* @see Member.isAdministrator 对 [Member] 的扩展函数, 判断此成员是否为管理员
*/ */
enum class MemberPermission : Comparable<MemberPermission> { enum class MemberPermission : Comparable<MemberPermission> {
/** /**
...@@ -38,7 +45,6 @@ enum class MemberPermission : Comparable<MemberPermission> { ...@@ -38,7 +45,6 @@ enum class MemberPermission : Comparable<MemberPermission> {
/** /**
* 权限等级. [OWNER] 为 2, [ADMINISTRATOR] 为 1, [MEMBER] 为 0 * 权限等级. [OWNER] 为 2, [ADMINISTRATOR] 为 1, [MEMBER] 为 0
*/ */
@SinceMirai("0.32.0")
val level: Int val level: Int
get() = ordinal get() = ordinal
} }
...@@ -46,16 +52,19 @@ enum class MemberPermission : Comparable<MemberPermission> { ...@@ -46,16 +52,19 @@ enum class MemberPermission : Comparable<MemberPermission> {
/** /**
* 判断权限是否为群主 * 判断权限是否为群主
*/ */
@InlineOnly
inline fun MemberPermission.isOwner(): Boolean = this == MemberPermission.OWNER inline fun MemberPermission.isOwner(): Boolean = this == MemberPermission.OWNER
/** /**
* 判断权限是否为管理员 * 判断权限是否为管理员
*/ */
@InlineOnly
inline fun MemberPermission.isAdministrator(): Boolean = this == MemberPermission.ADMINISTRATOR inline fun MemberPermission.isAdministrator(): Boolean = this == MemberPermission.ADMINISTRATOR
/** /**
* 判断权限是否为管理员或群主 * 判断权限是否为管理员或群主
*/ */
@InlineOnly
inline fun MemberPermission.isOperator(): Boolean = isAdministrator() || isOwner() inline fun MemberPermission.isOperator(): Boolean = isAdministrator() || isOwner()
...@@ -85,7 +94,7 @@ class PermissionDeniedException : IllegalStateException { ...@@ -85,7 +94,7 @@ class PermissionDeniedException : IllegalStateException {
} }
/** /**
* 要求 [Bot] 在这个群里的权限为 [required], 否则抛出异常 [PermissionDeniedException] * 要求 [Bot] 在这个群里的权限至少为 [required], 否则抛出异常 [PermissionDeniedException]
* *
* @throws PermissionDeniedException * @throws PermissionDeniedException
*/ */
...@@ -95,7 +104,7 @@ inline fun Group.checkBotPermission( ...@@ -95,7 +104,7 @@ inline fun Group.checkBotPermission(
"Permission denied: required $required, got actual $botPermission for $bot in group $id" "Permission denied: required $required, got actual $botPermission for $bot in group $id"
} }
) { ) {
if (botPermission != required) { if (botPermission < required) {
throw PermissionDeniedException(lazyMessage()) throw PermissionDeniedException(lazyMessage())
} }
} }
...@@ -105,12 +114,9 @@ inline fun Group.checkBotPermission( ...@@ -105,12 +114,9 @@ inline fun Group.checkBotPermission(
* *
* @throws PermissionDeniedException * @throws PermissionDeniedException
*/ */
@Deprecated("use checkBotPermission", ReplaceWith("checkBotPermission(MemberPermission.ADMINISTRATOR)"))
inline fun Group.checkBotPermissionOperator( inline fun Group.checkBotPermissionOperator(
crossinline lazyMessage: () -> String = { crossinline lazyMessage: () -> String = {
"Permission denied: required ${MemberPermission.ADMINISTRATOR} or ${MemberPermission.OWNER}, got actual $botPermission for $bot in group $id" "Permission denied: required ${MemberPermission.ADMINISTRATOR} or ${MemberPermission.OWNER}, got actual $botPermission for $bot in group $id"
} }
) { ) = checkBotPermission(MemberPermission.ADMINISTRATOR, lazyMessage)
if (!botPermission.isOperator()) { \ No newline at end of file
throw PermissionDeniedException(lazyMessage())
}
}
\ No newline at end of file
/*
* 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", "unused")
package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.utils.PlannedRemoval
import kotlin.jvm.JvmSynthetic
/**
* QQ 对象.
*
* 自 0.39.0 起 mirai 引入 [User] 作为 [Friend] 和 [Member] 的父类,
* 以备将来支持仅 [Friend] 可用的 API, 如设置备注.
*
* 所有 API 均有二进制兼容.
*
* 请根据实际情况, 使用 [Friend] 或 [User] 替代.
*/
@PlannedRemoval("1.0.0")
@Deprecated(
"use Friend or Person instead",
replaceWith = ReplaceWith("Friend", "net.mamoe.mirai.contact.Friend"),
level = DeprecationLevel.ERROR
)
@Suppress("DEPRECATION_ERROR")
abstract class QQ : User(), CoroutineScope {
/**
* QQ 号码
*/
abstract override val id: Long
/**
* 昵称
*/
abstract override val nick: String
/**
* 头像下载链接
*/
override val avatarUrl: String
get() = "http://q1.qlogo.cn/g?b=qq&nk=$id&s=640"
/**
* 向这个对象发送消息.
*
* 单条消息最大可发送 4500 字符或 50 张图片.
*
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送群消息事件. cancellable
*
* @throws EventCancelledException 当发送消息事件被取消时抛出
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
* @throws MessageTooLargeException 当消息过长时抛出
*
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
*/
@JvmSynthetic
abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ>
}
\ No newline at end of file
/*
* 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:JvmMultifileClass
@file:JvmName("BotHelperKt")
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.data
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
@MiraiExperimentalAPI
@Suppress("ClassName")
sealed class AddFriendResult {
abstract class DONE internal constructor() : AddFriendResult() {
override fun toString(): String = "AddFriendResult(Done)"
}
/**
* 对方拒绝添加好友
*/
object REJECTED : AddFriendResult() {
override fun toString(): String = "AddFriendResult(Rejected)"
}
/**
* 这个人已经是好友
*/
object ALREADY_ADDED : DONE() {
override fun toString(): String = "AddFriendResult(AlreadyAdded)"
}
/**
* 等待对方同意
*/
object WAITING_FOR_APPROVAL : DONE() {
override fun toString(): String = "AddFriendResult(WaitingForApproval)"
}
/**
* 成功添加 (只在对方设置为允许任何人直接添加为好友时才会获得这个结果)
*/
object ADDED : DONE() {
override fun toString(): String = "AddFriendResult(Added)"
}
}
\ No newline at end of file
...@@ -4,14 +4,12 @@ package net.mamoe.mirai.data ...@@ -4,14 +4,12 @@ package net.mamoe.mirai.data
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
/** /**
* 群统计信息 * 群统计信息
*/ */
@MiraiExperimentalAPI @MiraiExperimentalAPI
@SinceMirai("0.28.0")
@Serializable @Serializable
data class GroupActiveData( data class GroupActiveData(
......
/*
* 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.data
import net.mamoe.mirai.utils.MiraiExperimentalAPI
/**
* 曾用名列表
*
* 曾用名可能是:
* - 昵称
* - 共同群内的群名片
*/
@MiraiExperimentalAPI
class PreviousNameList(
list: List<String>
) : List<String> by list {
override fun toString(): String = this.joinToString(prefix = "PreviousNameList(", postfix = ")", separator = ", ")
}
\ No newline at end of file
...@@ -11,9 +11,7 @@ ...@@ -11,9 +11,7 @@
package net.mamoe.mirai.data package net.mamoe.mirai.data
import io.ktor.util.date.GMTDate /*
import net.mamoe.mirai.utils.MiraiExperimentalAPI
/** /**
* 个人资料 * 个人资料
*/ */
...@@ -61,4 +59,4 @@ enum class Gender(val value: Byte) { ...@@ -61,4 +59,4 @@ enum class Gender(val value: Byte) {
SECRET(0), SECRET(0),
MALE(1), MALE(1),
FEMALE(2) FEMALE(2)
} }*/
\ No newline at end of file \ No newline at end of file
...@@ -70,7 +70,7 @@ interface Event { ...@@ -70,7 +70,7 @@ interface Event {
abstract class AbstractEvent : Event { abstract class AbstractEvent : Event {
@Suppress("WRONG_MODIFIER_CONTAINING_DECLARATION", "PropertyName") @Suppress("WRONG_MODIFIER_CONTAINING_DECLARATION", "PropertyName")
@get:JvmSynthetic // so Java user won't see it @get:JvmSynthetic // so Java user won't see it
@Deprecated("", level = DeprecationLevel.HIDDEN) @Deprecated("prohibit illegal overrides", level = DeprecationLevel.HIDDEN)
final override val DoNotImplementThisClassButExtendAbstractEvent: Nothing final override val DoNotImplementThisClassButExtendAbstractEvent: Nothing
get() = throw Error("Shouldn't be reached") get() = throw Error("Shouldn't be reached")
......
...@@ -17,13 +17,11 @@ package net.mamoe.mirai.event ...@@ -17,13 +17,11 @@ package net.mamoe.mirai.event
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.internal.* import net.mamoe.mirai.event.internal.*
import net.mamoe.mirai.message.ContactMessage import net.mamoe.mirai.message.FriendMessageEvent
import net.mamoe.mirai.message.FriendMessage import net.mamoe.mirai.message.GroupMessageEvent
import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.MessageEvent
import net.mamoe.mirai.message.TempMessage import net.mamoe.mirai.message.TempMessageEvent
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.PlannedRemoval
import kotlin.js.JsName
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
...@@ -33,7 +31,7 @@ import kotlin.jvm.JvmSynthetic ...@@ -33,7 +31,7 @@ import kotlin.jvm.JvmSynthetic
* 消息事件的处理器. * 消息事件的处理器.
* *
* 注: * 注:
* 接受者 T 为 [ContactMessage] * 接受者 T 为 [MessageEvent]
* 参数 String 为 转为字符串了的消息 ([Message.toString]) * 参数 String 为 转为字符串了的消息 ([Message.toString])
*/ */
typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
...@@ -49,7 +47,7 @@ typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R ...@@ -49,7 +47,7 @@ typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
* @see subscribeFriendMessages * @see subscribeFriendMessages
*/ */
@MessageDsl @MessageDsl
open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>( open class MessageSubscribersBuilder<M : MessageEvent, out Ret, R : RR, RR>(
/** /**
* 用于 [MessageListener] 无返回值的替代. * 用于 [MessageListener] 无返回值的替代.
*/ */
...@@ -233,7 +231,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>( ...@@ -233,7 +231,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
/** 如果是这个人发的消息. 消息目前只会是群消息 */ /** 如果是这个人发的消息. 消息目前只会是群消息 */
@MessageDsl @MessageDsl
fun sentBy(name: String): ListeningFilter = content { this is GroupMessage && this.senderName == name } fun sentBy(name: String): ListeningFilter = content { this is GroupMessageEvent && this.senderName == name }
/** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */ /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
@MessageDsl @MessageDsl
...@@ -249,36 +247,37 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>( ...@@ -249,36 +247,37 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
/** 如果是好友发来的消息 */ /** 如果是好友发来的消息 */
@MessageDsl @MessageDsl
fun sentByFriend(onEvent: MessageListener<FriendMessage, R>): Ret = fun sentByFriend(onEvent: MessageListener<FriendMessageEvent, R>): Ret =
content({ this is FriendMessage }) { onEvent(this as FriendMessage, it) } content({ this is FriendMessageEvent }) { onEvent(this as FriendMessageEvent, it) }
/** 如果是好友发来的消息 */ /** 如果是好友发来的消息 */
@MessageDsl @MessageDsl
fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessage } fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessageEvent }
/** 如果是好友发来的消息 */ /** 如果是群临时会话消息 */
@MessageDsl @MessageDsl
fun sentByTemp(): ListeningFilter = newListeningFilter { this is TempMessage } fun sentByTemp(): ListeningFilter = newListeningFilter { this is TempMessageEvent }
/** 如果是管理员或群主发的消息 */ /** 如果是管理员或群主发的消息 */
@MessageDsl @MessageDsl
fun sentByOperator(): ListeningFilter = content { this is GroupMessage && sender.permission.isOperator() } fun sentByOperator(): ListeningFilter = content { this is GroupMessageEvent && sender.permission.isOperator() }
/** 如果是管理员发的消息 */ /** 如果是管理员发的消息 */
@MessageDsl @MessageDsl
fun sentByAdministrator(): ListeningFilter = content { this is GroupMessage && sender.permission.isAdministrator() } fun sentByAdministrator(): ListeningFilter =
content { this is GroupMessageEvent && sender.permission.isAdministrator() }
/** 如果是群主发的消息 */ /** 如果是群主发的消息 */
@MessageDsl @MessageDsl
fun sentByOwner(): ListeningFilter = content { this is GroupMessage && sender.isOwner() } fun sentByOwner(): ListeningFilter = content { this is GroupMessageEvent && sender.isOwner() }
/** 如果是来自这个群的消息 */ /** 如果是来自这个群的消息 */
@MessageDsl @MessageDsl
fun sentFrom(groupId: Long): ListeningFilter = content { this is GroupMessage && group.id == groupId } fun sentFrom(groupId: Long): ListeningFilter = content { this is GroupMessageEvent && group.id == groupId }
/** 如果是来自这个群的消息 */ /** 如果是来自这个群的消息 */
@MessageDsl @MessageDsl
fun sentFrom(group: Group): ListeningFilter = content { this is GroupMessage && group.id == group.id } fun sentFrom(group: Group): ListeningFilter = content { this is GroupMessageEvent && group.id == group.id }
/** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At] */ /** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At] */
@MessageDsl @MessageDsl
...@@ -440,79 +439,6 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>( ...@@ -440,79 +439,6 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
} }
return stub return stub
} }
/**
* 不考虑空格, [消息内容][Message.contentToString]以 [this] 开始则执行 [replier] 并将其返回值回复给发信对象.
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
*/
@PlannedRemoval("1.0.0")
@Deprecated("use startsWith on your own", replaceWith = ReplaceWith("startsWith(this, true, true, replier)"))
open infix fun String.startsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
val toCheck = this.trimStart()
return content({ it.trim().startsWith(toCheck) }, {
executeAndReply(this) { replier(this, it.trim().removePrefix(toCheck)) }
})
}
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun contains(message: Message, onEvent: MessageListener<M, R>): Ret {
return content({ this.message.any { it == message } }, onEvent)
}
@JvmName("case1")
@JsName("case1")
@PlannedRemoval("1.0.0")
@Deprecated("use String.invoke", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("this(block)"))
infix fun String.`->`(block: MessageListener<M, R>): Ret {
return this(block)
}
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun containsAll(
vararg sub: String,
ignoreCase: Boolean = false,
trim: Boolean = true,
onEvent: MessageListener<M, R>
): Ret = containsAllImpl(sub, ignoreCase = ignoreCase, trim = trim).invoke(onEvent)
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun containsAny(
vararg sub: String,
ignoreCase: Boolean = false,
trim: Boolean = true,
onEvent: MessageListener<M, R>
): Ret = containsAnyImpl(*sub, ignoreCase = ignoreCase, trim = trim).invoke(onEvent)
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun sentBy(name: String, onEvent: MessageListener<M, R>): Ret =
content({ (this as? GroupMessage)?.senderName == name }, onEvent)
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun sentByOperator(onEvent: MessageListener<M, R>): Ret =
content({ this is GroupMessage && this.sender.isOperator() }, onEvent)
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun sentByAdministrator(onEvent: MessageListener<M, R>): Ret =
content({ this is GroupMessage && this.sender.isAdministrator() }, onEvent)
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun sentByOwner(onEvent: MessageListener<M, R>): Ret =
content({ this is GroupMessage && this.sender.isOwner() }, onEvent)
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun sentFrom(groupId: Long, onEvent: MessageListener<GroupMessage, R>): Ret =
content({ this is GroupMessage && this.group.id == groupId }) { onEvent(this as GroupMessage, it) }
} }
/** /**
......
...@@ -25,7 +25,9 @@ import net.mamoe.mirai.message.data.Image ...@@ -25,7 +25,9 @@ import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.qqandroid.network.Packet import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.internal.runBlocking import net.mamoe.mirai.utils.internal.runBlocking
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
...@@ -75,7 +77,6 @@ sealed class BotOfflineEvent : BotEvent, AbstractEvent() { ...@@ -75,7 +77,6 @@ sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
/** /**
* 服务器主动要求更换另一个服务器 * 服务器主动要求更换另一个服务器
*/ */
@SinceMirai("0.37.1")
data class RequireReconnect(override val bot: Bot) : BotOfflineEvent(), Packet, BotPassiveEvent data class RequireReconnect(override val bot: Bot) : BotOfflineEvent(), Packet, BotPassiveEvent
} }
...@@ -131,7 +132,6 @@ sealed class MessageRecallEvent : BotEvent, AbstractEvent() { ...@@ -131,7 +132,6 @@ sealed class MessageRecallEvent : BotEvent, AbstractEvent() {
* 消息内部 id. * 消息内部 id.
* @see MessageSource.id * @see MessageSource.id
*/ */
@SinceMirai("0.39.0")
abstract val messageInternalId: Int abstract val messageInternalId: Int
/** /**
...@@ -148,7 +148,7 @@ sealed class MessageRecallEvent : BotEvent, AbstractEvent() { ...@@ -148,7 +148,7 @@ sealed class MessageRecallEvent : BotEvent, AbstractEvent() {
override val messageInternalId: Int, override val messageInternalId: Int,
override val messageTime: Int, override val messageTime: Int,
/** /**
* 撤回操作人, 可能为 [Bot.uin] 或好友的 [QQ.id] * 撤回操作人, 可能为 [Bot.uin] 或好友的 [User.id]
*/ */
val operator: Long val operator: Long
) : MessageRecallEvent(), Packet { ) : MessageRecallEvent(), Packet {
...@@ -235,20 +235,17 @@ sealed class ImageUploadEvent : BotEvent, BotActiveEvent, AbstractEvent() { ...@@ -235,20 +235,17 @@ sealed class ImageUploadEvent : BotEvent, BotActiveEvent, AbstractEvent() {
/** /**
* 机器人被踢出群或在其他客户端主动退出一个群. 在事件广播前 [Bot.groups] 就已删除这个群. * 机器人被踢出群或在其他客户端主动退出一个群. 在事件广播前 [Bot.groups] 就已删除这个群.
*/ */
@SinceMirai("0.36.0")
sealed class BotLeaveEvent : BotEvent, Packet, AbstractEvent() { sealed class BotLeaveEvent : BotEvent, Packet, AbstractEvent() {
abstract val group: Group abstract val group: Group
/** /**
* 机器人主动退出一个群. * 机器人主动退出一个群.
*/ */
@SinceMirai("0.37.0")
data class Active(override val group: Group) : BotLeaveEvent() data class Active(override val group: Group) : BotLeaveEvent()
/** /**
* 机器人被管理员或群主踢出群. 暂不支持获取操作人 * 机器人被管理员或群主踢出群. 暂不支持获取操作人
*/ */
@SinceMirai("0.37.0")
data class Kick(override val group: Group) : BotLeaveEvent() data class Kick(override val group: Group) : BotLeaveEvent()
override val bot: Bot get() = group.bot override val bot: Bot get() = group.bot
...@@ -321,7 +318,6 @@ data class GroupNameChangeEvent( ...@@ -321,7 +318,6 @@ data class GroupNameChangeEvent(
/** /**
* 操作人. 为 null 时则是机器人操作 * 操作人. 为 null 时则是机器人操作
*/ */
@SinceMirai("0.37.3")
override val operator: Member? override val operator: Member?
) : GroupSettingChangeEvent<String>, Packet, GroupOperableEvent, AbstractEvent() { ) : GroupSettingChangeEvent<String>, Packet, GroupOperableEvent, AbstractEvent() {
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
...@@ -409,13 +405,11 @@ sealed class MemberJoinEvent(override val member: Member) : GroupMemberEvent, Bo ...@@ -409,13 +405,11 @@ sealed class MemberJoinEvent(override val member: Member) : GroupMemberEvent, Bo
/** /**
* 被邀请加入群 * 被邀请加入群
*/ */
@SinceMirai("0.36.0")
data class Invite(override val member: Member) : MemberJoinEvent(member) data class Invite(override val member: Member) : MemberJoinEvent(member)
/** /**
* 成员主动加入群 * 成员主动加入群
*/ */
@SinceMirai("0.36.0")
data class Active(override val member: Member) : MemberJoinEvent(member) data class Active(override val member: Member) : MemberJoinEvent(member)
} }
...@@ -451,7 +445,6 @@ sealed class MemberLeaveEvent : GroupMemberEvent, AbstractEvent() { ...@@ -451,7 +445,6 @@ sealed class MemberLeaveEvent : GroupMemberEvent, AbstractEvent() {
/** /**
* [Bot] 被邀请加入一个群. * [Bot] 被邀请加入一个群.
*/ */
@SinceMirai("0.39.4")
data class BotInvitedJoinGroupRequestEvent( data class BotInvitedJoinGroupRequestEvent(
override val bot: Bot, override val bot: Bot,
/** /**
...@@ -494,7 +487,6 @@ data class BotInvitedJoinGroupRequestEvent( ...@@ -494,7 +487,6 @@ data class BotInvitedJoinGroupRequestEvent(
/** /**
* 一个账号请求加入群事件, [Bot] 在此群中是管理员或群主. * 一个账号请求加入群事件, [Bot] 在此群中是管理员或群主.
*/ */
@SinceMirai("0.35.0")
data class MemberJoinRequestEvent( data class MemberJoinRequestEvent(
override val bot: Bot, override val bot: Bot,
/** /**
...@@ -566,17 +558,8 @@ data class MemberCardChangeEvent( ...@@ -566,17 +558,8 @@ data class MemberCardChangeEvent(
*/ */
val new: String, val new: String,
override val member: Member, override val member: Member
) : GroupMemberEvent, Packet, AbstractEvent()
/**
* 此事件无法确定操作人, 将在未来版本删除
*/
@PlannedRemoval("1.0.0")
@Deprecated("operator is always unknown", level = DeprecationLevel.ERROR)
override val operator: Member?
) : GroupMemberEvent, Packet, AbstractEvent(),
@Deprecated("operator is always unknown", level = DeprecationLevel.ERROR)
GroupOperableEvent
/** /**
* 成员群头衔改动. 一定为群主操作 * 成员群头衔改动. 一定为群主操作
...@@ -623,6 +606,8 @@ data class MemberPermissionChangeEvent( ...@@ -623,6 +606,8 @@ data class MemberPermissionChangeEvent(
/** /**
* 群成员被禁言事件. 被禁言的成员都不可能是机器人本人 * 群成员被禁言事件. 被禁言的成员都不可能是机器人本人
*
* @see BotMuteEvent 机器人被禁言的事件
*/ */
data class MemberMuteEvent( data class MemberMuteEvent(
override val member: Member, override val member: Member,
...@@ -635,6 +620,8 @@ data class MemberMuteEvent( ...@@ -635,6 +620,8 @@ data class MemberMuteEvent(
/** /**
* 群成员被取消禁言事件. 被禁言的成员都不可能是机器人本人 * 群成员被取消禁言事件. 被禁言的成员都不可能是机器人本人
*
* @see BotUnmuteEvent 机器人被取消禁言的事件
*/ */
data class MemberUnmuteEvent( data class MemberUnmuteEvent(
override val member: Member, override val member: Member,
...@@ -655,65 +642,36 @@ data class MemberUnmuteEvent( ...@@ -655,65 +642,36 @@ data class MemberUnmuteEvent(
/** /**
* 好友昵称改变事件. 目前仅支持解析 (来自 PC 端的修改). * 好友昵称改变事件. 目前仅支持解析 (来自 PC 端的修改).
*/ */
@SinceMirai("0.36.0")
data class FriendRemarkChangeEvent( data class FriendRemarkChangeEvent(
override val bot: Bot, override val bot: Bot,
val friend: Friend, override val friend: Friend,
val newName: String val newName: String
) : BotEvent, Packet, AbstractEvent() { ) : FriendEvent, Packet, AbstractEvent()
@PlannedRemoval("1.0.0")
@Deprecated("", level = DeprecationLevel.HIDDEN)
@get:JvmSynthetic
@get:JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
val friendDeprecated: QQ
get() = friend
}
/** /**
* 成功添加了一个新好友的事件 * 成功添加了一个新好友的事件
*/ */
@SinceMirai("0.36.0")
data class FriendAddEvent( data class FriendAddEvent(
/** /**
* 新好友. 已经添加到 [Bot.friends] * 新好友. 已经添加到 [Bot.friends]
*/ */
val friend: Friend override val friend: Friend
) : BotEvent, Packet, AbstractEvent() { ) : FriendEvent, Packet, AbstractEvent() {
override val bot: Bot get() = friend.bot override val bot: Bot get() = friend.bot
@PlannedRemoval("1.0.0")
@Deprecated("", level = DeprecationLevel.HIDDEN)
@get:JvmSynthetic
@get:JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
val friendDeprecated: QQ
get() = friend
} }
/** /**
* 好友已被删除的事件. * 好友已被删除的事件.
*/ */
@SinceMirai("0.36.0")
data class FriendDeleteEvent( data class FriendDeleteEvent(
val friend: Friend override val friend: Friend
) : BotEvent, Packet, AbstractEvent() { ) : FriendEvent, Packet, AbstractEvent() {
override val bot: Bot get() = friend.bot override val bot: Bot get() = friend.bot
@PlannedRemoval("1.0.0")
@Deprecated("", level = DeprecationLevel.HIDDEN)
@get:JvmSynthetic
@get:JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
val friendDeprecated: QQ
get() = friend
} }
/** /**
* 一个账号请求添加机器人为好友的事件 * 一个账号请求添加机器人为好友的事件
*/ */
@SinceMirai("0.35.0")
data class NewFriendRequestEvent( data class NewFriendRequestEvent(
override val bot: Bot, override val bot: Bot,
/** /**
...@@ -725,7 +683,7 @@ data class NewFriendRequestEvent( ...@@ -725,7 +683,7 @@ data class NewFriendRequestEvent(
*/ */
val message: String, val message: String,
/** /**
* 请求人 [QQ.id] * 请求人 [User.id]
*/ */
val fromId: Long, val fromId: Long,
/** /**
......
...@@ -15,8 +15,6 @@ import net.mamoe.mirai.contact.Group ...@@ -15,8 +15,6 @@ import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.Event
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
/** /**
* 有关一个 [Bot] 的事件 * 有关一个 [Bot] 的事件
...@@ -90,11 +88,4 @@ interface FriendEvent : BotEvent { ...@@ -90,11 +88,4 @@ interface FriendEvent : BotEvent {
val friend: Friend val friend: Friend
override val bot: Bot override val bot: Bot
get() = friend.bot get() = friend.bot
@Deprecated("", level = DeprecationLevel.HIDDEN)
@get:JvmSynthetic
@get:JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
val friendDeprecated: net.mamoe.mirai.contact.QQ
get() = friend
} }
\ No newline at end of file
...@@ -12,7 +12,7 @@ package net.mamoe.mirai.event.internal ...@@ -12,7 +12,7 @@ package net.mamoe.mirai.event.internal
import net.mamoe.mirai.event.MessageDsl import net.mamoe.mirai.event.MessageDsl
import net.mamoe.mirai.event.MessageListener import net.mamoe.mirai.event.MessageListener
import net.mamoe.mirai.event.MessageSubscribersBuilder import net.mamoe.mirai.event.MessageSubscribersBuilder
import net.mamoe.mirai.message.ContactMessage import net.mamoe.mirai.message.MessageEvent
/* /*
...@@ -20,13 +20,13 @@ import net.mamoe.mirai.message.ContactMessage ...@@ -20,13 +20,13 @@ import net.mamoe.mirai.message.ContactMessage
*/ */
@MessageDsl @MessageDsl
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.content( internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.content(
filter: M.(String) -> Boolean, filter: M.(String) -> Boolean,
onEvent: MessageListener<M, RR> onEvent: MessageListener<M, RR>
): Ret = ): Ret =
subscriber(filter) { onEvent(this, it) } subscriber(filter) { onEvent(this, it) }
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.endsWithImpl( internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.endsWithImpl(
suffix: String, suffix: String,
removeSuffix: Boolean = true, removeSuffix: Boolean = true,
trim: Boolean = true, trim: Boolean = true,
...@@ -46,7 +46,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, ...@@ -46,7 +46,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
} }
} }
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.startsWithImpl( internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.startsWithImpl(
prefix: String, prefix: String,
removePrefix: Boolean = true, removePrefix: Boolean = true,
trim: Boolean = true, trim: Boolean = true,
...@@ -64,7 +64,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, ...@@ -64,7 +64,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
} }
} }
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAllImpl( internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAllImpl(
sub: Array<out String>, sub: Array<out String>,
ignoreCase: Boolean = false, ignoreCase: Boolean = false,
trim: Boolean = true trim: Boolean = true
...@@ -76,7 +76,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, ...@@ -76,7 +76,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
content { sub.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } } content { sub.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
} }
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAnyImpl( internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAnyImpl(
vararg sub: String, vararg sub: String,
ignoreCase: Boolean = false, ignoreCase: Boolean = false,
trim: Boolean = true trim: Boolean = true
...@@ -86,7 +86,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, ...@@ -86,7 +86,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
content { list.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } } content { list.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
} else content { sub.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } } } else content { sub.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.caseImpl( internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.caseImpl(
equals: String, equals: String,
ignoreCase: Boolean = false, ignoreCase: Boolean = false,
trim: Boolean = true trim: Boolean = true
...@@ -99,7 +99,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, ...@@ -99,7 +99,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
} }
} }
internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsImpl( internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsImpl(
sub: String, sub: String,
ignoreCase: Boolean = false, ignoreCase: Boolean = false,
trim: Boolean = true, trim: Boolean = true,
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
package net.mamoe.mirai.event package net.mamoe.mirai.event
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.mamoe.mirai.utils.SinceMirai
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
...@@ -25,12 +24,13 @@ import kotlin.reflect.KClass ...@@ -25,12 +24,13 @@ import kotlin.reflect.KClass
* @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值 * @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
* *
* @see asyncFromEvent 本函数的异步版本 * @see asyncFromEvent 本函数的异步版本
* @see subscribe 普通地监听一个事件
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
* *
* @throws TimeoutCancellationException 在超时后抛出. * @throws TimeoutCancellationException 在超时后抛出.
* @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常 * @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常
*/ */
@JvmSynthetic @JvmSynthetic
@SinceMirai("0.39.0")
suspend inline fun <reified E : Event, R : Any> syncFromEvent( suspend inline fun <reified E : Event, R : Any> syncFromEvent(
timeoutMillis: Long = -1, timeoutMillis: Long = -1,
crossinline mapper: suspend E.(E) -> R? crossinline mapper: suspend E.(E) -> R?
...@@ -57,10 +57,12 @@ suspend inline fun <reified E : Event, R : Any> syncFromEvent( ...@@ -57,10 +57,12 @@ suspend inline fun <reified E : Event, R : Any> syncFromEvent(
* @return 超时返回 `null`, 否则返回 [mapper] 返回的第一个非 `null` 值. * @return 超时返回 `null`, 否则返回 [mapper] 返回的第一个非 `null` 值.
* *
* @see asyncFromEvent 本函数的异步版本 * @see asyncFromEvent 本函数的异步版本
* @see subscribe 普通地监听一个事件
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
*
* @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常 * @throws Throwable 当 [mapper] 抛出任何异常时, 本函数会抛出该异常
*/ */
@JvmSynthetic @JvmSynthetic
@SinceMirai("0.39.0")
suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull( suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull(
timeoutMillis: Long, timeoutMillis: Long,
crossinline mapper: suspend E.(E) -> R? crossinline mapper: suspend E.(E) -> R?
...@@ -80,10 +82,14 @@ suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull( ...@@ -80,10 +82,14 @@ suspend inline fun <reified E : Event, R : Any> syncFromEventOrNull(
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @param coroutineContext 额外的 [CoroutineContext] * @param coroutineContext 额外的 [CoroutineContext]
* @param mapper 过滤转换器. 返回非 `null` 则代表得到了需要的值. [syncFromEvent] 会返回这个值 * @param mapper 过滤转换器. 返回非 `null` 则代表得到了需要的值. [syncFromEvent] 会返回这个值
*
* @see syncFromEvent
* @see asyncFromEvent
* @see subscribe 普通地监听一个事件
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
*/ */
@JvmSynthetic @JvmSynthetic
@Suppress("DeferredIsResult") @Suppress("DeferredIsResult")
@SinceMirai("0.39.0")
inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNull( inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNull(
timeoutMillis: Long, timeoutMillis: Long,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
...@@ -104,10 +110,14 @@ inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNull( ...@@ -104,10 +110,14 @@ inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEventOrNull(
* @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制 * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
* @param coroutineContext 额外的 [CoroutineContext] * @param coroutineContext 额外的 [CoroutineContext]
* @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值 * @param mapper 过滤转换器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
*
* @see syncFromEvent
* @see asyncFromEventOrNull
* @see subscribe 普通地监听一个事件
* @see nextEvent 挂起当前协程, 并获取下一个事件实例
*/ */
@JvmSynthetic @JvmSynthetic
@Suppress("DeferredIsResult") @Suppress("DeferredIsResult")
@SinceMirai("0.39.0")
inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEvent( inline fun <reified E : Event, R : Any> CoroutineScope.asyncFromEvent(
timeoutMillis: Long = -1, timeoutMillis: Long = -1,
coroutineContext: CoroutineContext = EmptyCoroutineContext, coroutineContext: CoroutineContext = EmptyCoroutineContext,
...@@ -132,9 +142,12 @@ internal suspend inline fun <E : Event, R> syncFromEventImpl( ...@@ -132,9 +142,12 @@ internal suspend inline fun <E : Event, R> syncFromEventImpl(
crossinline mapper: suspend E.(E) -> R? crossinline mapper: suspend E.(E) -> R?
): R = suspendCancellableCoroutine { cont -> ): R = suspendCancellableCoroutine { cont ->
coroutineScope.subscribe(eventClass) { coroutineScope.subscribe(eventClass) {
cont.resumeWith(kotlin.runCatching { try {
mapper.invoke(this, it) ?: return@subscribe ListeningStatus.LISTENING cont.resumeWith(kotlin.runCatching {
}) mapper.invoke(this, it) ?: return@subscribe ListeningStatus.LISTENING
})
} catch (e: Exception) {
}
return@subscribe ListeningStatus.STOPPED return@subscribe ListeningStatus.STOPPED
} }
} }
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment