MockGroupImpl.kt 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*
  2. * Copyright 2019-2022 Mamoe Technologies and contributors.
  3. *
  4. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  5. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
  6. *
  7. * https://github.com/mamoe/mirai/blob/dev/LICENSE
  8. */
  9. @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
  10. package net.mamoe.mirai.mock.internal.contact
  11. import kotlinx.coroutines.cancel
  12. import kotlinx.coroutines.runBlocking
  13. import net.mamoe.mirai.contact.*
  14. import net.mamoe.mirai.contact.announcement.OfflineAnnouncement
  15. import net.mamoe.mirai.contact.announcement.buildAnnouncementParameters
  16. import net.mamoe.mirai.contact.file.RemoteFiles
  17. import net.mamoe.mirai.contact.roaming.RoamingMessages
  18. import net.mamoe.mirai.data.GroupHonorType
  19. import net.mamoe.mirai.data.MemberInfo
  20. import net.mamoe.mirai.event.broadcast
  21. import net.mamoe.mirai.event.events.*
  22. import net.mamoe.mirai.internal.contact.uin
  23. import net.mamoe.mirai.message.MessageReceipt
  24. import net.mamoe.mirai.message.data.*
  25. import net.mamoe.mirai.mock.MockBot
  26. import net.mamoe.mirai.mock.contact.MockAnonymousMember
  27. import net.mamoe.mirai.mock.contact.MockGroup
  28. import net.mamoe.mirai.mock.contact.MockGroupControlPane
  29. import net.mamoe.mirai.mock.contact.MockNormalMember
  30. import net.mamoe.mirai.mock.contact.active.MockGroupActive
  31. import net.mamoe.mirai.mock.contact.essence.MockEssences
  32. import net.mamoe.mirai.mock.internal.contact.active.MockGroupActiveImpl
  33. import net.mamoe.mirai.mock.internal.contact.essence.MockEssencesImpl
  34. import net.mamoe.mirai.mock.internal.contact.roaming.MockRoamingMessages
  35. import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcToGroup
  36. import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc
  37. import net.mamoe.mirai.mock.utils.broadcastBlocking
  38. import net.mamoe.mirai.mock.utils.mock
  39. import net.mamoe.mirai.utils.*
  40. import java.util.concurrent.CancellationException
  41. import kotlin.coroutines.CoroutineContext
  42. internal class MockGroupImpl(
  43. parentCoroutineContext: CoroutineContext,
  44. bot: MockBot,
  45. id: Long,
  46. override val uin: Long,
  47. name: String,
  48. ) : AbstractMockContact(
  49. parentCoroutineContext, bot, id
  50. ), MockGroup {
  51. @Deprecated(
  52. "use active.changeHonorMember",
  53. replaceWith = ReplaceWith(".active.changeHonorMember(member, honorType)"),
  54. level = DeprecationLevel.ERROR
  55. )
  56. override val honorMembers: MutableMap<GroupHonorType, MockNormalMember> = ConcurrentHashMap()
  57. private val txFileSystem by lazy { bot.mock().tmpResourceServer.mockServerFileDisk.newFsSystem() }
  58. override fun avatarUrl(spec: AvatarSpec): String {
  59. return avatarUrl
  60. }
  61. override val active: MockGroupActive by lazy { MockGroupActiveImpl(this) }
  62. override fun appendMember(mockMember: MemberInfo): MockGroup {
  63. addMember(mockMember)
  64. return this
  65. }
  66. override fun addMember(mockMember: MemberInfo): MockNormalMember {
  67. val nMember = MockNormalMemberImpl(
  68. this.coroutineContext,
  69. bot,
  70. mockMember.uin,
  71. this,
  72. mockMember.permission,
  73. mockMember.remark,
  74. mockMember.nick,
  75. mockMember.muteTimestamp,
  76. mockMember.joinTimestamp,
  77. mockMember.lastSpeakTimestamp,
  78. mockMember.specialTitle,
  79. mockMember.nameCard
  80. )
  81. if (nMember.id == bot.id) {
  82. botAsMember = nMember
  83. } else {
  84. members.delegate.removeAll { it.uin == nMember.id }
  85. members.delegate.add(nMember)
  86. }
  87. if (nMember.permission == MemberPermission.OWNER) {
  88. if (::owner.isInitialized) {
  89. owner.mock().mockApi.permission = MemberPermission.MEMBER
  90. }
  91. owner = nMember
  92. }
  93. return nMember
  94. }
  95. override suspend fun changeOwner(member: NormalMember) {
  96. if (member === owner) return
  97. val oldOwner = owner
  98. val oldPerm = member.permission
  99. oldOwner.mock().mockApi.permission = MemberPermission.MEMBER
  100. member.mock().mockApi.permission = MemberPermission.OWNER
  101. owner = member
  102. if (member === botAsMember) {
  103. BotGroupPermissionChangeEvent(this, oldPerm, MemberPermission.OWNER)
  104. } else {
  105. MemberPermissionChangeEvent(member, oldPerm, MemberPermission.OWNER)
  106. }.broadcast()
  107. if (oldOwner === botAsMember) {
  108. BotGroupPermissionChangeEvent(this, MemberPermission.OWNER, MemberPermission.MEMBER)
  109. } else {
  110. MemberPermissionChangeEvent(oldOwner, MemberPermission.OWNER, MemberPermission.MEMBER)
  111. }.broadcast()
  112. }
  113. override fun changeOwnerNoEventBroadcast(member: NormalMember) {
  114. val oldOwner = owner
  115. member.mock().mockApi.permission = MemberPermission.OWNER
  116. oldOwner.mockApi.permission = MemberPermission.MEMBER
  117. owner = member
  118. }
  119. override fun newAnonymous(nick: String, id: String): MockAnonymousMember {
  120. return MockAnonymousMemberImpl(
  121. coroutineContext, bot, 80000000, id, this, nick
  122. )
  123. }
  124. private val rawGroupControlPane = object : MockGroupControlPane {
  125. override val group: MockGroup get() = this@MockGroupImpl
  126. override val currentActor: MockNormalMember get() = group.botAsMember
  127. override var isAllowMemberInvite: Boolean = false
  128. override var isMuteAll: Boolean = false
  129. override var isAllowMemberFileUploading: Boolean = false
  130. override var isAnonymousChatAllowed: Boolean = false
  131. override var isAllowConfessTalk: Boolean = false
  132. override var groupName: String = name
  133. override fun withActor(actor: MockNormalMember): MockGroupControlPane {
  134. return GroupControlPaneImpl(actor)
  135. }
  136. }
  137. internal inner class GroupControlPaneImpl(
  138. override val currentActor: MockNormalMember
  139. ) : MockGroupControlPane {
  140. override val group: MockGroup get() = this@MockGroupImpl
  141. private val actorNullIfBot: MockNormalMember?
  142. get() = currentActor.takeIf { it.id != bot.id }
  143. override var groupName: String
  144. get() = rawGroupControlPane.groupName
  145. set(value) {
  146. val ov = rawGroupControlPane.groupName
  147. if (ov == value) return
  148. rawGroupControlPane.groupName = value
  149. GroupNameChangeEvent(ov, value, group, actorNullIfBot).broadcastBlocking()
  150. }
  151. override var isMuteAll: Boolean
  152. get() = rawGroupControlPane.isMuteAll
  153. set(value) {
  154. val ov = rawGroupControlPane.isMuteAll
  155. if (ov == value) return
  156. rawGroupControlPane.isMuteAll = value
  157. GroupMuteAllEvent(ov, value, group, actorNullIfBot).broadcastBlocking()
  158. }
  159. override var isAllowMemberFileUploading: Boolean
  160. get() = rawGroupControlPane.isAllowMemberFileUploading
  161. set(value) {
  162. // TODO: core-api no event
  163. rawGroupControlPane.isAllowMemberFileUploading = value
  164. }
  165. override var isAllowMemberInvite: Boolean
  166. get() = rawGroupControlPane.isAllowMemberInvite
  167. set(value) {
  168. val ov = rawGroupControlPane.isAllowMemberInvite
  169. if (ov == value) return
  170. rawGroupControlPane.isAllowMemberInvite = value
  171. GroupAllowMemberInviteEvent(ov, value, group, actorNullIfBot).broadcastBlocking()
  172. }
  173. override var isAnonymousChatAllowed: Boolean
  174. get() = rawGroupControlPane.isAnonymousChatAllowed
  175. set(value) {
  176. val ov = rawGroupControlPane.isAnonymousChatAllowed
  177. if (ov == value) return
  178. rawGroupControlPane.isAnonymousChatAllowed = value
  179. GroupAllowAnonymousChatEvent(ov, value, group, actorNullIfBot).broadcastBlocking()
  180. }
  181. override var isAllowConfessTalk: Boolean
  182. get() = rawGroupControlPane.isAllowConfessTalk
  183. set(value) {
  184. val ov = rawGroupControlPane.isAllowConfessTalk
  185. if (ov == value) return
  186. rawGroupControlPane.isAllowConfessTalk = value
  187. GroupAllowConfessTalkEvent(ov, value, group, currentActor.id == bot.id).broadcastBlocking()
  188. }
  189. override fun withActor(actor: MockNormalMember): MockGroupControlPane {
  190. return GroupControlPaneImpl(actor)
  191. }
  192. }
  193. override val controlPane: MockGroupControlPane get() = rawGroupControlPane
  194. override var name: String
  195. get() = controlPane.groupName
  196. set(value) {
  197. checkBotPermission(MemberPermission.ADMINISTRATOR)
  198. controlPane.withActor(botAsMember).groupName = value
  199. }
  200. override val mockApi: MockGroup.MockApi = object : MockGroup.MockApi {
  201. override var avatarUrl: String by lateinitMutableProperty {
  202. runBlocking { MockImage.randomForGroup(bot, id).getUrl(bot) }
  203. }
  204. }
  205. override fun changeAvatarUrl(newAvatar: String) {
  206. mockApi.avatarUrl = newAvatar
  207. }
  208. override val avatarUrl: String by mockApi::avatarUrl
  209. override lateinit var owner: MockNormalMember
  210. override lateinit var botAsMember: MockNormalMember
  211. override val members: ContactList<MockNormalMember> = ContactList()
  212. override fun get(id: Long): MockNormalMember? {
  213. if (id == bot.id) return botAsMember
  214. return members[id]
  215. }
  216. override fun contains(id: Long): Boolean = members.any { it.id == id }
  217. override suspend fun quit(): Boolean {
  218. return if (bot.groups.delegate.remove(this)) {
  219. BotLeaveEvent.Active(this).broadcast()
  220. cancel(CancellationException("Bot quited group $id"))
  221. true
  222. } else {
  223. false
  224. }
  225. }
  226. override val announcements = MockAnnouncementsImpl(this)
  227. @Suppress("OverridingDeprecatedMember", "OVERRIDE_DEPRECATION")
  228. override val settings: GroupSettings = object : GroupSettings {
  229. override var entranceAnnouncement: String
  230. get() = announcements.announcements.values.asSequence()
  231. .filter { it.parameters.sendToNewMember }
  232. .firstOrNull()?.content ?: ""
  233. set(value) {
  234. checkBotPermission(MemberPermission.ADMINISTRATOR)
  235. announcements.mockPublish(OfflineAnnouncement.create(value, buildAnnouncementParameters {
  236. sendToNewMember = true
  237. }), [email protected])
  238. }
  239. override var isMuteAll: Boolean
  240. get() = rawGroupControlPane.isMuteAll
  241. set(value) {
  242. checkBotPermission(MemberPermission.ADMINISTRATOR)
  243. rawGroupControlPane.withActor(botAsMember).isMuteAll = value
  244. }
  245. override var isAllowMemberInvite: Boolean
  246. get() = rawGroupControlPane.isAllowMemberInvite
  247. set(value) {
  248. checkBotPermission(MemberPermission.ADMINISTRATOR)
  249. rawGroupControlPane.withActor(botAsMember).isAllowMemberInvite = value
  250. }
  251. @MiraiExperimentalApi
  252. override val isAutoApproveEnabled: Boolean
  253. get() = false // TODO
  254. override var isAnonymousChatEnabled: Boolean
  255. get() = rawGroupControlPane.isAnonymousChatAllowed
  256. set(value) {
  257. checkBotPermission(MemberPermission.ADMINISTRATOR)
  258. rawGroupControlPane.withActor(botAsMember).isAnonymousChatAllowed = value
  259. }
  260. }
  261. override fun newMessagePreSend(message: Message): MessagePreSendEvent =
  262. GroupMessagePreSendEvent(this, message)
  263. override suspend fun postMessagePreSend(message: MessageChain, receipt: MessageReceipt<*>) {
  264. GroupMessagePostSendEvent(this, message, null, receipt = receipt.cast())
  265. .broadcast()
  266. }
  267. override fun newMessageSource(message: MessageChain): OnlineMessageSource.Outgoing {
  268. return newMsgSrc(false, message) { ids, internalIds, time ->
  269. OnlineMsgSrcToGroup(ids, internalIds, time, message, bot, bot, this)
  270. }
  271. }
  272. override suspend fun broadcastMsgSyncEvent(client: OtherClient, message: MessageChain, time: Int) {
  273. val src = newMsgSrc(true, message, time.toLong()) { ids, internalIds, time0 ->
  274. OnlineMsgSrcToGroup(ids, internalIds, time0, message, bot, bot, this)
  275. }
  276. val msg = src.withMessage(message)
  277. GroupMessageSyncEvent(client, this, msg, botAsMember, bot.nick, time).broadcast()
  278. }
  279. override suspend fun sendMessage(message: Message): MessageReceipt<Group> {
  280. return super<AbstractMockContact>.sendMessage(message).cast()
  281. }
  282. @Suppress("OverridingDeprecatedMember", "DEPRECATION_ERROR", "OVERRIDE_DEPRECATION")
  283. override suspend fun uploadVoice(resource: ExternalResource): net.mamoe.mirai.message.data.Voice =
  284. resource.mockUploadVoice(bot)
  285. override suspend fun setEssenceMessage(source: MessageSource): Boolean {
  286. checkBotPermission(MemberPermission.ADMINISTRATOR)
  287. essences.mockSetEssences(source, this.botAsMember)
  288. return true
  289. }
  290. override val essences: MockEssences = MockEssencesImpl(this)
  291. @Deprecated("Please use files instead.", replaceWith = ReplaceWith("files.root"))
  292. @Suppress("OverridingDeprecatedMember", "DEPRECATION", "DEPRECATION_ERROR")
  293. override val filesRoot: RemoteFile by lazy {
  294. net.mamoe.mirai.mock.internal.remotefile.remotefile.RootRemoteFile(txFileSystem, this)
  295. }
  296. override val files: RemoteFiles by lazy {
  297. net.mamoe.mirai.mock.internal.remotefile.absolutefile.MockRemoteFiles(this, txFileSystem)
  298. }
  299. override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio =
  300. resource.mockUploadAudio(bot)
  301. override fun toString(): String {
  302. return "Group($id)"
  303. }
  304. override val roamingMessages: RoamingMessages = MockRoamingMessages(this)
  305. }