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
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
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.QQImpl
import net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl
import net.mamoe.mirai.qqandroid.message.*
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
......@@ -89,7 +89,7 @@ internal class QQAndroidBot constructor(
event,
accept = true
).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 nick: String get() = event.fromNick
}))
......@@ -150,7 +150,6 @@ internal class QQAndroidBot constructor(
}
override suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) {
check(event.responded.compareAndSet(false, true)) {
"the request $this has already been responded"
}
......@@ -188,15 +187,15 @@ internal abstract class QQAndroidBotBase constructor(
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
internal lateinit var selfInfo: JceFriendInfo
override val selfQQ: QQ by lazy {
override val selfQQ: Friend by lazy {
@OptIn(LowLevelAPI::class)
_lowLevelNewQQ(object : FriendInfo {
_lowLevelNewFriend(object : FriendInfo {
override val uin: Long get() = this@QQAndroidBotBase.id
override val nick: String get() = this@QQAndroidBotBase.nick
})
......@@ -214,10 +213,10 @@ internal abstract class QQAndroidBotBase constructor(
}
@LowLevelAPI
override fun _lowLevelNewQQ(friendInfo: FriendInfo): QQ {
return QQImpl(
override fun _lowLevelNewFriend(friendInfo: FriendInfo): Friend {
return FriendImpl(
this as QQAndroidBot,
coroutineContext + CoroutineName("QQ(${friendInfo.uin}"),
coroutineContext + CoroutineName("Friend(${friendInfo.uin}"),
friendInfo.uin,
friendInfo
)
......
......@@ -8,7 +8,7 @@
*/
@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
......@@ -16,12 +16,8 @@ import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic
import kotlinx.io.core.Closeable
import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.Friend
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
......@@ -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.utils.MiraiPlatformUtils
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.contract
import kotlin.coroutines.CoroutineContext
......@@ -49,20 +48,20 @@ internal inline class FriendInfoImpl(
}
@OptIn(ExperimentalContracts::class)
internal fun QQ.checkIsQQImpl(): QQImpl {
internal inline fun Friend.checkIsFriendImpl(): FriendImpl {
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
}
internal class QQImpl(
internal class FriendImpl(
bot: QQAndroidBot,
override val coroutineContext: CoroutineContext,
override val id: Long,
private val friendInfo: FriendInfo
) : QQ() {
) : Friend() {
@Suppress("unused") // bug
val lastMessageSequence: AtomicInt = atomic(-1)
......@@ -72,7 +71,7 @@ internal class QQImpl(
@JvmSynthetic
@Suppress("DuplicatedCode")
override suspend fun sendMessage(message: Message): MessageReceipt<QQ> {
override suspend fun sendMessage(message: Message): MessageReceipt<Friend> {
return sendMessageImpl(message).also {
logMessageSent(message)
}
......@@ -110,7 +109,7 @@ internal class QQImpl(
width = response.imageInfo.fileWidth,
resourceId = response.resourceId
).also {
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
}
is LongConn.OffPicUp.Response.RequireUpload -> {
MiraiPlatformUtils.Http.postImage(
......@@ -141,11 +140,11 @@ internal class QQImpl(
width = image.width,
resourceId = response.resourceId
).also {
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
}
}
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)
}
}
......@@ -153,35 +152,4 @@ internal class QQImpl(
} finally {
(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(
override fun newMember(memberInfo: MemberInfo): Member {
return MemberImpl(
@OptIn(LowLevelAPI::class)
bot._lowLevelNewQQ(memberInfo) as QQImpl,
bot._lowLevelNewFriend(memberInfo) as FriendImpl,
this,
this.coroutineContext,
memberInfo
......@@ -476,23 +476,5 @@ internal class GroupImpl(
(image.input as? Closeable)?.close()
}
override fun toString(): String {
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
}
override fun toString(): String = "Group($id)"
}
\ No newline at end of file
......@@ -16,10 +16,7 @@ import kotlinx.atomicfu.atomic
import kotlinx.coroutines.launch
import net.mamoe.mirai.LowLevelAPI
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.MemberCardChangeEvent
import net.mamoe.mirai.event.events.MemberLeaveEvent
......@@ -42,7 +39,7 @@ import kotlin.jvm.JvmSynthetic
@OptIn(LowLevelAPI::class)
@Suppress("MemberVisibilityCanBePrivate")
internal class MemberImpl constructor(
val qq: QQImpl, // 不要 WeakRef
val qq: FriendImpl, // 不要 WeakRef
group: GroupImpl,
override val coroutineContext: CoroutineContext,
memberInfo: MemberInfo
......@@ -52,22 +49,10 @@ internal class MemberImpl constructor(
@Suppress("unused") // false positive
val lastMessageSequence: AtomicInt = atomic(-1)
// 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()
@OptIn(MiraiInternalAPI::class)
@JvmSynthetic
@Suppress("DuplicatedCode", "UNCHECKED_CAST")
override suspend fun sendMessage(message: Message): MessageReceipt<Member> {
return sendMessageImpl(message).also {
logMessageSent(message)
......@@ -92,7 +77,6 @@ internal class MemberImpl constructor(
@JvmSynthetic
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image)
// endregion
override var permission: MemberPermission = memberInfo.permission
......@@ -217,23 +201,6 @@ internal class MemberImpl constructor(
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)
......
......@@ -10,7 +10,7 @@
package net.mamoe.mirai.qqandroid.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.events.EventCancelledException
import net.mamoe.mirai.event.events.MessageSendEvent
......@@ -31,7 +31,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.verbose
@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()
if (event.isCancelled) {
throw EventCancelledException("cancelled by FriendMessageSendEvent")
......
......@@ -12,8 +12,8 @@
package net.mamoe.mirai.qqandroid.message
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
......@@ -57,7 +57,7 @@ internal class MessageSourceFromFriendImpl(
override val random: Int get() = msg.msgBody.richText.attr!!.random
override val time: Int get() = msg.msgHead.msgTime
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) }
......
......@@ -14,9 +14,9 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.asyncFromEventOrNull
import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
import net.mamoe.mirai.message.data.MessageChain
......@@ -74,7 +74,7 @@ internal class MessageSourceToFriendImpl(
override val time: Int,
override val originalMessage: MessageChain,
override val sender: Bot,
override val target: QQ
override val target: Friend
) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal {
override val bot: Bot
get() = sender
......
......@@ -227,7 +227,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
data.friendList.forEach {
// atomic
bot.friends.delegate.addLast(
QQImpl(bot, bot.coroutineContext, it.friendUin, FriendInfoImpl(it))
FriendImpl(bot, bot.coroutineContext, it.friendUin, FriendInfoImpl(it))
).also { currentFriendCount++ }
}
logger.verbose { "正在加载好友列表 ${currentFriendCount}/${totalFriendCount}" }
......
......@@ -18,10 +18,10 @@ import kotlinx.coroutines.flow.*
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.events.BotJoinGroupEvent
......@@ -33,8 +33,8 @@ import net.mamoe.mirai.message.TempMessage
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.contact.GroupImpl
import net.mamoe.mirai.qqandroid.contact.checkIsFriendImpl
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.network.MultiPacketByIterable
import net.mamoe.mirai.qqandroid.network.Packet
......@@ -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.toByteArray
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.currentTimeSeconds
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.*
import kotlin.collections.firstOrNull
import kotlin.math.absoluteValue
import kotlin.random.Random
......@@ -191,7 +189,7 @@ internal class MessageSvc {
if (resp.result != 0) {
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
}
......@@ -231,7 +229,7 @@ internal class MessageSvc {
}
166 -> {
val friend = bot.getFriendOrNull(msg.msgHead.fromUin) ?: return@mapNotNull null
friend.checkIsQQImpl()
friend.checkIsFriendImpl()
if (msg.msgHead.fromUin == bot.id || !bot.firstLoginSucceed) {
return@mapNotNull null
......@@ -365,7 +363,7 @@ internal class MessageSvc {
inline fun createToFriend(
client: QQAndroidClient,
qq: QQ,
qq: Friend,
message: MessageChain,
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
): OutgoingPacket {
......
......@@ -409,7 +409,7 @@ internal class OnlinePush {
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
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 nick: String get() = body.msgAddFrdNotify.fuinNick
})
......
......@@ -28,8 +28,6 @@ import net.mamoe.mirai.utils.OverFileSizeMaxException
*
* 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.
*
* @author Him188moe
*/
@Suppress("INAPPLICABLE_JVM_NAME")
actual abstract class QQ : Contact(), CoroutineScope {
......
......@@ -109,20 +109,21 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
// 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] 当不存在这个好友时抛出
*/
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(
@MiraiInternalAPI
abstract val network: BotNetworkHandler
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
suspend inline fun Bot.join() = this.coroutineContext[Job]!!.join()
@PlannedRemoval("1.0.0.")
@get:JvmName("getSelfQQ")
@Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
val selfQQDeprecated: QQ
get() = selfQQ
@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)
inline fun Bot.containsGroup(id: Long): Boolean = this.groups.contains(id)
@JvmSynthetic
inline fun Bot.getFriendOrNull(id: Long): QQ? = this.friends.getOrNull(id)
inline fun Bot.getFriendOrNull(id: Long): Friend? = this.friends.getOrNull(id)
@JvmSynthetic
inline fun Bot.getGroupOrNull(id: Long): Group? = this.groups.getOrNull(id)
......@@ -32,9 +32,7 @@ import kotlin.jvm.JvmSynthetic
/**
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ], [群成员][Member] 和 [群][Group].
*
* @author Him188moe
* 联系人. 虽然叫做联系人, 但他的子类有 [人][User], 和 [群][Group].
*/
abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot {
/**
......@@ -85,23 +83,11 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot
@JvmSynthetic
abstract suspend fun uploadImage(image: ExternalImage): OfflineImage
/**
* 判断 `this` 和 [other] 是否是相同的类型, 并且 [id] 相同.
*
* 注:
* [id] 相同的 [Member] 和 [QQ], 他们并不 [equals].
* 因为, [Member] 含义为群员, 必属于一个群.
* 而 [QQ] 含义为一个独立的人, 可以是好友, 也可以是陌生人.
*/
abstract override fun equals(other: Any?): Boolean
/**
* @return `bot.hashCode() * 31 + id.hashCode()`
*/
abstract override fun hashCode(): Int
final override fun equals(other: Any?): Boolean = super.equals(other)
final override fun hashCode(): Int = super.hashCode()
/**
* @return "QQ($id)" or "Group($id)" or "Member($id)"
* @return "Friend($id)" or "Group($id)" or "Member($id)"
*/
abstract override fun toString(): String
}
......
......@@ -15,12 +15,11 @@ import net.mamoe.mirai.utils.SinceMirai
/**
* 拥有 [id] 的对象.
* 此为 [Contact] 与 [Bot] 的唯一公共接口.
* **注意:** 此接口为实验性接口, 将来可能会发生不兼容的更名.
*
* @see Contact
* @see Bot
*/
@SinceMirai("0.37.2")
@SinceMirai("0.39.0")
interface ContactOrBot {
/**
* 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
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Message
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.MiraiExperimentalAPI
import net.mamoe.mirai.utils.OverFileSizeMaxException
import net.mamoe.mirai.utils.SinceMirai
import kotlin.jvm.JvmStatic
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 无权限修改时将会抛出异常
*/
abstract var name: String
......@@ -88,6 +90,7 @@ expect abstract class Group() : Contact, CoroutineScope {
* 群头像下载链接.
*/
val avatarUrl: String
get() = "https://p.qlogo.cn/gh/$id/${id}_1/640"
/**
* 群成员列表, 不含机器人自己, 含群主.
......@@ -148,6 +151,16 @@ expect abstract class Group() : Contact, CoroutineScope {
@JvmSynthetic
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 {
abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage
companion object {
// don't @JvmStatic: JDK 1.8 required
fun calculateGroupUinByGroupCode(groupCode: Long): Long
fun calculateGroupCodeByGroupUin(groupUin: Long): Long
/**
* @suppress internal api
*/
@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
import net.mamoe.mirai.Bot
import net.mamoe.mirai.JavaFriendlyAPI
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.getFriendOrNull
import net.mamoe.mirai.message.MessageReceipt
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.WeakRefProperty
import kotlin.jvm.JvmSynthetic
......@@ -24,10 +26,13 @@ import kotlin.time.ExperimentalTime
/**
* 群成员.
*/ // 不要删除多平台结构, kotlin bug
*
* 群成员可能也是好友, 但他们在对象类型上不同.
* 群成员可以通过 [asFriend] 得到相关好友对象.
*/
@Suppress("INAPPLICABLE_JVM_NAME")
@OptIn(MiraiInternalAPI::class, JavaFriendlyAPI::class)
expect abstract class Member() : MemberJavaFriendlyAPI {
abstract class Member : MemberJavaFriendlyAPI() {
/**
* 所在的群.
*/
......@@ -119,16 +124,6 @@ expect abstract class Member() : MemberJavaFriendlyAPI {
@JvmSynthetic
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 {
*/
@JvmSynthetic
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 {
return muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt()
}
/**
* @see Member.mute
*/
@ExperimentalTime
suspend inline fun Member.mute(duration: Duration) {
require(duration.inDays <= 30) { "duration must be at most 1 month" }
......@@ -168,4 +203,7 @@ suspend inline fun Member.mute(duration: Duration) {
this.mute(duration.inSeconds.toInt())
}
/**
* @see Member.mute
*/
suspend inline fun Member.mute(durationSeconds: Long) = this.mute(durationSeconds.toInt())
\ No newline at end of file
......@@ -12,10 +12,8 @@
package net.mamoe.mirai.contact
import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
/**
* 群成员的权限.
*
......@@ -37,39 +35,42 @@ enum class MemberPermission : Comparable<MemberPermission> {
*/
OWNER; // ordinal = 2
/**
* 权限等级. [OWNER] 为 2, [ADMINISTRATOR] 为 1, [MEMBER] 为 0
*/
@SinceMirai("0.32.0")
val level: Int
get() = ordinal
}
/**
* 群主
* 判断权限是否为群主
*/
inline fun MemberPermission.isOwner(): Boolean = this == MemberPermission.OWNER
/**
* 管理员
* 判断权限是否为管理员
*/
inline fun MemberPermission.isAdministrator(): Boolean = this == MemberPermission.ADMINISTRATOR
/**
* 管理员或群主
* 判断权限是否为管理员或群主
*/
inline fun MemberPermission.isOperator(): Boolean = isAdministrator() || isOwner()
/**
* 群主
* 判断权限是否为群主
*/
inline fun Member.isOwner(): Boolean = this.permission.isOwner()
/**
* 管理员
* 判断权限是否为管理员
*/
inline fun Member.isAdministrator(): Boolean = this.permission.isAdministrator()
/**
* 管理员或群主
* 判断权限是否为管理员或群主
*/
inline fun Member.isOperator(): Boolean = this.permission.isOperator()
......@@ -77,9 +78,10 @@ inline fun Member.isOperator(): Boolean = this.permission.isOperator()
/**
* 权限不足
*/
expect class PermissionDeniedException : IllegalStateException {
constructor()
constructor(message: String?)
@Suppress("unused")
class PermissionDeniedException : IllegalStateException {
constructor() : super("Permission denied")
constructor(message: String?) : super(message)
}
/**
......@@ -87,10 +89,9 @@ expect class PermissionDeniedException : IllegalStateException {
*
* @throws PermissionDeniedException
*/
@OptIn(MiraiExperimentalAPI::class)
inline fun Group.checkBotPermission(
required: MemberPermission,
lazyMessage: () -> String = {
crossinline lazyMessage: () -> String = {
"Permission denied: required $required, got actual $botPermission for $bot in group $id"
}
) {
......@@ -104,9 +105,8 @@ inline fun Group.checkBotPermission(
*
* @throws PermissionDeniedException
*/
@OptIn(MiraiExperimentalAPI::class)
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"
}
) {
......
......@@ -13,76 +13,47 @@ package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope
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.ImageUploadEvent
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.OfflineFriendImage
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.OverFileSizeMaxException
import net.mamoe.mirai.utils.PlannedRemoval
import kotlin.jvm.JvmSynthetic
/**
* 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.
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
* 所有 API 均有二进制兼容.
*
* @author Him188moe
* 请根据实际情况, 使用 [Friend] 或 [User] 替代.
*/
@Suppress("INAPPLICABLE_JVM_NAME")
expect abstract class QQ() : Contact, CoroutineScope {
/**
* 请求头像下载链接
*/
// @MiraiExperimentalAPI
//suspend fun queryAvatar(): AvatarLink
@PlannedRemoval("1.0.0")
@Deprecated(
"use Friend or Person instead",
replaceWith = ReplaceWith("Friend", "net.mamoe.mirai.contact.Friend"),
level = DeprecationLevel.ERROR
)
@Suppress("DEPRECATION_ERROR")
abstract class QQ : User(), CoroutineScope {
/**
* QQ 号码
*/
abstract override val id: Long
/**
* 昵称
*/
abstract val nick: String
/**
* 查询用户资料
* 昵称
*/
@MiraiExperimentalAPI("还未支持")
abstract suspend fun queryProfile(): Profile
abstract override val nick: String
/**
* 头像下载链接
*/
val avatarUrl: String
/**
* 查询曾用名.
*
* 曾用名可能是:
* - 昵称
* - 共同群内的群名片
*/
@MiraiExperimentalAPI("还未支持")
abstract suspend fun queryPreviousNameList(): PreviousNameList
/**
* 查询机器人账号给这个人设置的备注
*/
@MiraiExperimentalAPI("还未支持")
abstract suspend fun queryRemark(): FriendNameRemark
override val avatarUrl: String
get() = "http://q1.qlogo.cn/g?b=qq&nk=$id&s=640"
/**
* 向这个对象发送消息.
......@@ -100,16 +71,4 @@ expect abstract class QQ() : Contact, CoroutineScope {
*/
@JvmSynthetic
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
import kotlinx.coroutines.CoroutineScope
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.ImageUploadEvent
......@@ -15,66 +21,38 @@ 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.OfflineFriendImage
import net.mamoe.mirai.message.data.toMessage
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
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.
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
* 注意: 一个 [User] 实例并不是独立的, 它属于一个 [Bot].
*
* @author Him188moe
* 对于同一个 [Bot] 任何一个人的 [User] 实例都是单一的.
*/
@Suppress("INAPPLICABLE_JVM_NAME")
actual abstract class QQ : Contact(), CoroutineScope {
/**
* 请求头像下载链接
*/
// @MiraiExperimentalAPI
//suspend fun queryAvatar(): AvatarLink
abstract class User : Contact(), CoroutineScope {
/**
* QQ 号码
*/
actual abstract override val id: Long
/**
* 昵称
*/
actual abstract val nick: String
abstract override val id: Long
/**
* 查询用户资料
* 昵称
*/
@MiraiExperimentalAPI("还未支持")
actual abstract suspend fun queryProfile(): Profile
abstract val nick: String
/**
* 头像下载链接
*/
actual val avatarUrl: String
open val avatarUrl: String
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 {
* @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
*/
@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 {
* @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
*/
@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
import net.mamoe.mirai.utils.MiraiInternalAPI
/**
* [Contact] 中为了让 `Java` 更容易调用的 API
* [Contact] 中为了让 `Java` 更容易调用的 API.
* 不要用它作为一个类型, 只应使用其中的方法
*/
@MiraiInternalAPI
@JavaFriendlyAPI
expect abstract class ContactJavaFriendlyAPI()
expect abstract class ContactJavaFriendlyAPI internal constructor()
/**
* [Member] 中为了让 `Java` 更容易调用的 API
* 不要用它作为一个类型, 只应使用其中的方法
*/
@Suppress("DEPRECATION_ERROR")
@MiraiInternalAPI
@JavaFriendlyAPI
expect abstract class MemberJavaFriendlyAPI : QQ
\ No newline at end of file
expect abstract class MemberJavaFriendlyAPI internal constructor() : QQ // 将来会改为 User
\ No newline at end of file
......@@ -241,7 +241,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
/** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
@MessageDsl
fun sentBy(friend: QQ): ListeningFilter = content { sender.id == friend.id }
fun sentBy(friend: User): ListeningFilter = content { sender.id == friend.id }
/** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
@MessageDsl
......@@ -294,7 +294,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
/** [消息内容][Message.contentToString]包含目标为 [target] 的 [At] */
@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] */
@MessageDsl
......
......@@ -102,7 +102,7 @@ sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractCancellableEve
) : MessageSendEvent(), CancellableEvent
data class FriendMessageSendEvent(
override val target: QQ,
override val target: Friend,
var message: MessageChain
) : MessageSendEvent(), CancellableEvent
}
......@@ -539,9 +539,17 @@ data class MemberUnmuteEvent(
@SinceMirai("0.36.0")
data class FriendRemarkChangeEvent(
override val bot: Bot,
val friend: QQ,
val friend: Friend,
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(
/**
* 新好友. 已经添加到 [Bot.friends]
*/
val friend: QQ
val friend: Friend
) : BotEvent, Packet {
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(
*/
@SinceMirai("0.36.0")
data class FriendDeleteEvent(
val friend: QQ
val friend: Friend
) : BotEvent, Packet {
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 @@
package net.mamoe.mirai.event.events
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
/**
* 有关一个 [Bot] 的事件
......@@ -85,7 +87,14 @@ val GroupOperableEvent.operatorOrBot: Member
* 有关好友的事件
*/
interface FriendEvent : BotEvent {
val friend: QQ
val friend: Friend
override val bot: 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 @@
package net.mamoe.mirai
import kotlinx.coroutines.Job
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.*
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
......@@ -37,13 +37,13 @@ annotation class LowLevelAPI
@LowLevelAPI
interface LowLevelBotAPIAccessor {
/**
* 构造一个 [_lowLevelNewQQ] 对象. 它持有对 [Bot] 的弱引用([WeakRef]).
* 构造一个 [Friend] 对象. 它持有对 [Bot] 的弱引用([WeakRef]).
*
* [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job.
* 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭.
*/
@LowLevelAPI
fun _lowLevelNewQQ(friendInfo: FriendInfo): QQ
fun _lowLevelNewFriend(friendInfo: FriendInfo): Friend
/**
* 向服务器查询群列表. 返回值高 32 bits 为 uin, 低 32 bits 为 groupCode
......
......@@ -23,10 +23,7 @@ import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.selectMessages
import net.mamoe.mirai.event.syncFromEvent
......@@ -52,7 +49,7 @@ import kotlin.jvm.JvmSynthetic
*/
@Suppress("DEPRECATION")
@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
message = "use 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>
/**
......@@ -74,7 +71,7 @@ expect abstract class MessagePacket<TSender : QQ, TSubject : Contact> constructo
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
@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
*/
abstract val message: MessageChain
/**
* 消息源
*/
open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
// region 发送 Message
......@@ -118,18 +119,11 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
suspend inline fun reply(plain: String): MessageReceipt<TSubject> =
subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject>
// endregion
// region 撤回
// endregion
// region 上传图片
// region 图片
suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
// endregion
// region 发送图片
suspend inline fun ExternalImage.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
@JvmName("reply2")
suspend inline fun MessageChain.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
// 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]
}
/**
* 创建 @ 这个账号的消息. 当且仅当消息为群消息时可用. 否则将会抛出 [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
// endregion
......@@ -202,13 +186,40 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
// 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")
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")
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 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("DEPRECATION_ERROR")
package net.mamoe.mirai.message
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.BroadcastControllable
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.message.data.source
import net.mamoe.mirai.utils.PlannedRemoval
import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.unsafeWeakRef
......@@ -23,17 +27,21 @@ import net.mamoe.mirai.utils.unsafeWeakRef
* 好友消息
*/
class FriendMessage(
sender: QQ,
sender: Friend,
override val message: MessageChain
) : ContactMessage(), BroadcastControllable {
@PlannedRemoval("1.0.0")
@Deprecated("", level = DeprecationLevel.HIDDEN)
constructor(sender: QQ, message: MessageChain) : this(sender as Friend, message)
init {
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" }
}
override val sender: QQ by sender.unsafeWeakRef()
override val sender: Friend by sender.unsafeWeakRef()
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 fun toString(): String = "FriendMessage(sender=${sender.id}, message=$message)"
......
......@@ -14,10 +14,7 @@ import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.message.data.source
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.unsafeWeakRef
......@@ -44,7 +41,7 @@ class GroupMessage(
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 =
"GroupMessage(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)"
......
......@@ -116,14 +116,12 @@ sealed class OnlineMessageSource : MessageSource() {
* 消息发送人. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群员][Member].
* 即类型必定为 [Bot], [QQ] 或 [Member]
*/
@ExperimentalIdentification
abstract val sender: ContactOrBot
/**
* 消息发送目标. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群][Group].
* 即类型必定为 [Bot], [QQ] 或 [Group]
*/
@ExperimentalIdentification
abstract val target: ContactOrBot
/**
......@@ -135,7 +133,6 @@ sealed class OnlineMessageSource : MessageSource() {
/**
* 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource]
*/
@OptIn(ExperimentalIdentification::class)
sealed class Outgoing : OnlineMessageSource() {
companion object Key : Message.Key<Outgoing> {
override val typeName: String get() = "OnlineMessageSource.Outgoing"
......@@ -152,8 +149,8 @@ sealed class OnlineMessageSource : MessageSource() {
override val typeName: String get() = "OnlineMessageSource.Outgoing.ToFriend"
}
abstract override val target: QQ
final override val subject: QQ get() = target
abstract override val target: Friend
final override val subject: Friend get() = target
// final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.id})"
}
......@@ -181,13 +178,12 @@ sealed class OnlineMessageSource : MessageSource() {
/**
* 接收到的一条消息的 [MessageSource]
*/
@OptIn(ExperimentalIdentification::class)
sealed class Incoming : OnlineMessageSource() {
companion object Key : Message.Key<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 targetId: Long get() = target.id
......@@ -197,8 +193,8 @@ sealed class OnlineMessageSource : MessageSource() {
override val typeName: String get() = "OnlineMessageSource.Incoming.FromFriend"
}
abstract override val sender: QQ
final override val subject: QQ get() = sender
abstract override val sender: Friend
final override val subject: Friend get() = sender
final override val target: Bot get() = sender.bot
// final override fun toString(): String = "OnlineMessageSource.FromFriend(from=${sender.id})"
}
......@@ -243,7 +239,6 @@ sealed class OnlineMessageSource : MessageSource() {
@Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
@get:JvmName("target")
@get:JvmSynthetic
@OptIn(ExperimentalIdentification::class)
open val target2: Any
get() = target
......@@ -251,7 +246,6 @@ sealed class OnlineMessageSource : MessageSource() {
@Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
@get:JvmName("sender")
@get:JvmSynthetic
@OptIn(ExperimentalIdentification::class)
open val sender2: Any
get() = sender
}
......
......@@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("EXPERIMENTAL_API_USAGE")
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused")
package net.mamoe.mirai.utils
......@@ -18,7 +18,7 @@ import kotlinx.io.core.Input
import kotlinx.serialization.InternalSerializationApi
import net.mamoe.mirai.contact.Contact
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.data.Image
import net.mamoe.mirai.message.data.OfflineImage
......@@ -153,7 +153,7 @@ class ExternalImage private constructor(
@JvmSynthetic
suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> = when (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")
}
......@@ -166,7 +166,7 @@ suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> =
@JvmSynthetic
suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact) {
is Group -> contact.uploadImage(this)
is QQ -> contact.uploadImage(this)
is User -> contact.uploadImage(this)
else -> error("unreachable")
}
......
package net.mamoe.mirai
import kotlinx.coroutines.*
import net.mamoe.mirai.contact.PermissionDeniedException
import net.mamoe.mirai.contact.recall
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.message.MessageReceipt
......
......@@ -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
@JavaFriendlyAPI
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
import kotlinx.io.core.Output
import kotlinx.io.core.use
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.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
......@@ -38,7 +39,7 @@ import java.net.URL
replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
)
@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>() {
// region 上传图片
@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