[mock] Unified contacts data

This commit is contained in:
Karlatemp 2022-10-03 12:38:58 +08:00
parent 495542d2a2
commit cae7b1161c
No known key found for this signature in database
GPG Key ID: BA173CA2B9956C59
6 changed files with 137 additions and 86 deletions

View File

@ -23,7 +23,9 @@ import net.mamoe.mirai.contact.friendgroup.FriendGroups
import net.mamoe.mirai.event.EventChannel
import net.mamoe.mirai.event.GlobalEventChannel
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.events.BotOnlineEvent
import net.mamoe.mirai.event.events.BotReloginEvent
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
import net.mamoe.mirai.internal.network.components.EventDispatcher
@ -37,15 +39,16 @@ import net.mamoe.mirai.mock.database.MessageDatabase
import net.mamoe.mirai.mock.internal.components.MockEventDispatcherImpl
import net.mamoe.mirai.mock.internal.contact.*
import net.mamoe.mirai.mock.internal.contact.friendfroup.MockFriendGroups
import net.mamoe.mirai.mock.internal.contactbase.ContactDatabase
import net.mamoe.mirai.mock.internal.serverfs.TmpResourceServerImpl
import net.mamoe.mirai.mock.resserver.TmpResourceServer
import net.mamoe.mirai.mock.userprofile.UserProfileService
import net.mamoe.mirai.mock.utils.NameGenerator
import net.mamoe.mirai.mock.utils.broadcastBlocking
import net.mamoe.mirai.mock.utils.simpleMemberInfo
import net.mamoe.mirai.utils.*
import java.util.concurrent.CancellationException
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
import net.mamoe.mirai.internal.utils.subLoggerImpl as subLog
@ -58,22 +61,22 @@ internal class MockBotImpl(
override val msgDatabase: MessageDatabase,
override val userProfileService: UserProfileService,
) : MockBot, Bot, ContactOrBot {
@JvmField
internal val contactDatabase = ContactDatabase(this)
private val botccinfo = contactDatabase.acquireCI(id, nick)
private val loginBefore = AtomicBoolean(false)
override var nickNoEvent: String = nick
override var nickNoEvent: String by botccinfo::nick
override var nick: String
get() = nickNoEvent
get() = botccinfo.nick
set(value) {
val ov = nickNoEvent
if (value == ov) return
nickNoEvent = value
BotNickChangedEvent(this, ov, value).broadcastBlocking()
botccinfo.changeNick(value)
}
override var avatarUrl: String
get() = asFriend.avatarUrl
set(value) {
asFriend.mockApi.avatarUrl = value
BotAvatarChangedEvent(this).broadcastBlocking()
asFriend.changeAvatarUrl(value)
}
override fun avatarUrl(spec: AvatarSpec): String {
@ -186,4 +189,9 @@ internal class MockBotImpl(
override fun toString(): String {
return "MockBot($id)"
}
}
}
internal fun MockBot.impl(): MockBotImpl {
contract { returns() implies (this@impl is MockBotImpl) }
return cast()
}

View File

@ -12,7 +12,6 @@
package net.mamoe.mirai.mock.internal.contact
import kotlinx.coroutines.cancel
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.contact.AvatarSpec
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.friendgroup.FriendGroup
@ -28,13 +27,13 @@ import net.mamoe.mirai.mock.MockBot
import net.mamoe.mirai.mock.contact.MockFriend
import net.mamoe.mirai.mock.internal.contact.friendfroup.MockFriendGroups
import net.mamoe.mirai.mock.internal.contact.roaming.MockRoamingMessages
import net.mamoe.mirai.mock.internal.impl
import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcFromFriend
import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcToFriend
import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc
import net.mamoe.mirai.mock.utils.broadcastBlocking
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.lateinitMutableProperty
import java.util.concurrent.CancellationException
import kotlin.coroutines.CoroutineContext
@ -48,33 +47,15 @@ internal class MockFriendImpl(
parentCoroutineContext,
bot, id
), MockFriend {
private val ccinfo = bot.impl().contactDatabase.acquireCI(id, nick)
override val mockApi: MockFriend.MockApi = object : MockFriend.MockApi {
override val contact: MockFriend get() = this@MockFriendImpl
override var nick: String = nick
override var remark: String = remark
override var avatarUrl: String
get() = this@MockFriendImpl._avatarUrl
set(value) {
this@MockFriendImpl._avatarUrl = value
bot.groups.forEach { g ->
val mems = if (this@MockFriendImpl.id == bot.id) {
sequenceOf(g.botAsMember)
} else g.members.asSequence().filter {
it.id == this@MockFriendImpl.id
}
mems.forEach { m ->
m.cast<MockNormalMemberImpl>().avatarUrl = value
}
}
if (this@MockFriendImpl.id == bot.id) {
sequenceOf(bot.asStranger)
} else {
bot.strangers.asSequence().filter { s ->
s.id == this@MockFriendImpl.id
}
}.forEach { it.cast<MockStrangerImpl>().avatarUrl = value }
}
override var nick: String by ccinfo::nick
override var avatarUrl: String by ccinfo::avatarUrl
override var friendGroupId: Int = 0
}
@ -82,15 +63,13 @@ internal class MockFriendImpl(
override val friendGroup: FriendGroup
get() = bot.friendGroups.cast<MockFriendGroups>().findOrDefault(mockApi.friendGroupId)
private var _avatarUrl: String by lateinitMutableProperty { runBlocking { MockImage.random(bot).getUrl(bot) } }
override val avatarUrl: String get() = _avatarUrl
override val avatarUrl: String get() = ccinfo.avatarUrl
internal fun initAvatarUrl(v: String) {
_avatarUrl = v
ccinfo.avatarUrl = v
}
override fun changeAvatarUrl(newAvatar: String) {
mockApi.avatarUrl = newAvatar
FriendAvatarChangedEvent(this).broadcastBlocking()
ccinfo.changeAvatarUrl(newAvatar)
}
override fun avatarUrl(spec: AvatarSpec): String {
@ -98,12 +77,9 @@ internal class MockFriendImpl(
}
override var nick: String
get() = mockApi.nick
get() = ccinfo.nick
set(value) {
val ov = mockApi.nick
if (ov == value) return
mockApi.nick = value
FriendNickChangedEvent(this, ov, value).broadcastBlocking()
ccinfo.changeNick(value)
}
override var remark: String

View File

@ -10,7 +10,6 @@
package net.mamoe.mirai.mock.internal.contact
import kotlinx.coroutines.cancel
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
@ -24,13 +23,13 @@ import net.mamoe.mirai.mock.contact.MockGroup
import net.mamoe.mirai.mock.contact.MockNormalMember
import net.mamoe.mirai.mock.contact.active.MockMemberActive
import net.mamoe.mirai.mock.internal.contact.active.MockMemberActiveImpl
import net.mamoe.mirai.mock.internal.impl
import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcFromGroup
import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcToTemp
import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc
import net.mamoe.mirai.mock.utils.broadcastBlocking
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.currentTimeSeconds
import net.mamoe.mirai.utils.lateinitMutableProperty
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.max
@ -52,18 +51,19 @@ internal class MockNormalMemberImpl(
parentCoroutineContext, bot,
id
), MockNormalMember {
override var avatarUrl: String by lateinitMutableProperty {
bot.getFriend(id)?.let { return@lateinitMutableProperty it.avatarUrl }
runBlocking { MockImage.random(bot).getUrl(bot) }
private val ccinfo = bot.impl().contactDatabase.let {
if (nick.isEmpty()) it.acquireCI(id)
else it.acquireCI(id, nick)
}
override val avatarUrl: String get() = ccinfo.avatarUrl
override fun avatarUrl(spec: AvatarSpec): String {
return avatarUrl
}
override fun changeAvatarUrl(newAvatar: String) {
bot.getFriend(id)?.let { return it.changeAvatarUrl(newAvatar) }
this.avatarUrl = newAvatar
ccinfo.changeAvatarUrl(newAvatar)
}
private inline fun <T> crossFriendAccess(
@ -80,11 +80,7 @@ internal class MockNormalMemberImpl(
override var joinTimestamp: Int = joinTimestamp
override var muteTimeEndTimestamp: Long = currentTimeSeconds() + muteTimeRemaining
override var nick: String = nick
get() = crossFriendAccess(ifExists = { it.nick }, ifNotExists = { field })
set(value) {
crossFriendAccess(ifExists = { it.mockApi.nick = value }, ifNotExists = { field = value })
}
override var nick: String by ccinfo::nick
override var remark: String = remark
get() = crossFriendAccess(ifExists = { it.remark }, ifNotExists = { field })
@ -95,15 +91,7 @@ internal class MockNormalMemberImpl(
override var permission: MemberPermission = permission
override var nameCard: String = nameCard
override var specialTitle: String = specialTitle
override var avatarUrl: String
get() = this@MockNormalMemberImpl.avatarUrl
set(value) {
this@MockNormalMemberImpl.avatarUrl = value
bot.getFriend(this@MockNormalMemberImpl.id)?.let { f ->
f.mockApi.avatarUrl = value
}
}
override var avatarUrl: String by ccinfo::avatarUrl
}
override val permission: MemberPermission

View File

@ -10,7 +10,6 @@
package net.mamoe.mirai.mock.internal.contact
import kotlinx.coroutines.cancel
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.contact.AvatarSpec
import net.mamoe.mirai.contact.Stranger
import net.mamoe.mirai.event.broadcast
@ -21,11 +20,11 @@ import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.mock.MockBot
import net.mamoe.mirai.mock.contact.MockStranger
import net.mamoe.mirai.mock.internal.impl
import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcFromStranger
import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcToStranger
import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.lateinitMutableProperty
import java.util.concurrent.CancellationException
import kotlin.coroutines.CoroutineContext
@ -37,34 +36,23 @@ internal class MockStrangerImpl(
remark: String,
nick: String
) : AbstractMockContact(parentCoroutineContext, bot, id), MockStranger {
private val ccinfo = bot.impl().contactDatabase.acquireCI(id, nick)
override val mockApi: MockStranger.MockApi = object : MockStranger.MockApi {
override val contact: MockStranger get() = this@MockStrangerImpl
override var nick: String = nick
override var nick: String by ccinfo::nick
override var remark: String = remark
override var avatarUrl: String
get() = this@MockStrangerImpl.avatarUrl
set(value) {
this@MockStrangerImpl.avatarUrl = value
override var avatarUrl: String by ccinfo::avatarUrl
}
bot.getFriend(this@MockStrangerImpl.id)?.let { f ->
f.mockApi.avatarUrl = value
return
}
}
}
override var avatarUrl: String by lateinitMutableProperty {
bot.getFriend(id)?.let { return@lateinitMutableProperty it.avatarUrl }
runBlocking { MockImage.random(bot).getUrl(bot) }
}
override val avatarUrl: String get() = ccinfo.avatarUrl
override fun avatarUrl(spec: AvatarSpec): String {
return avatarUrl
}
override fun changeAvatarUrl(newAvatar: String) {
this.avatarUrl = newAvatar
bot.getFriend(id)?.let { return it.changeAvatarUrl(newAvatar) }
ccinfo.changeAvatarUrl(newAvatar)
}
override val nick: String

View File

@ -0,0 +1,33 @@
/*
* Copyright 2019-2022 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/dev/LICENSE
*/
package net.mamoe.mirai.mock.internal.contactbase
import net.mamoe.mirai.mock.internal.MockBotImpl
import net.mamoe.mirai.utils.ConcurrentHashMap
internal class ContactDatabase(
private val bot: MockBotImpl,
) {
val contacts = ConcurrentHashMap<Long, ContactInfo>()
fun acquireCI(id: Long): ContactInfo {
return contacts.computeIfAbsent(id) {
ContactInfo(bot, id, bot.nameGenerator.nextFriendName())
}
}
fun acquireCI(id: Long, name: String): ContactInfo {
return contacts.computeIfAbsent(id) {
ContactInfo(bot, id, name)
}.also { rsp ->
if (rsp.nick != name) rsp.changeNick(name)
}
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019-2022 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/dev/LICENSE
*/
package net.mamoe.mirai.mock.internal.contactbase
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.event.events.BotAvatarChangedEvent
import net.mamoe.mirai.event.events.BotNickChangedEvent
import net.mamoe.mirai.event.events.FriendAvatarChangedEvent
import net.mamoe.mirai.event.events.FriendNickChangedEvent
import net.mamoe.mirai.mock.internal.MockBotImpl
import net.mamoe.mirai.mock.internal.contact.MockImage
import net.mamoe.mirai.mock.utils.broadcastBlocking
import net.mamoe.mirai.utils.lateinitMutableProperty
internal class ContactInfo(
private val declaredBot: MockBotImpl,
@JvmField val id: Long,
@JvmField var nick: String,
) {
var avatarUrl: String by lateinitMutableProperty {
runBlocking { MockImage.random(declaredBot).getUrl(declaredBot) }
}
fun changeAvatarUrl(newAvatar: String) {
avatarUrl = newAvatar
if (declaredBot.id == id) {
BotAvatarChangedEvent(declaredBot).broadcastBlocking()
return
}
declaredBot.getFriend(id)?.let {
FriendAvatarChangedEvent(it).broadcastBlocking()
}
}
fun changeNick(newNick: String) {
if (id == declaredBot.id) {
val o = nick
nick = newNick
BotNickChangedEvent(declaredBot, o, newNick).broadcastBlocking()
return
}
val friend = declaredBot.getFriend(id)
if (friend == null) {
nick = newNick
return
}
val o = nick
nick = newNick
FriendNickChangedEvent(friend, o, newNick).broadcastBlocking()
}
}