diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt index f02aec3ab..7899d22db 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt @@ -28,6 +28,8 @@ import net.mamoe.mirai.data.* import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.MessageRecallEvent import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.qqandroid.contact.MemberInfoImpl +import net.mamoe.mirai.qqandroid.contact.QQImpl import net.mamoe.mirai.qqandroid.message.OnlineFriendImageImpl import net.mamoe.mirai.qqandroid.message.OnlineGroupImageImpl import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt similarity index 54% rename from mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt rename to mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt index 5601d77d3..7c55d264d 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt @@ -9,383 +9,34 @@ @file: Suppress("INAPPLICABLE_JVM_NAME") -package net.mamoe.mirai.qqandroid +package net.mamoe.mirai.qqandroid.contact import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull import kotlinx.io.core.Closeable import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.contact.* -import net.mamoe.mirai.data.* +import net.mamoe.mirai.data.GroupInfo +import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.* -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.* +import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.OfflineGroupImage +import net.mamoe.mirai.message.data.asMessageChain +import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.message.MessageSourceFromSendGroup import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper -import net.mamoe.mirai.qqandroid.network.highway.postImage -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.io.toUHexString import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext import kotlin.jvm.JvmSynthetic -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo - -internal inline class FriendInfoImpl( - private val jceFriendInfo: JceFriendInfo -) : FriendInfo { - override val nick: String get() = jceFriendInfo.nick ?: "" - override val uin: Long get() = jceFriendInfo.friendUin -} - -internal class QQImpl( - bot: QQAndroidBot, - override val coroutineContext: CoroutineContext, - override val id: Long, - private val friendInfo: FriendInfo -) : QQ() { - override val bot: QQAndroidBot by bot.unsafeWeakRef() - override val nick: String - get() = friendInfo.nick - - @JvmSynthetic - @Suppress("DuplicatedCode") - override suspend fun sendMessage(message: Message): MessageReceipt { - val event = FriendMessageSendEvent(this, message.asMessageChain()).broadcast() - if (event.isCancelled) { - throw EventCancelledException("cancelled by FriendMessageSendEvent") - } - lateinit var source: MessageSource - bot.network.run { - check( - MessageSvc.PbSendMsg.ToFriend( - bot.client, - id, - event.message - ) { - source = it - }.sendAndExpect() is MessageSvc.PbSendMsg.Response.SUCCESS - ) { "send message failed" } - } - return MessageReceipt(source, this, null) - } - - @JvmSynthetic - @OptIn(MiraiInternalAPI::class) - override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try { - if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) { - throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup") - } - bot.network.run { - val response = LongConn.OffPicUp( - bot.client, Cmd0x352.TryUpImgReq( - srcUin = bot.uin.toInt(), - dstUin = id.toInt(), - fileId = 0, - fileMd5 = image.md5, - fileSize = image.inputSize.toInt(), - fileName = image.md5.toUHexString("") + "." + image.format, - imgOriginal = 1, - imgWidth = image.width, - imgHeight = image.height, - imgType = image.imageType - ) - ).sendAndExpect() - - @Suppress("UNCHECKED_CAST") // bug - return when (response) { - is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage( - filepath = response.resourceId, - md5 = response.imageInfo.fileMd5, - fileLength = response.imageInfo.fileSize.toInt(), - height = response.imageInfo.fileHeight, - width = response.imageInfo.fileWidth, - resourceId = response.resourceId - ).also { - ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast() - } - is LongConn.OffPicUp.Response.RequireUpload -> { - MiraiPlatformUtils.Http.postImage( - "0x6ff0070", - bot.uin, - null, - imageInput = image.input, - inputSize = image.inputSize, - uKeyHex = response.uKey.toUHexString("") - ) - //HighwayHelper.uploadImage( - // client = bot.client, - // serverIp = response.serverIp[0].toIpV4AddressString(), - // serverPort = response.serverPort[0], - // imageInput = image.input, - // inputSize = image.inputSize.toInt(), - // fileMd5 = image.md5, - // uKey = response.uKey, - // commandId = 1 - //) - // 为什么不能 ?? - - return OfflineFriendImage( - filepath = response.resourceId, - md5 = image.md5, - fileLength = image.inputSize.toInt(), - height = image.height, - width = image.width, - resourceId = response.resourceId - ).also { - ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast() - } - } - is LongConn.OffPicUp.Response.Failed -> { - ImageUploadEvent.Failed(this@QQImpl, image, -1, response.message).broadcast() - error(response.message) - } - } - } - } finally { - (image.input as? Closeable)?.close() - (image.input as? Closeable)?.close() - } - - override fun hashCode(): Int { - var result = bot.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun equals(other: Any?): Boolean { - @Suppress("DuplicatedCode") - if (this === other) return true - if (other !is Contact) return false - if (this::class != other::class) return false - return this.id == other.id && this.bot == other.bot - } - - @MiraiExperimentalAPI - override suspend fun queryProfile(): Profile { - TODO("not implemented") - } - - @MiraiExperimentalAPI - override suspend fun queryPreviousNameList(): PreviousNameList { - TODO("not implemented") - } - - @MiraiExperimentalAPI - override suspend fun queryRemark(): FriendNameRemark { - TODO("not implemented") - } - - override fun toString(): String = "QQ($id)" -} - - -@Suppress("MemberVisibilityCanBePrivate") -internal class MemberImpl( - val qq: QQImpl, // 不要 WeakRef - group: GroupImpl, - override val coroutineContext: CoroutineContext, - memberInfo: MemberInfo -) : Member() { - override val group: GroupImpl by group.unsafeWeakRef() - - // region QQ delegate - override val id: Long = qq.id - override val nick: String = qq.nick - - @MiraiExperimentalAPI - override suspend fun queryProfile(): Profile = qq.queryProfile() - - @MiraiExperimentalAPI - override suspend fun queryPreviousNameList(): PreviousNameList = qq.queryPreviousNameList() - - @MiraiExperimentalAPI - override suspend fun queryRemark(): FriendNameRemark = qq.queryRemark() - - @JvmSynthetic - @Suppress("DuplicatedCode") - override suspend fun sendMessage(message: Message): MessageReceipt { - val event = FriendMessageSendEvent(this, message.asMessageChain()).broadcast() - if (event.isCancelled) { - throw EventCancelledException("cancelled by FriendMessageSendEvent") - } - lateinit var source: MessageSource - bot.network.run { - check( - MessageSvc.PbSendMsg.ToFriend( - bot.client, - id, - event.message - ) { - source = it - }.sendAndExpect() is MessageSvc.PbSendMsg.Response.SUCCESS - ) { "send message failed" } - } - return MessageReceipt(source, this, null) - } - - @JvmSynthetic - override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image) - // endregion - - override var permission: MemberPermission = memberInfo.permission - - @Suppress("PropertyName") - internal var _nameCard: String = memberInfo.nameCard - - @Suppress("PropertyName") - internal var _specialTitle: String = memberInfo.specialTitle - - @Suppress("PropertyName") - var _muteTimestamp: Int = memberInfo.muteTimestamp - - override val muteTimeRemaining: Int = - if (_muteTimestamp == 0 || _muteTimestamp == 0xFFFFFFFF.toInt()) { - 0 - } else { - _muteTimestamp - currentTimeSeconds.toInt() - bot.client.timeDifference.toInt() - } - - override var nameCard: String - get() = _nameCard - set(newValue) { - group.checkBotPermissionOperator() - if (_nameCard != newValue) { - val oldValue = _nameCard - _nameCard = newValue - launch { - bot.network.run { - TroopManagement.EditGroupNametag( - bot.client, - this@MemberImpl, - newValue - ).sendWithoutExpect() - } - MemberCardChangeEvent(oldValue, newValue, this@MemberImpl, null).broadcast() - } - } - } - - override var specialTitle: String - get() = _specialTitle - set(newValue) { - group.checkBotPermission(MemberPermission.OWNER) - if (_specialTitle != newValue) { - val oldValue = _specialTitle - _specialTitle = newValue - launch { - bot.network.run { - TroopManagement.EditSpecialTitle( - bot.client, - this@MemberImpl, - newValue - ).sendWithoutExpect() - } - MemberSpecialTitleChangeEvent(oldValue, newValue, this@MemberImpl, null).broadcast() - } - } - } - - override val bot: QQAndroidBot get() = qq.bot - - @JvmSynthetic - override suspend fun mute(durationSeconds: Int) { - if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) { - throw PermissionDeniedException() - } - - bot.network.run { - TroopManagement.Mute( - client = bot.client, - groupCode = group.id, - memberUin = this@MemberImpl.id, - timeInSecond = durationSeconds - ).sendAndExpect() - } - - @Suppress("RemoveRedundantQualifierName") // or unresolved reference - net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast() - } - - @JvmSynthetic - override suspend fun unmute() { - if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) { - throw PermissionDeniedException() - } - - bot.network.run { - TroopManagement.Mute( - client = bot.client, - groupCode = group.id, - memberUin = this@MemberImpl.id, - timeInSecond = 0 - ).sendAndExpect() - } - - @Suppress("RemoveRedundantQualifierName") // or unresolved reference - net.mamoe.mirai.event.events.MemberUnmuteEvent(this@MemberImpl, null).broadcast() - } - - @JvmSynthetic - override suspend fun kick(message: String) { - if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) { - throw PermissionDeniedException() - } - - bot.network.run { - TroopManagement.Kick( - client = bot.client, - member = this@MemberImpl, - message = message - ).sendAndExpect().success.also { - MemberLeaveEvent.Kick(this@MemberImpl, null).broadcast() - } - } - } - - override fun hashCode(): Int { - var result = bot.hashCode() - result = 31 * result + id.hashCode() - return result - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is Contact) return false - if (this::class != other::class) return false - return this.id == other.id && this.bot == other.bot - } - - override fun toString(): String { - return "Member($id)" - } -} - -internal class MemberInfoImpl( - jceInfo: StTroopMemberInfo, - groupOwnerId: Long -) : MemberInfo { - override val uin: Long = jceInfo.memberUin - override val nameCard: String = jceInfo.sName ?: "" - override val nick: String = jceInfo.nick - override val permission: MemberPermission = when { - jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER - jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR - else -> MemberPermission.MEMBER - } - override val specialTitle: String = jceInfo.sSpecialTitle ?: "" - override val muteTimestamp: Int = jceInfo.dwShutupTimestap?.toInt() ?: 0 -} @OptIn(ExperimentalContracts::class) internal fun GroupImpl.Companion.checkIsInstance(expression: Boolean) { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt new file mode 100644 index 000000000..92c9a7da7 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt @@ -0,0 +1,229 @@ +/* + * 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.contact + +import kotlinx.coroutines.launch +import net.mamoe.mirai.contact.* +import net.mamoe.mirai.data.FriendNameRemark +import net.mamoe.mirai.data.MemberInfo +import net.mamoe.mirai.data.PreviousNameList +import net.mamoe.mirai.data.Profile +import net.mamoe.mirai.event.broadcast +import net.mamoe.mirai.event.events.* +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.message.data.OfflineFriendImage +import net.mamoe.mirai.message.data.asMessageChain +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc +import net.mamoe.mirai.utils.* +import kotlin.coroutines.CoroutineContext +import kotlin.jvm.JvmSynthetic + + +@Suppress("MemberVisibilityCanBePrivate") +internal class MemberImpl( + val qq: QQImpl, // 不要 WeakRef + group: GroupImpl, + override val coroutineContext: CoroutineContext, + memberInfo: MemberInfo +) : Member() { + override val group: GroupImpl by group.unsafeWeakRef() + + // region QQ delegate + override val id: Long = qq.id + override val nick: String = qq.nick + + @MiraiExperimentalAPI + override suspend fun queryProfile(): Profile = qq.queryProfile() + + @MiraiExperimentalAPI + override suspend fun queryPreviousNameList(): PreviousNameList = qq.queryPreviousNameList() + + @MiraiExperimentalAPI + override suspend fun queryRemark(): FriendNameRemark = qq.queryRemark() + + @JvmSynthetic + @Suppress("DuplicatedCode") + override suspend fun sendMessage(message: Message): MessageReceipt { + val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast() + if (event.isCancelled) { + throw EventCancelledException("cancelled by FriendMessageSendEvent") + } + lateinit var source: MessageSource + bot.network.run { + check( + MessageSvc.PbSendMsg.ToFriend( + bot.client, + id, + event.message + ) { + source = it + }.sendAndExpect() is MessageSvc.PbSendMsg.Response.SUCCESS + ) { "send message failed" } + } + return MessageReceipt(source, this, null) + } + + @JvmSynthetic + override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image) + // endregion + + override var permission: MemberPermission = memberInfo.permission + + @Suppress("PropertyName") + internal var _nameCard: String = memberInfo.nameCard + + @Suppress("PropertyName") + internal var _specialTitle: String = memberInfo.specialTitle + + @Suppress("PropertyName") + var _muteTimestamp: Int = memberInfo.muteTimestamp + + override val muteTimeRemaining: Int = + if (_muteTimestamp == 0 || _muteTimestamp == 0xFFFFFFFF.toInt()) { + 0 + } else { + _muteTimestamp - currentTimeSeconds.toInt() - bot.client.timeDifference.toInt() + } + + override var nameCard: String + get() = _nameCard + set(newValue) { + group.checkBotPermissionOperator() + if (_nameCard != newValue) { + val oldValue = _nameCard + _nameCard = newValue + launch { + bot.network.run { + TroopManagement.EditGroupNametag( + bot.client, + this@MemberImpl, + newValue + ).sendWithoutExpect() + } + MemberCardChangeEvent(oldValue, newValue, this@MemberImpl, null).broadcast() + } + } + } + + override var specialTitle: String + get() = _specialTitle + set(newValue) { + group.checkBotPermission(MemberPermission.OWNER) + if (_specialTitle != newValue) { + val oldValue = _specialTitle + _specialTitle = newValue + launch { + bot.network.run { + TroopManagement.EditSpecialTitle( + bot.client, + this@MemberImpl, + newValue + ).sendWithoutExpect() + } + MemberSpecialTitleChangeEvent(oldValue, newValue, this@MemberImpl, null).broadcast() + } + } + } + + override val bot: QQAndroidBot get() = qq.bot + + @JvmSynthetic + override suspend fun mute(durationSeconds: Int) { + if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) { + throw PermissionDeniedException() + } + + bot.network.run { + TroopManagement.Mute( + client = bot.client, + groupCode = group.id, + memberUin = this@MemberImpl.id, + timeInSecond = durationSeconds + ).sendAndExpect() + } + + @Suppress("RemoveRedundantQualifierName") // or unresolved reference + net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast() + } + + @JvmSynthetic + override suspend fun unmute() { + if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) { + throw PermissionDeniedException() + } + + bot.network.run { + TroopManagement.Mute( + client = bot.client, + groupCode = group.id, + memberUin = this@MemberImpl.id, + timeInSecond = 0 + ).sendAndExpect() + } + + @Suppress("RemoveRedundantQualifierName") // or unresolved reference + net.mamoe.mirai.event.events.MemberUnmuteEvent(this@MemberImpl, null).broadcast() + } + + @JvmSynthetic + override suspend fun kick(message: String) { + if (group.botPermission != MemberPermission.OWNER && (!group.botPermission.isOperator() || this.isOperator())) { + throw PermissionDeniedException() + } + + bot.network.run { + TroopManagement.Kick( + client = bot.client, + member = this@MemberImpl, + message = message + ).sendAndExpect().success.also { + MemberLeaveEvent.Kick(this@MemberImpl, null).broadcast() + } + } + } + + override fun hashCode(): Int { + var result = bot.hashCode() + result = 31 * result + id.hashCode() + return result + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Contact) return false + if (this::class != other::class) return false + return this.id == other.id && this.bot == other.bot + } + + override fun toString(): String { + return "Member($id)" + } +} + +internal class MemberInfoImpl( + jceInfo: StTroopMemberInfo, + groupOwnerId: Long +) : MemberInfo { + override val uin: Long = jceInfo.memberUin + override val nameCard: String = jceInfo.sName ?: "" + override val nick: String = jceInfo.nick + override val permission: MemberPermission = when { + jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER + jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR + else -> MemberPermission.MEMBER + } + override val specialTitle: String = jceInfo.sSpecialTitle ?: "" + override val muteTimestamp: Int = jceInfo.dwShutupTimestap?.toInt() ?: 0 +} diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt new file mode 100644 index 000000000..0b4e90556 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt @@ -0,0 +1,177 @@ +package net.mamoe.mirai.qqandroid.contact + +import kotlinx.io.core.Closeable +import net.mamoe.mirai.contact.Contact +import net.mamoe.mirai.contact.QQ +import net.mamoe.mirai.data.FriendInfo +import net.mamoe.mirai.data.FriendNameRemark +import net.mamoe.mirai.data.PreviousNameList +import net.mamoe.mirai.data.Profile +import net.mamoe.mirai.event.broadcast +import net.mamoe.mirai.event.events.BeforeImageUploadEvent +import net.mamoe.mirai.event.events.EventCancelledException +import net.mamoe.mirai.event.events.ImageUploadEvent +import net.mamoe.mirai.event.events.MessageSendEvent +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.message.data.OfflineFriendImage +import net.mamoe.mirai.message.data.asMessageChain +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.network.highway.postImage +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352 +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc +import net.mamoe.mirai.utils.* +import net.mamoe.mirai.utils.io.toUHexString +import kotlin.coroutines.CoroutineContext +import kotlin.jvm.JvmSynthetic + +internal inline class FriendInfoImpl( + private val jceFriendInfo: net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo +) : FriendInfo { + override val nick: String get() = jceFriendInfo.nick ?: "" + override val uin: Long get() = jceFriendInfo.friendUin +} + +internal class QQImpl( + bot: QQAndroidBot, + override val coroutineContext: CoroutineContext, + override val id: Long, + private val friendInfo: FriendInfo +) : QQ() { + override val bot: QQAndroidBot by bot.unsafeWeakRef() + override val nick: String + get() = friendInfo.nick + + @JvmSynthetic + @Suppress("DuplicatedCode") + override suspend fun sendMessage(message: Message): MessageReceipt { + val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast() + if (event.isCancelled) { + throw EventCancelledException("cancelled by FriendMessageSendEvent") + } + lateinit var source: MessageSource + bot.network.run { + check( + MessageSvc.PbSendMsg.ToFriend( + bot.client, + id, + event.message + ) { + source = it + } + .sendAndExpect() is MessageSvc.PbSendMsg.Response.SUCCESS + ) { "send message failed" } + } + return MessageReceipt(source, this, null) + } + + @JvmSynthetic + @OptIn(MiraiInternalAPI::class) + override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try { + if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) { + throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup") + } + bot.network.run { + val response = LongConn.OffPicUp( + bot.client, Cmd0x352.TryUpImgReq( + srcUin = bot.uin.toInt(), + dstUin = id.toInt(), + fileId = 0, + fileMd5 = image.md5, + fileSize = image.inputSize.toInt(), + fileName = image.md5.toUHexString("") + "." + image.format, + imgOriginal = 1, + imgWidth = image.width, + imgHeight = image.height, + imgType = image.imageType + ) + ).sendAndExpect() + + @Suppress("UNCHECKED_CAST") // bug + return when (response) { + is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage( + filepath = response.resourceId, + md5 = response.imageInfo.fileMd5, + fileLength = response.imageInfo.fileSize.toInt(), + height = response.imageInfo.fileHeight, + width = response.imageInfo.fileWidth, + resourceId = response.resourceId + ).also { + ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast() + } + is LongConn.OffPicUp.Response.RequireUpload -> { + MiraiPlatformUtils.Http.postImage( + "0x6ff0070", + bot.uin, + null, + imageInput = image.input, + inputSize = image.inputSize, + uKeyHex = response.uKey.toUHexString("") + ) + //HighwayHelper.uploadImage( + // client = bot.client, + // serverIp = response.serverIp[0].toIpV4AddressString(), + // serverPort = response.serverPort[0], + // imageInput = image.input, + // inputSize = image.inputSize.toInt(), + // fileMd5 = image.md5, + // uKey = response.uKey, + // commandId = 1 + //) + // 为什么不能 ?? + + return OfflineFriendImage( + filepath = response.resourceId, + md5 = image.md5, + fileLength = image.inputSize.toInt(), + height = image.height, + width = image.width, + resourceId = response.resourceId + ).also { + ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast() + } + } + is LongConn.OffPicUp.Response.Failed -> { + ImageUploadEvent.Failed(this@QQImpl, image, -1, response.message).broadcast() + error(response.message) + } + } + } + } finally { + (image.input as? Closeable)?.close() + (image.input as? Closeable)?.close() + } + + override fun hashCode(): Int { + var result = bot.hashCode() + result = 31 * result + id.hashCode() + return result + } + + override fun equals(other: Any?): Boolean { + @Suppress("DuplicatedCode") + if (this === other) return true + if (other !is Contact) return false + if (this::class != other::class) return false + return this.id == other.id && this.bot == other.bot + } + + @MiraiExperimentalAPI + override suspend fun queryProfile(): Profile { + TODO("not implemented") + } + + @MiraiExperimentalAPI + override suspend fun queryPreviousNameList(): PreviousNameList { + TODO("not implemented") + } + + @MiraiExperimentalAPI + override suspend fun queryRemark(): FriendNameRemark { + TODO("not implemented") + } + + override fun toString(): String = "QQ($id)" +} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index 14c2c42c8..69ef95653 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -25,10 +25,10 @@ import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.BotOnlineEvent import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.WrongPasswordException -import net.mamoe.mirai.qqandroid.FriendInfoImpl -import net.mamoe.mirai.qqandroid.GroupImpl import net.mamoe.mirai.qqandroid.QQAndroidBot -import net.mamoe.mirai.qqandroid.QQImpl +import net.mamoe.mirai.qqandroid.contact.FriendInfoImpl +import net.mamoe.mirai.qqandroid.contact.GroupImpl +import net.mamoe.mirai.qqandroid.contact.QQImpl import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.* @@ -264,32 +264,32 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler bot.groups.delegate.addLast( @Suppress("DuplicatedCode") (GroupImpl( - bot = bot, - coroutineContext = bot.coroutineContext, - id = troopNum.groupCode, - groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply { - this as GroupInfoImpl + bot = bot, + coroutineContext = bot.coroutineContext, + id = troopNum.groupCode, + groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply { + this as GroupInfoImpl - if (this.delegate.groupName == null) { - this.delegate.groupName = troopNum.groupName - } + if (this.delegate.groupName == null) { + this.delegate.groupName = troopNum.groupName + } - if (this.delegate.groupMemo == null) { - this.delegate.groupMemo = troopNum.groupMemo - } + if (this.delegate.groupMemo == null) { + this.delegate.groupMemo = troopNum.groupMemo + } - if (this.delegate.groupUin == null) { - this.delegate.groupUin = troopNum.groupUin - } + if (this.delegate.groupUin == null) { + this.delegate.groupUin = troopNum.groupUin + } - this.delegate.groupCode = troopNum.groupCode - }, - members = bot._lowLevelQueryGroupMemberList( - troopNum.groupUin, - troopNum.groupCode, - troopNum.dwGroupOwnerUin - ) - )) + this.delegate.groupCode = troopNum.groupCode + }, + members = bot._lowLevelQueryGroupMemberList( + troopNum.groupUin, + troopNum.groupCode, + troopNum.dwGroupOwnerUin + ) + )) ) }?.let { logger.error { "群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试" } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt index 973dd853e..cda2866e2 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt @@ -24,7 +24,7 @@ import net.mamoe.mirai.event.events.MemberJoinEvent import net.mamoe.mirai.getFriendOrNull import net.mamoe.mirai.message.FriendMessage import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.qqandroid.GroupImpl +import net.mamoe.mirai.qqandroid.contact.GroupImpl import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt index 97e50fb47..56f30cc37 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt @@ -19,10 +19,10 @@ import net.mamoe.mirai.data.Packet import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.events.* import net.mamoe.mirai.message.GroupMessage -import net.mamoe.mirai.qqandroid.GroupImpl -import net.mamoe.mirai.qqandroid.MemberImpl +import net.mamoe.mirai.qqandroid.contact.GroupImpl +import net.mamoe.mirai.qqandroid.contact.MemberImpl import net.mamoe.mirai.qqandroid.QQAndroidBot -import net.mamoe.mirai.qqandroid.checkIsInstance +import net.mamoe.mirai.qqandroid.contact.checkIsInstance import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.message.toMessageChain