util.kt 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. * Copyright 2019-2023 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.serialization.Serializable
  12. import net.mamoe.mirai.Bot
  13. import net.mamoe.mirai.contact.Contact
  14. import net.mamoe.mirai.contact.Group
  15. import net.mamoe.mirai.contact.Member
  16. import net.mamoe.mirai.contact.PermissionDeniedException
  17. import net.mamoe.mirai.internal.contact.uin
  18. import net.mamoe.mirai.internal.message.data.OnlineAudioImpl
  19. import net.mamoe.mirai.internal.message.image.DeferredOriginUrlAware
  20. import net.mamoe.mirai.message.data.*
  21. import net.mamoe.mirai.mock.MockBot
  22. import net.mamoe.mirai.mock.contact.MockGroup
  23. import net.mamoe.mirai.mock.utils.mock
  24. import net.mamoe.mirai.mock.utils.plusHttpSubpath
  25. import net.mamoe.mirai.utils.ExternalResource
  26. import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
  27. import net.mamoe.mirai.utils.Services
  28. import net.mamoe.mirai.utils.cast
  29. import net.mamoe.mirai.utils.toUHexString
  30. internal fun Member.requireBotPermissionHigherThanThis(msg: String) {
  31. if (this.permission < this.group.botPermission) return
  32. throw PermissionDeniedException("bot current permission ${group.botPermission} can't modify $id($permission), $msg")
  33. }
  34. internal fun MessageSource.withMessage(msg: Message): MessageChain = buildMessageChain {
  35. add(this@withMessage)
  36. if (msg is MessageChain) {
  37. msg.forEach { sub ->
  38. if (sub !is MessageSource) {
  39. add(sub)
  40. }
  41. }
  42. } else if (msg !is MessageSource) {
  43. add(msg)
  44. }
  45. }
  46. @Suppress("UNUSED_PARAMETER")
  47. internal suspend fun ExternalResource.mockUploadAudio(bot: MockBot): OfflineAudio {
  48. val md5 = md5 // calculate before using resource
  49. return inResource {
  50. OfflineAudio(
  51. filename = md5.toUHexString() + ".amr",
  52. fileMd5 = md5,
  53. fileSize = size,
  54. codec = AudioCodec.SILK,
  55. extraData = null,
  56. )
  57. }
  58. }
  59. internal suspend fun ExternalResource.mockUploadVoice(bot: MockBot) = kotlin.run {
  60. val md5 = this.md5
  61. val size = this.size
  62. @Suppress("DEPRECATION_ERROR")
  63. net.mamoe.mirai.message.data.Voice(
  64. fileName = md5.toUHexString() + ".amr",
  65. md5 = md5,
  66. fileSize = size,
  67. _url = bot.tmpResourceServer.uploadResourceAndGetUrl(this)
  68. )
  69. }
  70. internal const val AQQ_RECALL_FAILED_MESSAGE: String = "No message meets the requirements"
  71. internal val Group.mockUin: Long
  72. get() = when (this) {
  73. is MockGroup -> this.uin
  74. else -> this.uin
  75. }
  76. internal suspend fun ExternalResource.mockImplUploadAudioAsOnline(bot: MockBot): OnlineAudio {
  77. val md5 = this.md5
  78. val size = this.size
  79. return OnlineAudioImpl(
  80. filename = md5.toUHexString() + ".amr",
  81. fileMd5 = md5,
  82. fileSize = size,
  83. codec = AudioCodec.SILK,
  84. url = bot.tmpResourceServer.uploadResourceAndGetUrl(this),
  85. length = size,
  86. originalPtt = null,
  87. )
  88. }
  89. @Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
  90. @Serializable(MockImage.Serializer::class)
  91. internal class MockImage(
  92. override val imageId: String,
  93. private val urlPath: String,
  94. override val width: Int = 0,
  95. override val height: Int = 0,
  96. override val size: Long = 0,
  97. override val imageType: ImageType = ImageType.UNKNOWN,
  98. ) : DeferredOriginUrlAware, Image {
  99. companion object {
  100. // create a mockImage with random content
  101. internal suspend fun random(bot: MockBot): MockImage {
  102. val text = bot.avatarGenerator.generateRandomAvatar()
  103. return bot.uploadMockImage(text.toExternalResource().toAutoCloseable()).cast()
  104. }
  105. internal suspend fun randomForPerson(bot: MockBot, id: Long): MockImage {
  106. val text = bot.avatarGenerator.generateAvatarForPerson(id)
  107. return bot.uploadMockImage(text.toExternalResource().toAutoCloseable()).cast()
  108. }
  109. internal suspend fun randomForGroup(bot: MockBot, id: Long): MockImage {
  110. val text = bot.avatarGenerator.generateAvatarForGroup(id)
  111. return bot.uploadMockImage(text.toExternalResource().toAutoCloseable()).cast()
  112. }
  113. }
  114. object Serializer : Image.FallbackSerializer("MockImage")
  115. private val _stringValue: String? by lazy(LazyThreadSafetyMode.NONE) { "[mirai:image:$imageId]" }
  116. override fun getUrl(bot: Bot): String {
  117. if (urlPath.startsWith("http"))
  118. return urlPath
  119. return bot.mock().tmpResourceServer.storageRoot.toString().plusHttpSubpath(urlPath)
  120. }
  121. override fun toString(): String = _stringValue!!
  122. override fun contentToString(): String = if (isEmoji) {
  123. "[动画表情]"
  124. } else {
  125. "[图片]"
  126. }
  127. override fun appendMiraiCodeTo(builder: StringBuilder) {
  128. builder.append("[mirai:image:").append(imageId).append("]")
  129. }
  130. override fun hashCode(): Int = imageId.hashCode()
  131. override fun equals(other: Any?): Boolean {
  132. if (other === this) return true
  133. if (other !is Image) return false
  134. return this.imageId == other.imageId
  135. }
  136. }
  137. internal object MockInternalImageProtocolImpl : InternalImageProtocol {
  138. override fun createImage(
  139. imageId: String,
  140. size: Long,
  141. type: ImageType,
  142. width: Int,
  143. height: Int,
  144. isEmoji: Boolean
  145. ): Image = MockImage(imageId, "images/" + imageId.substring(1..36), width, height, size, type)
  146. override suspend fun isUploaded(
  147. bot: Bot,
  148. md5: ByteArray,
  149. size: Long,
  150. context: Contact?,
  151. type: ImageType,
  152. width: Int,
  153. height: Int
  154. ): Boolean = bot.cast<MockBot>().tmpResourceServer.isImageUploaded(md5, size)
  155. }
  156. internal fun registerMockServices() {
  157. Services.registerAsOverride(
  158. Services.qualifiedNameOrFail(InternalImageProtocol::class),
  159. "net.mamoe.mirai.mock.internal.contact.MockInternalImageProtocolImpl"
  160. ) {
  161. MockInternalImageProtocolImpl
  162. }
  163. }