Commit a63af806 authored by Him188's avatar Him188

Deprecate `QQ`

Introduce `Friend` extending `User`
Introduce `User` acting as the superclass of `Friend` and `Member`

Add extensions `Member.asFriend`, etc.
Remove specialized `Contact.hashCode` and `Contact.equals`, inherit from `kotlin.Any`
Specify `toString` behavior in abstract `Contact` classes, instead of in implementation classes.

Add virtual member `Contact.sendMessage(String)`

Migrate the project from using `QQ` to `Friend`

Remove multiplatform Contact class structure, keep them common.

Remove extension `Long.at` in `GroupMessage`, `QQ.at` in `ContactMessage`.
Add `At.asMember()` in `GroupMessage`.

Make `calculateGroupUinByGroupCode`, `calculateGroupCodeByGroupUin` static on JVM.
parent 3efeba19
...@@ -38,8 +38,8 @@ import net.mamoe.mirai.event.events.NewFriendRequestEvent ...@@ -38,8 +38,8 @@ import net.mamoe.mirai.event.events.NewFriendRequestEvent
import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.network.LoginFailedException import net.mamoe.mirai.network.LoginFailedException
import net.mamoe.mirai.qqandroid.contact.FriendImpl
import net.mamoe.mirai.qqandroid.contact.MemberInfoImpl import net.mamoe.mirai.qqandroid.contact.MemberInfoImpl
import net.mamoe.mirai.qqandroid.contact.QQImpl
import net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl import net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl
import net.mamoe.mirai.qqandroid.message.* import net.mamoe.mirai.qqandroid.message.*
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
...@@ -89,7 +89,7 @@ internal class QQAndroidBot constructor( ...@@ -89,7 +89,7 @@ internal class QQAndroidBot constructor(
event, event,
accept = true accept = true
).sendWithoutExpect() ).sendWithoutExpect()
bot.friends.delegate.addLast(bot._lowLevelNewQQ(object : FriendInfo { bot.friends.delegate.addLast(bot._lowLevelNewFriend(object : FriendInfo {
override val uin: Long get() = event.fromId override val uin: Long get() = event.fromId
override val nick: String get() = event.fromNick override val nick: String get() = event.fromNick
})) }))
...@@ -150,7 +150,6 @@ internal class QQAndroidBot constructor( ...@@ -150,7 +150,6 @@ internal class QQAndroidBot constructor(
} }
override suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) { override suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) {
check(event.responded.compareAndSet(false, true)) { check(event.responded.compareAndSet(false, true)) {
"the request $this has already been responded" "the request $this has already been responded"
} }
...@@ -188,15 +187,15 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -188,15 +187,15 @@ internal abstract class QQAndroidBotBase constructor(
val json = Json(JsonConfiguration(ignoreUnknownKeys = true, encodeDefaults = true)) val json = Json(JsonConfiguration(ignoreUnknownKeys = true, encodeDefaults = true))
} }
override val friends: ContactList<QQ> = ContactList(LockFreeLinkedList()) override val friends: ContactList<Friend> = ContactList(LockFreeLinkedList())
override val nick: String get() = selfInfo.nick override val nick: String get() = selfInfo.nick
internal lateinit var selfInfo: JceFriendInfo internal lateinit var selfInfo: JceFriendInfo
override val selfQQ: QQ by lazy { override val selfQQ: Friend by lazy {
@OptIn(LowLevelAPI::class) @OptIn(LowLevelAPI::class)
_lowLevelNewQQ(object : FriendInfo { _lowLevelNewFriend(object : FriendInfo {
override val uin: Long get() = this@QQAndroidBotBase.id override val uin: Long get() = this@QQAndroidBotBase.id
override val nick: String get() = this@QQAndroidBotBase.nick override val nick: String get() = this@QQAndroidBotBase.nick
}) })
...@@ -214,10 +213,10 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -214,10 +213,10 @@ internal abstract class QQAndroidBotBase constructor(
} }
@LowLevelAPI @LowLevelAPI
override fun _lowLevelNewQQ(friendInfo: FriendInfo): QQ { override fun _lowLevelNewFriend(friendInfo: FriendInfo): Friend {
return QQImpl( return FriendImpl(
this as QQAndroidBot, this as QQAndroidBot,
coroutineContext + CoroutineName("QQ(${friendInfo.uin}"), coroutineContext + CoroutineName("Friend(${friendInfo.uin}"),
friendInfo.uin, friendInfo.uin,
friendInfo friendInfo
) )
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
*/ */
@file:OptIn(MiraiInternalAPI::class, LowLevelAPI::class) @file:OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
@file:Suppress("EXPERIMENTAL_API_USAGE", "DEPRECATION_ERROR") @file:Suppress("EXPERIMENTAL_API_USAGE", "DEPRECATION_ERROR", "NOTHING_TO_INLINE")
package net.mamoe.mirai.qqandroid.contact package net.mamoe.mirai.qqandroid.contact
...@@ -16,12 +16,8 @@ import kotlinx.atomicfu.AtomicInt ...@@ -16,12 +16,8 @@ import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.io.core.Closeable import kotlinx.io.core.Closeable
import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.FriendInfo 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.broadcast
import net.mamoe.mirai.event.events.BeforeImageUploadEvent import net.mamoe.mirai.event.events.BeforeImageUploadEvent
import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.EventCancelledException
...@@ -35,7 +31,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352 ...@@ -35,7 +31,10 @@ 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.image.LongConn
import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import net.mamoe.mirai.qqandroid.utils.toUHexString import net.mamoe.mirai.qqandroid.utils.toUHexString
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.unsafeWeakRef
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
...@@ -49,20 +48,20 @@ internal inline class FriendInfoImpl( ...@@ -49,20 +48,20 @@ internal inline class FriendInfoImpl(
} }
@OptIn(ExperimentalContracts::class) @OptIn(ExperimentalContracts::class)
internal fun QQ.checkIsQQImpl(): QQImpl { internal inline fun Friend.checkIsFriendImpl(): FriendImpl {
contract { contract {
returns() implies (this@checkIsQQImpl is QQImpl) returns() implies (this@checkIsFriendImpl is FriendImpl)
} }
check(this is QQImpl) { "A QQ instance is not instance of QQImpl. Don't interlace two protocol implementations together!" } check(this is FriendImpl) { "A Friend instance is not instance of FriendImpl. Don't interlace two protocol implementations together!" }
return this return this
} }
internal class QQImpl( internal class FriendImpl(
bot: QQAndroidBot, bot: QQAndroidBot,
override val coroutineContext: CoroutineContext, override val coroutineContext: CoroutineContext,
override val id: Long, override val id: Long,
private val friendInfo: FriendInfo private val friendInfo: FriendInfo
) : QQ() { ) : Friend() {
@Suppress("unused") // bug @Suppress("unused") // bug
val lastMessageSequence: AtomicInt = atomic(-1) val lastMessageSequence: AtomicInt = atomic(-1)
...@@ -72,7 +71,7 @@ internal class QQImpl( ...@@ -72,7 +71,7 @@ internal class QQImpl(
@JvmSynthetic @JvmSynthetic
@Suppress("DuplicatedCode") @Suppress("DuplicatedCode")
override suspend fun sendMessage(message: Message): MessageReceipt<QQ> { override suspend fun sendMessage(message: Message): MessageReceipt<Friend> {
return sendMessageImpl(message).also { return sendMessageImpl(message).also {
logMessageSent(message) logMessageSent(message)
} }
...@@ -110,7 +109,7 @@ internal class QQImpl( ...@@ -110,7 +109,7 @@ internal class QQImpl(
width = response.imageInfo.fileWidth, width = response.imageInfo.fileWidth,
resourceId = response.resourceId resourceId = response.resourceId
).also { ).also {
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast() ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
} }
is LongConn.OffPicUp.Response.RequireUpload -> { is LongConn.OffPicUp.Response.RequireUpload -> {
MiraiPlatformUtils.Http.postImage( MiraiPlatformUtils.Http.postImage(
...@@ -141,11 +140,11 @@ internal class QQImpl( ...@@ -141,11 +140,11 @@ internal class QQImpl(
width = image.width, width = image.width,
resourceId = response.resourceId resourceId = response.resourceId
).also { ).also {
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast() ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
} }
} }
is LongConn.OffPicUp.Response.Failed -> { is LongConn.OffPicUp.Response.Failed -> {
ImageUploadEvent.Failed(this@QQImpl, image, -1, response.message).broadcast() ImageUploadEvent.Failed(this@FriendImpl, image, -1, response.message).broadcast()
error(response.message) error(response.message)
} }
} }
...@@ -153,35 +152,4 @@ internal class QQImpl( ...@@ -153,35 +152,4 @@ internal class QQImpl(
} finally { } 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
...@@ -250,7 +250,7 @@ internal class GroupImpl( ...@@ -250,7 +250,7 @@ internal class GroupImpl(
override fun newMember(memberInfo: MemberInfo): Member { override fun newMember(memberInfo: MemberInfo): Member {
return MemberImpl( return MemberImpl(
@OptIn(LowLevelAPI::class) @OptIn(LowLevelAPI::class)
bot._lowLevelNewQQ(memberInfo) as QQImpl, bot._lowLevelNewFriend(memberInfo) as FriendImpl,
this, this,
this.coroutineContext, this.coroutineContext,
memberInfo memberInfo
...@@ -476,23 +476,5 @@ internal class GroupImpl( ...@@ -476,23 +476,5 @@ internal class GroupImpl(
(image.input as? Closeable)?.close() (image.input as? Closeable)?.close()
} }
override fun toString(): String { override fun toString(): String = "Group($id)"
return "Group($id)"
}
override fun hashCode(): Int {
var result = bot.hashCode()
result = 31 * result + id.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
@Suppress("DuplicatedCode", "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
}
} }
\ No newline at end of file
...@@ -16,10 +16,7 @@ import kotlinx.atomicfu.atomic ...@@ -16,10 +16,7 @@ import kotlinx.atomicfu.atomic
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.data.MemberInfo 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.broadcast
import net.mamoe.mirai.event.events.MemberCardChangeEvent import net.mamoe.mirai.event.events.MemberCardChangeEvent
import net.mamoe.mirai.event.events.MemberLeaveEvent import net.mamoe.mirai.event.events.MemberLeaveEvent
...@@ -42,7 +39,7 @@ import kotlin.jvm.JvmSynthetic ...@@ -42,7 +39,7 @@ import kotlin.jvm.JvmSynthetic
@OptIn(LowLevelAPI::class) @OptIn(LowLevelAPI::class)
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
internal class MemberImpl constructor( internal class MemberImpl constructor(
val qq: QQImpl, // 不要 WeakRef val qq: FriendImpl, // 不要 WeakRef
group: GroupImpl, group: GroupImpl,
override val coroutineContext: CoroutineContext, override val coroutineContext: CoroutineContext,
memberInfo: MemberInfo memberInfo: MemberInfo
...@@ -52,22 +49,10 @@ internal class MemberImpl constructor( ...@@ -52,22 +49,10 @@ internal class MemberImpl constructor(
@Suppress("unused") // false positive @Suppress("unused") // false positive
val lastMessageSequence: AtomicInt = atomic(-1) val lastMessageSequence: AtomicInt = atomic(-1)
// region QQ delegate
override val id: Long = qq.id override val id: Long = qq.id
override val nick: String = qq.nick 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()
@OptIn(MiraiInternalAPI::class)
@JvmSynthetic @JvmSynthetic
@Suppress("DuplicatedCode", "UNCHECKED_CAST")
override suspend fun sendMessage(message: Message): MessageReceipt<Member> { override suspend fun sendMessage(message: Message): MessageReceipt<Member> {
return sendMessageImpl(message).also { return sendMessageImpl(message).also {
logMessageSent(message) logMessageSent(message)
...@@ -92,7 +77,6 @@ internal class MemberImpl constructor( ...@@ -92,7 +77,6 @@ internal class MemberImpl constructor(
@JvmSynthetic @JvmSynthetic
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image) override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image)
// endregion
override var permission: MemberPermission = memberInfo.permission override var permission: MemberPermission = memberInfo.permission
...@@ -217,23 +201,6 @@ internal class MemberImpl constructor( ...@@ -217,23 +201,6 @@ internal class MemberImpl constructor(
MemberLeaveEvent.Kick(this@MemberImpl, null).broadcast() 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)"
}
} }
@OptIn(ExperimentalContracts::class) @OptIn(ExperimentalContracts::class)
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
package net.mamoe.mirai.qqandroid.contact package net.mamoe.mirai.qqandroid.contact
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.MessageSendEvent import net.mamoe.mirai.event.events.MessageSendEvent
...@@ -31,7 +31,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI ...@@ -31,7 +31,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.verbose import net.mamoe.mirai.utils.verbose
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
internal suspend fun QQ.sendMessageImpl(message: Message): MessageReceipt<QQ> { internal suspend fun Friend.sendMessageImpl(message: Message): MessageReceipt<Friend> {
val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast() val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast()
if (event.isCancelled) { if (event.isCancelled) {
throw EventCancelledException("cancelled by FriendMessageSendEvent") throw EventCancelledException("cancelled by FriendMessageSendEvent")
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
package net.mamoe.mirai.qqandroid.message package net.mamoe.mirai.qqandroid.message
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.internal.MiraiAtomicBoolean import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
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
...@@ -57,7 +57,7 @@ internal class MessageSourceFromFriendImpl( ...@@ -57,7 +57,7 @@ internal class MessageSourceFromFriendImpl(
override val random: Int get() = msg.msgBody.richText.attr!!.random override val random: Int get() = msg.msgBody.richText.attr!!.random
override val time: Int get() = msg.msgHead.msgTime override val time: Int get() = msg.msgHead.msgTime
override val originalMessage: MessageChain by lazy { msg.toMessageChain(bot, 0, false) } override val originalMessage: MessageChain by lazy { msg.toMessageChain(bot, 0, false) }
override val sender: QQ get() = bot.getFriend(msg.msgHead.fromUin) override val sender: Friend get() = bot.getFriend(msg.msgHead.fromUin)
private val jceData by lazy { msg.toJceDataFriendOrTemp(random) } private val jceData by lazy { msg.toJceDataFriendOrTemp(random) }
......
...@@ -14,9 +14,9 @@ import kotlinx.coroutines.CoroutineScope ...@@ -14,9 +14,9 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.asyncFromEventOrNull import net.mamoe.mirai.event.asyncFromEventOrNull
import net.mamoe.mirai.event.internal.MiraiAtomicBoolean import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
...@@ -74,7 +74,7 @@ internal class MessageSourceToFriendImpl( ...@@ -74,7 +74,7 @@ internal class MessageSourceToFriendImpl(
override val time: Int, override val time: Int,
override val originalMessage: MessageChain, override val originalMessage: MessageChain,
override val sender: Bot, override val sender: Bot,
override val target: QQ override val target: Friend
) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal { ) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal {
override val bot: Bot override val bot: Bot
get() = sender get() = sender
......
...@@ -227,7 +227,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -227,7 +227,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
data.friendList.forEach { data.friendList.forEach {
// atomic // atomic
bot.friends.delegate.addLast( bot.friends.delegate.addLast(
QQImpl(bot, bot.coroutineContext, it.friendUin, FriendInfoImpl(it)) FriendImpl(bot, bot.coroutineContext, it.friendUin, FriendInfoImpl(it))
).also { currentFriendCount++ } ).also { currentFriendCount++ }
} }
logger.verbose { "正在加载好友列表 ${currentFriendCount}/${totalFriendCount}" } logger.verbose { "正在加载好友列表 ${currentFriendCount}/${totalFriendCount}" }
......
...@@ -18,10 +18,10 @@ import kotlinx.coroutines.flow.* ...@@ -18,10 +18,10 @@ import kotlinx.coroutines.flow.*
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.events.BotJoinGroupEvent import net.mamoe.mirai.event.events.BotJoinGroupEvent
...@@ -33,8 +33,8 @@ import net.mamoe.mirai.message.TempMessage ...@@ -33,8 +33,8 @@ import net.mamoe.mirai.message.TempMessage
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.contact.GroupImpl import net.mamoe.mirai.qqandroid.contact.GroupImpl
import net.mamoe.mirai.qqandroid.contact.checkIsFriendImpl
import net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl import net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl
import net.mamoe.mirai.qqandroid.contact.checkIsQQImpl
import net.mamoe.mirai.qqandroid.message.* import net.mamoe.mirai.qqandroid.message.*
import net.mamoe.mirai.qqandroid.network.MultiPacketByIterable import net.mamoe.mirai.qqandroid.network.MultiPacketByIterable
import net.mamoe.mirai.qqandroid.network.Packet import net.mamoe.mirai.qqandroid.network.Packet
...@@ -53,10 +53,8 @@ import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf ...@@ -53,10 +53,8 @@ import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket import net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.MiraiInternalAPI import kotlin.collections.firstOrNull
import net.mamoe.mirai.utils.currentTimeSeconds
import net.mamoe.mirai.utils.debug
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.random.Random import kotlin.random.Random
...@@ -191,7 +189,7 @@ internal class MessageSvc { ...@@ -191,7 +189,7 @@ internal class MessageSvc {
if (resp.result != 0) { if (resp.result != 0) {
bot.network.logger bot.network.logger
.warning("MessageSvc.PushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}") .warning { "MessageSvc.PushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}" }
return EmptyResponse return EmptyResponse
} }
...@@ -231,7 +229,7 @@ internal class MessageSvc { ...@@ -231,7 +229,7 @@ internal class MessageSvc {
} }
166 -> { 166 -> {
val friend = bot.getFriendOrNull(msg.msgHead.fromUin) ?: return@mapNotNull null val friend = bot.getFriendOrNull(msg.msgHead.fromUin) ?: return@mapNotNull null
friend.checkIsQQImpl() friend.checkIsFriendImpl()
if (msg.msgHead.fromUin == bot.id || !bot.firstLoginSucceed) { if (msg.msgHead.fromUin == bot.id || !bot.firstLoginSucceed) {
return@mapNotNull null return@mapNotNull null
...@@ -365,7 +363,7 @@ internal class MessageSvc { ...@@ -365,7 +363,7 @@ internal class MessageSvc {
inline fun createToFriend( inline fun createToFriend(
client: QQAndroidClient, client: QQAndroidClient,
qq: QQ, qq: Friend,
message: MessageChain, message: MessageChain,
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
): OutgoingPacket { ): OutgoingPacket {
......
...@@ -409,7 +409,7 @@ internal class OnlinePush { ...@@ -409,7 +409,7 @@ internal class OnlinePush {
0xB3L to lambda528 { bot -> 0xB3L to lambda528 { bot ->
// 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07 // 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07
val body = vProtobuf.loadAs(Submsgtype0xb3.SubMsgType0xb3.MsgBody.serializer()) val body = vProtobuf.loadAs(Submsgtype0xb3.SubMsgType0xb3.MsgBody.serializer())
val new = bot._lowLevelNewQQ(object : FriendInfo { val new = bot._lowLevelNewFriend(object : FriendInfo {
override val uin: Long get() = body.msgAddFrdNotify.fuin override val uin: Long get() = body.msgAddFrdNotify.fuin
override val nick: String get() = body.msgAddFrdNotify.fuinNick override val nick: String get() = body.msgAddFrdNotify.fuinNick
}) })
......
...@@ -28,8 +28,6 @@ import net.mamoe.mirai.utils.OverFileSizeMaxException ...@@ -28,8 +28,6 @@ import net.mamoe.mirai.utils.OverFileSizeMaxException
* *
* A QQ instance helps you to receive event from or sendPacket event to. * A QQ instance helps you to receive event from or sendPacket event to.
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same. * Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
*
* @author Him188moe
*/ */
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME")
actual abstract class QQ : Contact(), CoroutineScope { actual abstract class QQ : Contact(), CoroutineScope {
......
...@@ -109,20 +109,21 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -109,20 +109,21 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
// region contacts // region contacts
/** /**
* [QQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewQQ] 实例 * [QQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewFriend] 实例
*/ */
abstract val selfQQ: QQ abstract val selfQQ: Friend
/** /**
* 机器人的好友列表. 与服务器同步更新 * 机器人的好友列表. 与服务器同步更新
*/ */
abstract val friends: ContactList<QQ> abstract val friends: ContactList<Friend>
/** /**
* 获取一个好友对象. * 获取一个好友对象.
* @throws [NoSuchElementException] 当不存在这个好友时抛出 * @throws [NoSuchElementException] 当不存在这个好友时抛出
*/ */
fun getFriend(id: Long): QQ = friends.firstOrNull { it.id == id } ?: throw NoSuchElementException("friend $id") fun getFriend(id: Long): Friend = friends.firstOrNull { it.id == id } ?: throw NoSuchElementException("friend $id")
/** /**
* 机器人加入的群列表. 与服务器同步更新 * 机器人加入的群列表. 与服务器同步更新
...@@ -273,9 +274,17 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI( ...@@ -273,9 +274,17 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
@MiraiInternalAPI @MiraiInternalAPI
abstract val network: BotNetworkHandler abstract val network: BotNetworkHandler
@PlannedRemoval("1.0.0") @PlannedRemoval("1.0.0.")
@Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN) @get:JvmName("getSelfQQ")
suspend inline fun Bot.join() = this.coroutineContext[Job]!!.join() @Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
val selfQQDeprecated: QQ
get() = selfQQ
@JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun getFriendDeprecated(id: Long): QQ = this.getFriend(id)
} }
/** /**
...@@ -351,7 +360,7 @@ inline fun Bot.containsFriend(id: Long): Boolean = this.friends.contains(id) ...@@ -351,7 +360,7 @@ inline fun Bot.containsFriend(id: Long): Boolean = this.friends.contains(id)
inline fun Bot.containsGroup(id: Long): Boolean = this.groups.contains(id) inline fun Bot.containsGroup(id: Long): Boolean = this.groups.contains(id)
@JvmSynthetic @JvmSynthetic
inline fun Bot.getFriendOrNull(id: Long): QQ? = this.friends.getOrNull(id) inline fun Bot.getFriendOrNull(id: Long): Friend? = this.friends.getOrNull(id)
@JvmSynthetic @JvmSynthetic
inline fun Bot.getGroupOrNull(id: Long): Group? = this.groups.getOrNull(id) inline fun Bot.getGroupOrNull(id: Long): Group? = this.groups.getOrNull(id)
...@@ -32,9 +32,7 @@ import kotlin.jvm.JvmSynthetic ...@@ -32,9 +32,7 @@ import kotlin.jvm.JvmSynthetic
/** /**
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ], [群成员][Member] 和 [群][Group]. * 联系人. 虽然叫做联系人, 但他的子类有 [人][User], 和 [群][Group].
*
* @author Him188moe
*/ */
abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot { abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot {
/** /**
...@@ -85,23 +83,11 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot ...@@ -85,23 +83,11 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot
@JvmSynthetic @JvmSynthetic
abstract suspend fun uploadImage(image: ExternalImage): OfflineImage abstract suspend fun uploadImage(image: ExternalImage): OfflineImage
/** final override fun equals(other: Any?): Boolean = super.equals(other)
* 判断 `this` 和 [other] 是否是相同的类型, 并且 [id] 相同. final override fun hashCode(): Int = super.hashCode()
*
* 注:
* [id] 相同的 [Member] 和 [QQ], 他们并不 [equals].
* 因为, [Member] 含义为群员, 必属于一个群.
* 而 [QQ] 含义为一个独立的人, 可以是好友, 也可以是陌生人.
*/
abstract override fun equals(other: Any?): Boolean
/**
* @return `bot.hashCode() * 31 + id.hashCode()`
*/
abstract override fun hashCode(): Int
/** /**
* @return "QQ($id)" or "Group($id)" or "Member($id)" * @return "Friend($id)" or "Group($id)" or "Member($id)"
*/ */
abstract override fun toString(): String abstract override fun toString(): String
} }
......
...@@ -15,12 +15,11 @@ import net.mamoe.mirai.utils.SinceMirai ...@@ -15,12 +15,11 @@ import net.mamoe.mirai.utils.SinceMirai
/** /**
* 拥有 [id] 的对象. * 拥有 [id] 的对象.
* 此为 [Contact] 与 [Bot] 的唯一公共接口. * 此为 [Contact] 与 [Bot] 的唯一公共接口.
* **注意:** 此接口为实验性接口, 将来可能会发生不兼容的更名.
* *
* @see Contact * @see Contact
* @see Bot * @see Bot
*/ */
@SinceMirai("0.37.2") @SinceMirai("0.39.0")
interface ContactOrBot { interface ContactOrBot {
/** /**
* QQ 号或群号. * QQ 号或群号.
......
/*
* 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.message.data.toMessage
import kotlin.jvm.JvmSynthetic
/**
* 好友 对象.
* 注意: 一个 [Friend] 实例并不是独立的, 它属于一个 [Bot].
* 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
*
* 对于同一个 [Bot] 任何一个人的 [Friend] 实例都是单一的.
* 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
*/
@Suppress("DEPRECATION_ERROR")
abstract class Friend : QQ(), CoroutineScope {
/**
* 请求头像下载链接
*/
// @MiraiExperimentalAPI
//suspend fun queryAvatar(): AvatarLink
/**
* 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<Friend>
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<Friend> {
return sendMessage(message.toMessage())
}
final override fun toString(): String = "Friend($id)"
}
\ No newline at end of file
...@@ -21,23 +21,25 @@ import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent ...@@ -21,23 +21,25 @@ import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
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.OfflineGroupImage import net.mamoe.mirai.message.data.OfflineGroupImage
import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.OverFileSizeMaxException import net.mamoe.mirai.utils.OverFileSizeMaxException
import net.mamoe.mirai.utils.SinceMirai import net.mamoe.mirai.utils.SinceMirai
import kotlin.jvm.JvmStatic
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
/** /**
* 群. 在 QQ Android 中叫做 "Troop" * 群.
*/ */
expect abstract class Group() : Contact, CoroutineScope { abstract class Group : Contact(), CoroutineScope {
/** /**
* 群名称. * 群名称.
* *
* 在修改时将会异步上传至服务器. * 在修改时将会异步上传至服务器, 也会广播事件 [GroupNameChangeEvent].
* 频繁修改可能会被服务器拒绝. * 频繁修改可能会被服务器拒绝.
* *
* @see MemberPermissionChangeEvent * @see GroupNameChangeEvent 群名片修改事件
* @throws PermissionDeniedException 无权限修改时将会抛出异常 * @throws PermissionDeniedException 无权限修改时将会抛出异常
*/ */
abstract var name: String abstract var name: String
...@@ -88,6 +90,7 @@ expect abstract class Group() : Contact, CoroutineScope { ...@@ -88,6 +90,7 @@ expect abstract class Group() : Contact, CoroutineScope {
* 群头像下载链接. * 群头像下载链接.
*/ */
val avatarUrl: String val avatarUrl: String
get() = "https://p.qlogo.cn/gh/$id/${id}_1/640"
/** /**
* 群成员列表, 不含机器人自己, 含群主. * 群成员列表, 不含机器人自己, 含群主.
...@@ -148,6 +151,16 @@ expect abstract class Group() : Contact, CoroutineScope { ...@@ -148,6 +151,16 @@ expect abstract class Group() : Contact, CoroutineScope {
@JvmSynthetic @JvmSynthetic
abstract override suspend fun sendMessage(message: Message): MessageReceipt<Group> abstract override suspend fun sendMessage(message: Message): MessageReceipt<Group>
/**
* @see sendMessage
*/
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<Group> {
return sendMessage(message.toMessage())
}
/** /**
* 上传一个图片以备发送. * 上传一个图片以备发送.
* *
...@@ -161,14 +174,20 @@ expect abstract class Group() : Contact, CoroutineScope { ...@@ -161,14 +174,20 @@ expect abstract class Group() : Contact, CoroutineScope {
abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage
companion object { companion object {
// don't @JvmStatic: JDK 1.8 required /**
fun calculateGroupUinByGroupCode(groupCode: Long): Long * @suppress internal api
*/
fun calculateGroupCodeByGroupUin(groupUin: Long): Long @JvmStatic
fun calculateGroupUinByGroupCode(groupCode: Long): Long =
CommonGroupCalculations.calculateGroupUinByGroupCode(groupCode)
/**
* @suppress internal api
*/
@JvmStatic
fun calculateGroupCodeByGroupUin(groupUin: Long): Long =
CommonGroupCalculations.calculateGroupCodeByGroupUin(groupUin)
} }
@MiraiExperimentalAPI
fun toFullString(): String
} }
/** /**
......
...@@ -14,8 +14,10 @@ package net.mamoe.mirai.contact ...@@ -14,8 +14,10 @@ package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.JavaFriendlyAPI import net.mamoe.mirai.JavaFriendlyAPI
import net.mamoe.mirai.event.events.* import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.getFriendOrNull
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.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.WeakRefProperty import net.mamoe.mirai.utils.WeakRefProperty
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
...@@ -24,10 +26,13 @@ import kotlin.time.ExperimentalTime ...@@ -24,10 +26,13 @@ import kotlin.time.ExperimentalTime
/** /**
* 群成员. * 群成员.
*/ // 不要删除多平台结构, kotlin bug *
* 群成员可能也是好友, 但他们在对象类型上不同.
* 群成员可以通过 [asFriend] 得到相关好友对象.
*/
@Suppress("INAPPLICABLE_JVM_NAME") @Suppress("INAPPLICABLE_JVM_NAME")
@OptIn(MiraiInternalAPI::class, JavaFriendlyAPI::class) @OptIn(MiraiInternalAPI::class, JavaFriendlyAPI::class)
expect abstract class Member() : MemberJavaFriendlyAPI { abstract class Member : MemberJavaFriendlyAPI() {
/** /**
* 所在的群. * 所在的群.
*/ */
...@@ -119,16 +124,6 @@ expect abstract class Member() : MemberJavaFriendlyAPI { ...@@ -119,16 +124,6 @@ expect abstract class Member() : MemberJavaFriendlyAPI {
@JvmSynthetic @JvmSynthetic
abstract suspend fun kick(message: String = "") abstract suspend fun kick(message: String = "")
/**
* 当且仅当 `[other] is [Member] && [other].id == this.id && [other].group == this.group` 时为 true
*/
abstract override fun equals(other: Any?): Boolean
/**
* @return `bot.hashCode() * 31 + id.hashCode()`
*/
abstract override fun hashCode(): Int
/** /**
* 向这个对象发送消息. * 向这个对象发送消息.
* *
...@@ -145,6 +140,43 @@ expect abstract class Member() : MemberJavaFriendlyAPI { ...@@ -145,6 +140,43 @@ expect abstract class Member() : MemberJavaFriendlyAPI {
*/ */
@JvmSynthetic @JvmSynthetic
abstract override suspend fun sendMessage(message: Message): MessageReceipt<Member> abstract override suspend fun sendMessage(message: Message): MessageReceipt<Member>
/**
* @see sendMessage
*/
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<Member> {
return sendMessage(message.toMessage())
}
final override fun toString(): String = "Member($id)"
}
/**
* 得到此成员作为好友的对象.
*/
inline val Member.asFriend: Friend
get() = this.bot.getFriendOrNull(this.id) ?: error("$this is not a friend")
/**
* 得到此成员作为好友的对象.
*/
inline val Member.asFriendOrNull: Friend?
get() = this.bot.getFriendOrNull(this.id)
/**
* 判断此成员是否为好友
*/
inline val Member.isFriend: Boolean
get() = this.bot.friends.contains(this.id)
/**
* 如果此成员是好友, 则执行 [block] 并返回其返回值. 否则返回 `null`
*/
inline fun <R> Member.takeIfFriend(block: (Friend) -> R): R? {
return this.asFriendOrNull?.let(block)
} }
/** /**
...@@ -161,6 +193,9 @@ fun Member.isMuted(): Boolean { ...@@ -161,6 +193,9 @@ fun Member.isMuted(): Boolean {
return muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt() return muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt()
} }
/**
* @see Member.mute
*/
@ExperimentalTime @ExperimentalTime
suspend inline fun Member.mute(duration: Duration) { suspend inline fun Member.mute(duration: Duration) {
require(duration.inDays <= 30) { "duration must be at most 1 month" } require(duration.inDays <= 30) { "duration must be at most 1 month" }
...@@ -168,4 +203,7 @@ suspend inline fun Member.mute(duration: Duration) { ...@@ -168,4 +203,7 @@ suspend inline fun Member.mute(duration: Duration) {
this.mute(duration.inSeconds.toInt()) this.mute(duration.inSeconds.toInt())
} }
/**
* @see Member.mute
*/
suspend inline fun Member.mute(durationSeconds: Long) = this.mute(durationSeconds.toInt()) suspend inline fun Member.mute(durationSeconds: Long) = this.mute(durationSeconds.toInt())
\ No newline at end of file
...@@ -12,10 +12,8 @@ ...@@ -12,10 +12,8 @@
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.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai import net.mamoe.mirai.utils.SinceMirai
/** /**
* 群成员的权限. * 群成员的权限.
* *
...@@ -37,39 +35,42 @@ enum class MemberPermission : Comparable<MemberPermission> { ...@@ -37,39 +35,42 @@ enum class MemberPermission : Comparable<MemberPermission> {
*/ */
OWNER; // ordinal = 2 OWNER; // ordinal = 2
/**
* 权限等级. [OWNER] 为 2, [ADMINISTRATOR] 为 1, [MEMBER] 为 0
*/
@SinceMirai("0.32.0") @SinceMirai("0.32.0")
val level: Int val level: Int
get() = ordinal get() = ordinal
} }
/** /**
* 群主 * 判断权限是否为群主
*/ */
inline fun MemberPermission.isOwner(): Boolean = this == MemberPermission.OWNER inline fun MemberPermission.isOwner(): Boolean = this == MemberPermission.OWNER
/** /**
* 管理员 * 判断权限是否为管理员
*/ */
inline fun MemberPermission.isAdministrator(): Boolean = this == MemberPermission.ADMINISTRATOR inline fun MemberPermission.isAdministrator(): Boolean = this == MemberPermission.ADMINISTRATOR
/** /**
* 管理员或群主 * 判断权限是否为管理员或群主
*/ */
inline fun MemberPermission.isOperator(): Boolean = isAdministrator() || isOwner() inline fun MemberPermission.isOperator(): Boolean = isAdministrator() || isOwner()
/** /**
* 群主 * 判断权限是否为群主
*/ */
inline fun Member.isOwner(): Boolean = this.permission.isOwner() inline fun Member.isOwner(): Boolean = this.permission.isOwner()
/** /**
* 管理员 * 判断权限是否为管理员
*/ */
inline fun Member.isAdministrator(): Boolean = this.permission.isAdministrator() inline fun Member.isAdministrator(): Boolean = this.permission.isAdministrator()
/** /**
* 管理员或群主 * 判断权限是否为管理员或群主
*/ */
inline fun Member.isOperator(): Boolean = this.permission.isOperator() inline fun Member.isOperator(): Boolean = this.permission.isOperator()
...@@ -77,9 +78,10 @@ inline fun Member.isOperator(): Boolean = this.permission.isOperator() ...@@ -77,9 +78,10 @@ inline fun Member.isOperator(): Boolean = this.permission.isOperator()
/** /**
* 权限不足 * 权限不足
*/ */
expect class PermissionDeniedException : IllegalStateException { @Suppress("unused")
constructor() class PermissionDeniedException : IllegalStateException {
constructor(message: String?) constructor() : super("Permission denied")
constructor(message: String?) : super(message)
} }
/** /**
...@@ -87,10 +89,9 @@ expect class PermissionDeniedException : IllegalStateException { ...@@ -87,10 +89,9 @@ expect class PermissionDeniedException : IllegalStateException {
* *
* @throws PermissionDeniedException * @throws PermissionDeniedException
*/ */
@OptIn(MiraiExperimentalAPI::class)
inline fun Group.checkBotPermission( inline fun Group.checkBotPermission(
required: MemberPermission, required: MemberPermission,
lazyMessage: () -> String = { crossinline lazyMessage: () -> String = {
"Permission denied: required $required, got actual $botPermission for $bot in group $id" "Permission denied: required $required, got actual $botPermission for $bot in group $id"
} }
) { ) {
...@@ -104,9 +105,8 @@ inline fun Group.checkBotPermission( ...@@ -104,9 +105,8 @@ inline fun Group.checkBotPermission(
* *
* @throws PermissionDeniedException * @throws PermissionDeniedException
*/ */
@OptIn(MiraiExperimentalAPI::class)
inline fun Group.checkBotPermissionOperator( inline fun Group.checkBotPermissionOperator(
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"
} }
) { ) {
......
...@@ -13,76 +13,47 @@ package net.mamoe.mirai.contact ...@@ -13,76 +13,47 @@ package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.data.PreviousNameList
import net.mamoe.mirai.data.Profile
import net.mamoe.mirai.event.events.BeforeImageUploadEvent
import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.ImageUploadEvent
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.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.OfflineFriendImage import net.mamoe.mirai.utils.PlannedRemoval
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.OverFileSizeMaxException
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
/** /**
* QQ 对象. * QQ 对象.
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
* 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
* *
* 对于同一个 [Bot] 任何一个人的 [QQ] 实例都是单一的. * 自 0.39.0 起 mirai 引入 [User] 作为 [Friend] 和 [Member] 的父类,
* 以备将来支持仅 [Friend] 可用的 API, 如设置备注.
* *
* A QQ instance helps you to receive event from or sendPacket event to. * 所有 API 均有二进制兼容.
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
* *
* @author Him188moe * 请根据实际情况, 使用 [Friend] 或 [User] 替代.
*/ */
@Suppress("INAPPLICABLE_JVM_NAME") @PlannedRemoval("1.0.0")
expect abstract class QQ() : Contact, CoroutineScope { @Deprecated(
/** "use Friend or Person instead",
* 请求头像下载链接 replaceWith = ReplaceWith("Friend", "net.mamoe.mirai.contact.Friend"),
*/ level = DeprecationLevel.ERROR
// @MiraiExperimentalAPI )
//suspend fun queryAvatar(): AvatarLink @Suppress("DEPRECATION_ERROR")
abstract class QQ : User(), CoroutineScope {
/** /**
* QQ 号码 * QQ 号码
*/ */
abstract override val id: Long abstract override val id: Long
/**
* 昵称
*/
abstract val nick: String
/** /**
* 查询用户资料 * 昵称
*/ */
@MiraiExperimentalAPI("还未支持") abstract override val nick: String
abstract suspend fun queryProfile(): Profile
/** /**
* 头像下载链接 * 头像下载链接
*/ */
val avatarUrl: String override val avatarUrl: String
get() = "http://q1.qlogo.cn/g?b=qq&nk=$id&s=640"
/**
* 查询曾用名.
*
* 曾用名可能是:
* - 昵称
* - 共同群内的群名片
*/
@MiraiExperimentalAPI("还未支持")
abstract suspend fun queryPreviousNameList(): PreviousNameList
/**
* 查询机器人账号给这个人设置的备注
*/
@MiraiExperimentalAPI("还未支持")
abstract suspend fun queryRemark(): FriendNameRemark
/** /**
* 向这个对象发送消息. * 向这个对象发送消息.
...@@ -100,16 +71,4 @@ expect abstract class QQ() : Contact, CoroutineScope { ...@@ -100,16 +71,4 @@ expect abstract class QQ() : Contact, CoroutineScope {
*/ */
@JvmSynthetic @JvmSynthetic
abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ> abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ>
/**
* 上传一个图片以备发送.
*
* @see BeforeImageUploadEvent 图片发送前事件, cancellable
* @see ImageUploadEvent 图片发送完成事件
*
* @throws EventCancelledException 当发送消息事件被取消
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
*/
@JvmSynthetic
abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage
} }
\ No newline at end of file
@file:Suppress("unused") /*
* 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 package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.data.PreviousNameList
import net.mamoe.mirai.data.Profile
import net.mamoe.mirai.event.events.BeforeImageUploadEvent import net.mamoe.mirai.event.events.BeforeImageUploadEvent
import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.ImageUploadEvent import net.mamoe.mirai.event.events.ImageUploadEvent
...@@ -15,66 +21,38 @@ import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent ...@@ -15,66 +21,38 @@ import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
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.OfflineFriendImage import net.mamoe.mirai.message.data.OfflineFriendImage
import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.OverFileSizeMaxException import net.mamoe.mirai.utils.OverFileSizeMaxException
import kotlin.jvm.JvmSynthetic
/** /**
* QQ 对象. * 代表一个 **用户**.
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
* 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
* *
* 对于同一个 [Bot] 任何一个人的 [QQ] 实例都是单一的. * 其子类有 [群成员][Member] 和 [好友][Friend].
* 虽然群成员也可能是好友, 但他们仍是不同的两个类型.
* *
* A QQ instance helps you to receive event from or sendPacket event to. * 注意: 一个 [User] 实例并不是独立的, 它属于一个 [Bot].
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
* *
* @author Him188moe * 对于同一个 [Bot] 任何一个人的 [User] 实例都是单一的.
*/ */
@Suppress("INAPPLICABLE_JVM_NAME") abstract class User : Contact(), CoroutineScope {
actual abstract class QQ : Contact(), CoroutineScope {
/**
* 请求头像下载链接
*/
// @MiraiExperimentalAPI
//suspend fun queryAvatar(): AvatarLink
/** /**
* QQ 号码 * QQ 号码
*/ */
actual abstract override val id: Long abstract override val id: Long
/**
* 昵称
*/
actual abstract val nick: String
/** /**
* 查询用户资料 * 昵称
*/ */
@MiraiExperimentalAPI("还未支持") abstract val nick: String
actual abstract suspend fun queryProfile(): Profile
/** /**
* 头像下载链接 * 头像下载链接
*/ */
actual val avatarUrl: String open val avatarUrl: String
get() = "http://q1.qlogo.cn/g?b=qq&nk=$id&s=640" get() = "http://q1.qlogo.cn/g?b=qq&nk=$id&s=640"
/**
* 查询曾用名.
*
* 曾用名可能是:
* - 昵称
* - 共同群内的群名片
*/
@MiraiExperimentalAPI("还未支持")
actual abstract suspend fun queryPreviousNameList(): PreviousNameList
/**
* 查询机器人账号给这个人设置的备注
*/
@MiraiExperimentalAPI("还未支持")
actual abstract suspend fun queryRemark(): FriendNameRemark
/** /**
* 向这个对象发送消息. * 向这个对象发送消息.
* *
...@@ -90,7 +68,17 @@ actual abstract class QQ : Contact(), CoroutineScope { ...@@ -90,7 +68,17 @@ actual abstract class QQ : Contact(), CoroutineScope {
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall]) * @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
*/ */
@JvmSynthetic @JvmSynthetic
actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ> abstract override suspend fun sendMessage(message: Message): MessageReceipt<User>
/**
* @see sendMessage
*/
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
@kotlin.internal.InlineOnly // purely virtual
@JvmSynthetic
suspend inline fun sendMessage(message: String): MessageReceipt<User> {
return sendMessage(message.toMessage())
}
/** /**
* 上传一个图片以备发送. * 上传一个图片以备发送.
...@@ -102,5 +90,7 @@ actual abstract class QQ : Contact(), CoroutineScope { ...@@ -102,5 +90,7 @@ actual abstract class QQ : Contact(), CoroutineScope {
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB) * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
*/ */
@JvmSynthetic @JvmSynthetic
actual abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage
abstract override fun toString(): String
} }
\ No newline at end of file
...@@ -13,15 +13,18 @@ import net.mamoe.mirai.JavaFriendlyAPI ...@@ -13,15 +13,18 @@ import net.mamoe.mirai.JavaFriendlyAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
/** /**
* [Contact] 中为了让 `Java` 更容易调用的 API * [Contact] 中为了让 `Java` 更容易调用的 API.
* 不要用它作为一个类型, 只应使用其中的方法
*/ */
@MiraiInternalAPI @MiraiInternalAPI
@JavaFriendlyAPI @JavaFriendlyAPI
expect abstract class ContactJavaFriendlyAPI() expect abstract class ContactJavaFriendlyAPI internal constructor()
/** /**
* [Member] 中为了让 `Java` 更容易调用的 API * [Member] 中为了让 `Java` 更容易调用的 API
* 不要用它作为一个类型, 只应使用其中的方法
*/ */
@Suppress("DEPRECATION_ERROR")
@MiraiInternalAPI @MiraiInternalAPI
@JavaFriendlyAPI @JavaFriendlyAPI
expect abstract class MemberJavaFriendlyAPI : QQ expect abstract class MemberJavaFriendlyAPI internal constructor() : QQ // 将来会改为 User
\ No newline at end of file \ No newline at end of file
...@@ -241,7 +241,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>( ...@@ -241,7 +241,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
/** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */ /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
@MessageDsl @MessageDsl
fun sentBy(friend: QQ): ListeningFilter = content { sender.id == friend.id } fun sentBy(friend: User): ListeningFilter = content { sender.id == friend.id }
/** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */ /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
@MessageDsl @MessageDsl
...@@ -294,7 +294,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>( ...@@ -294,7 +294,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
/** [消息内容][Message.contentToString]包含目标为 [target] 的 [At] */ /** [消息内容][Message.contentToString]包含目标为 [target] 的 [At] */
@MessageDsl @MessageDsl
fun at(target: QQ): ListeningFilter = content { message.firstIsInstanceOrNull<At>()?.target == target.id } fun at(target: User): ListeningFilter = content { message.firstIsInstanceOrNull<At>()?.target == target.id }
/** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At], 就执行 [onEvent] */ /** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At], 就执行 [onEvent] */
@MessageDsl @MessageDsl
......
...@@ -102,7 +102,7 @@ sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractCancellableEve ...@@ -102,7 +102,7 @@ sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractCancellableEve
) : MessageSendEvent(), CancellableEvent ) : MessageSendEvent(), CancellableEvent
data class FriendMessageSendEvent( data class FriendMessageSendEvent(
override val target: QQ, override val target: Friend,
var message: MessageChain var message: MessageChain
) : MessageSendEvent(), CancellableEvent ) : MessageSendEvent(), CancellableEvent
} }
...@@ -539,9 +539,17 @@ data class MemberUnmuteEvent( ...@@ -539,9 +539,17 @@ data class MemberUnmuteEvent(
@SinceMirai("0.36.0") @SinceMirai("0.36.0")
data class FriendRemarkChangeEvent( data class FriendRemarkChangeEvent(
override val bot: Bot, override val bot: Bot,
val friend: QQ, val friend: Friend,
val newName: String val newName: String
) : BotEvent, Packet ) : BotEvent, Packet {
@Deprecated("", level = DeprecationLevel.HIDDEN)
@get:JvmSynthetic
@get:JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
val friendDeprecated: QQ
get() = friend
}
/** /**
* 成功添加了一个新好友的事件 * 成功添加了一个新好友的事件
...@@ -551,9 +559,16 @@ data class FriendAddEvent( ...@@ -551,9 +559,16 @@ data class FriendAddEvent(
/** /**
* 新好友. 已经添加到 [Bot.friends] * 新好友. 已经添加到 [Bot.friends]
*/ */
val friend: QQ val friend: Friend
) : BotEvent, Packet { ) : BotEvent, Packet {
override val bot: Bot get() = friend.bot override val bot: Bot get() = friend.bot
@Deprecated("", level = DeprecationLevel.HIDDEN)
@get:JvmSynthetic
@get:JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
val friendDeprecated: QQ
get() = friend
} }
/** /**
...@@ -561,9 +576,16 @@ data class FriendAddEvent( ...@@ -561,9 +576,16 @@ data class FriendAddEvent(
*/ */
@SinceMirai("0.36.0") @SinceMirai("0.36.0")
data class FriendDeleteEvent( data class FriendDeleteEvent(
val friend: QQ val friend: Friend
) : BotEvent, Packet { ) : BotEvent, Packet {
override val bot: Bot get() = friend.bot override val bot: Bot get() = friend.bot
@Deprecated("", level = DeprecationLevel.HIDDEN)
@get:JvmSynthetic
@get:JvmName("getFriend")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
val friendDeprecated: QQ
get() = friend
} }
/** /**
......
...@@ -10,11 +10,13 @@ ...@@ -10,11 +10,13 @@
package net.mamoe.mirai.event.events package net.mamoe.mirai.event.events
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
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] 的事件
...@@ -85,7 +87,14 @@ val GroupOperableEvent.operatorOrBot: Member ...@@ -85,7 +87,14 @@ val GroupOperableEvent.operatorOrBot: Member
* 有关好友的事件 * 有关好友的事件
*/ */
interface FriendEvent : BotEvent { interface FriendEvent : BotEvent {
val friend: QQ 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
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
package net.mamoe.mirai package net.mamoe.mirai
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.* import net.mamoe.mirai.data.*
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai import net.mamoe.mirai.utils.SinceMirai
...@@ -37,13 +37,13 @@ annotation class LowLevelAPI ...@@ -37,13 +37,13 @@ annotation class LowLevelAPI
@LowLevelAPI @LowLevelAPI
interface LowLevelBotAPIAccessor { interface LowLevelBotAPIAccessor {
/** /**
* 构造一个 [_lowLevelNewQQ] 对象. 它持有对 [Bot] 的弱引用([WeakRef]). * 构造一个 [Friend] 对象. 它持有对 [Bot] 的弱引用([WeakRef]).
* *
* [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job. * [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job.
* 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭. * 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭.
*/ */
@LowLevelAPI @LowLevelAPI
fun _lowLevelNewQQ(friendInfo: FriendInfo): QQ fun _lowLevelNewFriend(friendInfo: FriendInfo): Friend
/** /**
* 向服务器查询群列表. 返回值高 32 bits 为 uin, 低 32 bits 为 groupCode * 向服务器查询群列表. 返回值高 32 bits 为 uin, 低 32 bits 为 groupCode
......
...@@ -23,10 +23,7 @@ import kotlinx.coroutines.TimeoutCancellationException ...@@ -23,10 +23,7 @@ import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.io.ByteReadChannel import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.selectMessages import net.mamoe.mirai.event.selectMessages
import net.mamoe.mirai.event.syncFromEvent import net.mamoe.mirai.event.syncFromEvent
...@@ -52,7 +49,7 @@ import kotlin.jvm.JvmSynthetic ...@@ -52,7 +49,7 @@ import kotlin.jvm.JvmSynthetic
*/ */
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@SinceMirai("0.32.0") @SinceMirai("0.32.0")
abstract class ContactMessage : MessagePacket<QQ, Contact>(), BotEvent abstract class ContactMessage : MessagePacket<User, Contact>(), BotEvent
/** /**
* 一条从服务器接收到的消息事件. * 一条从服务器接收到的消息事件.
...@@ -63,7 +60,7 @@ abstract class ContactMessage : MessagePacket<QQ, Contact>(), BotEvent ...@@ -63,7 +60,7 @@ abstract class ContactMessage : MessagePacket<QQ, Contact>(), BotEvent
message = "use ContactMessage", message = "use ContactMessage",
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage") replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
) )
expect abstract class MessagePacket<TSender : QQ, TSubject : Contact> constructor() : expect abstract class MessagePacket<TSender : User, TSubject : Contact> constructor() :
MessagePacketBase<TSender, TSubject> MessagePacketBase<TSender, TSubject>
/** /**
...@@ -74,7 +71,7 @@ expect abstract class MessagePacket<TSender : QQ, TSubject : Contact> constructo ...@@ -74,7 +71,7 @@ expect abstract class MessagePacket<TSender : QQ, TSubject : Contact> constructo
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage") replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
) )
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Packet, BotEvent { abstract class MessagePacketBase<out TSender : User, out TSubject : Contact> : Packet, BotEvent {
/** /**
* 接受到这条消息的 * 接受到这条消息的
*/ */
...@@ -105,6 +102,10 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac ...@@ -105,6 +102,10 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
*/ */
abstract val message: MessageChain abstract val message: MessageChain
/**
* 消息源
*/
open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
// region 发送 Message // region 发送 Message
...@@ -118,18 +119,11 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac ...@@ -118,18 +119,11 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
suspend inline fun reply(plain: String): MessageReceipt<TSubject> = suspend inline fun reply(plain: String): MessageReceipt<TSubject> =
subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject> subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject>
// endregion
// region 撤回
// endregion // endregion
// region 上传图片 // region 图片
suspend inline fun ExternalImage.upload(): Image = this.upload(subject) suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
// endregion
// region 发送图片
suspend inline fun ExternalImage.send(): MessageReceipt<TSubject> = this.sendTo(subject) suspend inline fun ExternalImage.send(): MessageReceipt<TSubject> = this.sendTo(subject)
suspend inline fun Image.send(): MessageReceipt<TSubject> = this.sendTo(subject) suspend inline fun Image.send(): MessageReceipt<TSubject> = this.sendTo(subject)
...@@ -159,22 +153,12 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac ...@@ -159,22 +153,12 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
@JvmName("reply2") @JvmName("reply2")
suspend inline fun MessageChain.quoteReply(): MessageReceipt<TSubject> = quoteReply(this) suspend inline fun MessageChain.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
// endregion // endregion
operator fun <M : Message> get(at: Message.Key<M>): M { inline operator fun <M : Message> get(at: Message.Key<M>): M {
return this.message[at] return this.message[at]
} }
/**
* 创建 @ 这个账号的消息. 当且仅当消息为群消息时可用. 否则将会抛出 [IllegalArgumentException]
*/
fun QQ.at(): At = At(this as? Member ?: error("`QQ.at` can only be used in GroupMessage"))
fun At.member(): Member = (this@MessagePacketBase as? GroupMessage)?.group?.get(this.target)
?: error("`At.member` can only be used in GroupMessage")
inline fun At.isBot(): Boolean = target == bot.id inline fun At.isBot(): Boolean = target == bot.id
// endregion // endregion
...@@ -202,13 +186,40 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac ...@@ -202,13 +186,40 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
// endregion // endregion
@Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)")) @PlannedRemoval("1.0.0")
@Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)"),
DeprecationLevel.ERROR)
@JvmName("reply1") @JvmName("reply1")
suspend inline fun String.reply(): MessageReceipt<TSubject> = reply(this) suspend inline fun String.reply(): MessageReceipt<TSubject> = reply(this)
@Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)")) @PlannedRemoval("1.0.0")
@Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)"),
level = DeprecationLevel.ERROR)
@JvmName("reply1") @JvmName("reply1")
suspend inline fun Message.reply(): MessageReceipt<TSubject> = reply(this) suspend inline fun Message.reply(): MessageReceipt<TSubject> = reply(this)
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
@get:JvmSynthetic
@get:JvmName("getSender")
@Suppress("DEPRECATION_ERROR", "INAPPLICABLE_JVM_NAME")
val senderDeprecated: QQ
get() = sender as QQ
@Suppress("DEPRECATION_ERROR")
@PlannedRemoval("1.0.0")
@Deprecated("removed",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith("At(this as? Member ?: error(\"`QQ.at` can only be used in GroupMessage\"))",
"net.mamoe.mirai.message.data.At",
"net.mamoe.mirai.contact.Member"))
fun QQ.at(): At = At(this as? Member ?: error("`QQ.at` can only be used in GroupMessage"))
@PlannedRemoval("1.0.0")
@Deprecated("removed",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith("(this@MessagePacketBase as? GroupMessage)?.group?.get(this.target) ?: error(\"`At.member` can only be used in GroupMessage\")"))
fun At.member(): Member = (this@MessagePacketBase as? GroupMessage)?.group?.get(this.target)
?: error("`At.member` can only be used in GroupMessage")
} }
/** /**
......
...@@ -7,15 +7,19 @@ ...@@ -7,15 +7,19 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("DEPRECATION_ERROR")
package net.mamoe.mirai.message package net.mamoe.mirai.message
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.BroadcastControllable import net.mamoe.mirai.event.BroadcastControllable
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.message.data.OnlineMessageSource import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.message.data.source import net.mamoe.mirai.message.data.source
import net.mamoe.mirai.utils.PlannedRemoval
import net.mamoe.mirai.utils.getValue import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.unsafeWeakRef import net.mamoe.mirai.utils.unsafeWeakRef
...@@ -23,17 +27,21 @@ import net.mamoe.mirai.utils.unsafeWeakRef ...@@ -23,17 +27,21 @@ import net.mamoe.mirai.utils.unsafeWeakRef
* 好友消息 * 好友消息
*/ */
class FriendMessage( class FriendMessage(
sender: QQ, sender: Friend,
override val message: MessageChain override val message: MessageChain
) : ContactMessage(), BroadcastControllable { ) : ContactMessage(), BroadcastControllable {
@PlannedRemoval("1.0.0")
@Deprecated("", level = DeprecationLevel.HIDDEN)
constructor(sender: QQ, message: MessageChain) : this(sender as Friend, message)
init { init {
val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message") val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message")
check(source is OnlineMessageSource.Incoming.FromFriend) { "source provided to a FriendMessage must be an instance of OnlineMessageSource.Incoming.FromFriend" } check(source is OnlineMessageSource.Incoming.FromFriend) { "source provided to a FriendMessage must be an instance of OnlineMessageSource.Incoming.FromFriend" }
} }
override val sender: QQ by sender.unsafeWeakRef() override val sender: Friend by sender.unsafeWeakRef()
override val bot: Bot get() = sender.bot override val bot: Bot get() = sender.bot
override val subject: QQ get() = sender override val subject: Friend get() = sender
override val source: OnlineMessageSource.Incoming.FromFriend get() = message.source as OnlineMessageSource.Incoming.FromFriend override val source: OnlineMessageSource.Incoming.FromFriend get() = message.source as OnlineMessageSource.Incoming.FromFriend
override fun toString(): String = "FriendMessage(sender=${sender.id}, message=$message)" override fun toString(): String = "FriendMessage(sender=${sender.id}, message=$message)"
......
...@@ -14,10 +14,7 @@ import net.mamoe.mirai.contact.Group ...@@ -14,10 +14,7 @@ import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.Event
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.message.data.source
import net.mamoe.mirai.utils.getValue import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.unsafeWeakRef import net.mamoe.mirai.utils.unsafeWeakRef
...@@ -44,7 +41,7 @@ class GroupMessage( ...@@ -44,7 +41,7 @@ class GroupMessage(
override val source: OnlineMessageSource.Incoming.FromGroup get() = message.source as OnlineMessageSource.Incoming.FromGroup override val source: OnlineMessageSource.Incoming.FromGroup get() = message.source as OnlineMessageSource.Incoming.FromGroup
inline fun Long.member(): Member = group[this] inline fun At.asMember(): Member = group[this.target]
override fun toString(): String = override fun toString(): String =
"GroupMessage(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)" "GroupMessage(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)"
......
...@@ -116,14 +116,12 @@ sealed class OnlineMessageSource : MessageSource() { ...@@ -116,14 +116,12 @@ sealed class OnlineMessageSource : MessageSource() {
* 消息发送人. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群员][Member]. * 消息发送人. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群员][Member].
* 即类型必定为 [Bot], [QQ] 或 [Member] * 即类型必定为 [Bot], [QQ] 或 [Member]
*/ */
@ExperimentalIdentification
abstract val sender: ContactOrBot abstract val sender: ContactOrBot
/** /**
* 消息发送目标. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群][Group]. * 消息发送目标. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群][Group].
* 即类型必定为 [Bot], [QQ] 或 [Group] * 即类型必定为 [Bot], [QQ] 或 [Group]
*/ */
@ExperimentalIdentification
abstract val target: ContactOrBot abstract val target: ContactOrBot
/** /**
...@@ -135,7 +133,6 @@ sealed class OnlineMessageSource : MessageSource() { ...@@ -135,7 +133,6 @@ sealed class OnlineMessageSource : MessageSource() {
/** /**
* 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource] * 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource]
*/ */
@OptIn(ExperimentalIdentification::class)
sealed class Outgoing : OnlineMessageSource() { sealed class Outgoing : OnlineMessageSource() {
companion object Key : Message.Key<Outgoing> { companion object Key : Message.Key<Outgoing> {
override val typeName: String get() = "OnlineMessageSource.Outgoing" override val typeName: String get() = "OnlineMessageSource.Outgoing"
...@@ -152,8 +149,8 @@ sealed class OnlineMessageSource : MessageSource() { ...@@ -152,8 +149,8 @@ sealed class OnlineMessageSource : MessageSource() {
override val typeName: String get() = "OnlineMessageSource.Outgoing.ToFriend" override val typeName: String get() = "OnlineMessageSource.Outgoing.ToFriend"
} }
abstract override val target: QQ abstract override val target: Friend
final override val subject: QQ get() = target final override val subject: Friend get() = target
// final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.id})" // final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.id})"
} }
...@@ -181,13 +178,12 @@ sealed class OnlineMessageSource : MessageSource() { ...@@ -181,13 +178,12 @@ sealed class OnlineMessageSource : MessageSource() {
/** /**
* 接收到的一条消息的 [MessageSource] * 接收到的一条消息的 [MessageSource]
*/ */
@OptIn(ExperimentalIdentification::class)
sealed class Incoming : OnlineMessageSource() { sealed class Incoming : OnlineMessageSource() {
companion object Key : Message.Key<Incoming> { companion object Key : Message.Key<Incoming> {
override val typeName: String get() = "OnlineMessageSource.Incoming" override val typeName: String get() = "OnlineMessageSource.Incoming"
} }
abstract override val sender: QQ // out QQ abstract override val sender: User
final override val fromId: Long get() = sender.id final override val fromId: Long get() = sender.id
final override val targetId: Long get() = target.id final override val targetId: Long get() = target.id
...@@ -197,8 +193,8 @@ sealed class OnlineMessageSource : MessageSource() { ...@@ -197,8 +193,8 @@ sealed class OnlineMessageSource : MessageSource() {
override val typeName: String get() = "OnlineMessageSource.Incoming.FromFriend" override val typeName: String get() = "OnlineMessageSource.Incoming.FromFriend"
} }
abstract override val sender: QQ abstract override val sender: Friend
final override val subject: QQ get() = sender final override val subject: Friend get() = sender
final override val target: Bot get() = sender.bot final override val target: Bot get() = sender.bot
// final override fun toString(): String = "OnlineMessageSource.FromFriend(from=${sender.id})" // final override fun toString(): String = "OnlineMessageSource.FromFriend(from=${sender.id})"
} }
...@@ -243,7 +239,6 @@ sealed class OnlineMessageSource : MessageSource() { ...@@ -243,7 +239,6 @@ sealed class OnlineMessageSource : MessageSource() {
@Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN) @Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
@get:JvmName("target") @get:JvmName("target")
@get:JvmSynthetic @get:JvmSynthetic
@OptIn(ExperimentalIdentification::class)
open val target2: Any open val target2: Any
get() = target get() = target
...@@ -251,7 +246,6 @@ sealed class OnlineMessageSource : MessageSource() { ...@@ -251,7 +246,6 @@ sealed class OnlineMessageSource : MessageSource() {
@Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN) @Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
@get:JvmName("sender") @get:JvmName("sender")
@get:JvmSynthetic @get:JvmSynthetic
@OptIn(ExperimentalIdentification::class)
open val sender2: Any open val sender2: Any
get() = sender get() = sender
} }
......
...@@ -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_API_USAGE") @file:Suppress("EXPERIMENTAL_API_USAGE", "unused")
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
...@@ -18,7 +18,7 @@ import kotlinx.io.core.Input ...@@ -18,7 +18,7 @@ import kotlinx.io.core.Input
import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.InternalSerializationApi
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.User
import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.OfflineImage import net.mamoe.mirai.message.data.OfflineImage
...@@ -153,7 +153,7 @@ class ExternalImage private constructor( ...@@ -153,7 +153,7 @@ class ExternalImage private constructor(
@JvmSynthetic @JvmSynthetic
suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> = when (contact) { suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> = when (contact) {
is Group -> contact.uploadImage(this).sendTo(contact) is Group -> contact.uploadImage(this).sendTo(contact)
is QQ -> contact.uploadImage(this).sendTo(contact) is User -> contact.uploadImage(this).sendTo(contact)
else -> error("unreachable") else -> error("unreachable")
} }
...@@ -166,7 +166,7 @@ suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> = ...@@ -166,7 +166,7 @@ suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> =
@JvmSynthetic @JvmSynthetic
suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact) { suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact) {
is Group -> contact.uploadImage(this) is Group -> contact.uploadImage(this)
is QQ -> contact.uploadImage(this) is User -> contact.uploadImage(this)
else -> error("unreachable") else -> error("unreachable")
} }
......
package net.mamoe.mirai package net.mamoe.mirai
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.mamoe.mirai.contact.PermissionDeniedException
import net.mamoe.mirai.contact.recall import net.mamoe.mirai.contact.recall
import net.mamoe.mirai.data.AddFriendResult import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.MessageReceipt
......
...@@ -205,7 +205,7 @@ actual abstract class ContactJavaFriendlyAPI { ...@@ -205,7 +205,7 @@ actual abstract class ContactJavaFriendlyAPI {
} }
} }
@Suppress("INAPPLICABLE_JVM_NAME", "FunctionName", "unused", "unused") @Suppress("INAPPLICABLE_JVM_NAME", "FunctionName", "unused", "unused", "DEPRECATION_ERROR")
@MiraiInternalAPI @MiraiInternalAPI
@JavaFriendlyAPI @JavaFriendlyAPI
actual abstract class MemberJavaFriendlyAPI : QQ() { actual abstract class MemberJavaFriendlyAPI : QQ() {
......
/*
* 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.contact
import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.Bot
import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.data.MemberInfo
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.Message
import net.mamoe.mirai.message.data.OfflineGroupImage
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.OverFileSizeMaxException
import net.mamoe.mirai.utils.SinceMirai
/**
* 群. 在 QQ Android 中叫做 "Troop"
*/
@Suppress("INAPPLICABLE_JVM_NAME")
actual abstract class Group : Contact(), CoroutineScope {
/**
* 群名称.
*
* 在修改时将会异步上传至服务器.
* 频繁修改可能会被服务器拒绝.
*
* @see MemberPermissionChangeEvent
* @throws PermissionDeniedException 无权限修改时将会抛出异常
*/
actual abstract var name: String
/**
* 群设置
*/
@SinceMirai("0.30.0")
actual abstract val settings: GroupSettings
/**
* 同为 groupCode, 用户看到的群号码.
*/
actual abstract override val id: Long
/**
* 群主.
*
* @return 若机器人是群主, 返回 [botAsMember]. 否则返回相应的成员
*/
actual abstract val owner: Member
/**
* [Bot] 在群内的 [newMember] 实例
*/
@MiraiExperimentalAPI
actual abstract val botAsMember: Member
/**
* 机器人被禁言还剩余多少秒
*
* @see BotMuteEvent 机器人被禁言事件
* @see isBotMuted 判断机器人是否正在被禁言
*/
actual abstract val botMuteRemaining: Int
/**
* 机器人在这个群里的权限
*
* @see Group.checkBotPermission 检查 [Bot] 在这个群里的权限
* @see Group.checkBotPermissionOperator 要求 [Bot] 在这个群里的权限为 [管理员或群主][MemberPermission.isOperator]
*
* @see BotGroupPermissionChangeEvent 机器人群员修改
*/
actual abstract val botPermission: MemberPermission
/**
* 群头像下载链接.
*/
actual val avatarUrl: String
get() = "https://p.qlogo.cn/gh/$id/${id}_1/640"
/**
* 群成员列表, 不含机器人自己, 含群主.
* 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新
*/
actual abstract val members: ContactList<Member>
/**
* 获取群成员实例. 不存在时抛出 [kotlin.NoSuchElementException]
* 当 [id] 为 [Bot.id] 时返回 [botAsMember]
*/
actual abstract operator fun get(id: Long): Member
/**
* 获取群成员实例, 不存在则 null
* 当 [id] 为 [Bot.id] 时返回 [botAsMember]
*/
actual abstract fun getOrNull(id: Long): Member?
/**
* 检查此 id 的群成员是否存在
* 当 [id] 为 [Bot.id] 时返回 `true`
*/
actual abstract operator fun contains(id: Long): Boolean
/**
* 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败
*/
@SinceMirai("0.37.0")
actual abstract suspend fun quit(): Boolean
/**
* 构造一个 [Member].
* 非特殊情况请不要使用这个函数. 优先使用 [get].
*/
@LowLevelAPI
@MiraiExperimentalAPI("dangerous")
actual abstract fun newMember(memberInfo: MemberInfo): Member
/**
* 向这个对象发送消息.
*
* 单条消息最大可发送 4500 字符或 50 张图片.
*
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送群消息事件. cancellable
*
* @throws EventCancelledException 当发送消息事件被取消时抛出
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
* @throws MessageTooLargeException 当消息过长时抛出
*
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
*/
@JvmSynthetic
actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<Group>
/**
* 上传一个图片以备发送.
*
* @see BeforeImageUploadEvent 图片上传前事件, cancellable
* @see ImageUploadEvent 图片上传完成事件
*
* @throws EventCancelledException 当发送消息事件被取消
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
*/
@JvmSynthetic
actual abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage
actual companion object {
actual fun calculateGroupUinByGroupCode(groupCode: Long): Long =
CommonGroupCalculations.calculateGroupUinByGroupCode(groupCode)
actual fun calculateGroupCodeByGroupUin(groupUin: Long): Long =
CommonGroupCalculations.calculateGroupCodeByGroupUin(groupUin)
}
@MiraiExperimentalAPI
actual fun toFullString(): String {
return "Group(id=${this.id}, name=$name, owner=${owner.id}, members=${members.idContentString})"
}
}
\ 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("unused")
package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot
import net.mamoe.mirai.JavaFriendlyAPI
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.Message
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.WeakRefProperty
/**
* 群成员.
*/
@OptIn(MiraiInternalAPI::class, JavaFriendlyAPI::class)
@Suppress("INAPPLICABLE_JVM_NAME")
actual abstract class Member : MemberJavaFriendlyAPI() {
/**
* 所在的群.
*/
@WeakRefProperty
actual abstract val group: Group
/**
* 成员的权限, 动态更新.
*
* @see MemberPermissionChangeEvent 权限变更事件. 由群主或机器人的操作触发.
*/
actual abstract val permission: MemberPermission
/**
* 群名片. 可能为空.
*
* 管理员和群主都可修改任何人(包括群主)的群名片.
*
* 在修改时将会异步上传至服务器.
*
* @see [nameCardOrNick] 获取非空群名片或昵称
*
* @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
* @throws PermissionDeniedException 无权限修改时
*/
actual abstract var nameCard: String
/**
* 群头衔.
*
* 仅群主可以修改群头衔.
*
* 在修改时将会异步上传至服务器.
*
* @see MemberSpecialTitleChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
* @throws PermissionDeniedException 无权限修改时
*/
actual abstract var specialTitle: String
/**
* 被禁言剩余时长. 单位为秒.
*
* @see isMuted 判断改成员是否处于禁言状态
* @see mute 设置禁言
* @see unmute 取消禁言
*/
actual abstract val muteTimeRemaining: Int
/**
* 禁言.
*
* QQ 中最小操作和显示的时间都是一分钟.
* 机器人可以实现精确到秒, 会被客户端显示为 1 分钟但不影响实际禁言时间.
*
* 管理员可禁言成员, 群主可禁言管理员和群员.
*
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
* @return 机器人无权限时返回 `false`
*
* @see Int.minutesToSeconds
* @see Int.hoursToSeconds
* @see Int.daysToSeconds
*
* @see MemberMuteEvent 成员被禁言事件
* @throws PermissionDeniedException 无权限修改时
*/
@JvmSynthetic
actual abstract suspend fun mute(durationSeconds: Int)
/**
* 解除禁言.
*
* 管理员可解除成员的禁言, 群主可解除管理员和群员的禁言.
*
* @see MemberUnmuteEvent 成员被取消禁言事件.
* @throws PermissionDeniedException 无权限修改时
*/
@JvmSynthetic
actual abstract suspend fun unmute()
/**
* 踢出该成员.
*
* 管理员可踢出成员, 群主可踢出管理员和群员.
*
* @see MemberLeaveEvent.Kick 成员被踢出事件.
* @throws PermissionDeniedException 无权限修改时
*/
@JvmSynthetic
actual abstract suspend fun kick(message: String)
/**
* 向这个对象发送消息.
*
* 单条消息最大可发送 4500 字符或 50 张图片.
*
* @see FriendMessageSendEvent 发送好友信息事件, cancellable
* @see GroupMessageSendEvent 发送群消息事件. cancellable
*
* @throws EventCancelledException 当发送消息事件被取消时抛出
* @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
* @throws MessageTooLargeException 当消息过长时抛出
*
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
*/
@JvmSynthetic
actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<Member>
/**
* 当且仅当 `[other] is [Member] && [other].id == this.id && [other].group == this.group` 时为 true
*/
actual abstract override fun equals(other: Any?): Boolean
/**
* @return `bot.hashCode() * 31 + id.hashCode()`
*/
actual abstract override fun hashCode(): Int
}
\ No newline at end of file
package net.mamoe.mirai.contact
/**
* 权限不足
*/ // 不要删除多平台结构
actual class PermissionDeniedException : IllegalStateException {
actual constructor() : super("Permission denied")
actual constructor(message: String?) : super(message)
}
\ No newline at end of file
...@@ -16,7 +16,8 @@ import kotlinx.io.core.Input ...@@ -16,7 +16,8 @@ import kotlinx.io.core.Input
import kotlinx.io.core.Output import kotlinx.io.core.Output
import kotlinx.io.core.use import kotlinx.io.core.use
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
...@@ -38,7 +39,7 @@ import java.net.URL ...@@ -38,7 +39,7 @@ import java.net.URL
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage") replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
) )
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class) @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual constructor() : actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual constructor() :
MessagePacketBase<TSender, TSubject>() { MessagePacketBase<TSender, TSubject>() {
// region 上传图片 // region 上传图片
@JvmSynthetic @JvmSynthetic
......
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