|
@@ -1,24 +1,29 @@
|
|
|
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
|
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
|
|
|
|
|
|
|
-package net.mamoe.mirai.contact.internal
|
|
|
|
|
|
|
+package net.mamoe.mirai.timpc.network
|
|
|
|
|
|
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.CoroutineScope
|
|
|
import kotlinx.coroutines.Job
|
|
import kotlinx.coroutines.Job
|
|
|
-import kotlinx.coroutines.launch
|
|
|
|
|
-import net.mamoe.mirai.Bot
|
|
|
|
|
|
|
+import kotlinx.coroutines.withContext
|
|
|
import net.mamoe.mirai.contact.*
|
|
import net.mamoe.mirai.contact.*
|
|
|
-import net.mamoe.mirai.contact.data.Profile
|
|
|
|
|
import net.mamoe.mirai.event.subscribeAlways
|
|
import net.mamoe.mirai.event.subscribeAlways
|
|
|
-import net.mamoe.mirai.message.MessageChain
|
|
|
|
|
-import net.mamoe.mirai.network.protocol.timpc.packet.action.*
|
|
|
|
|
-import net.mamoe.mirai.network.protocol.timpc.packet.event.MemberJoinEventPacket
|
|
|
|
|
-import net.mamoe.mirai.network.protocol.timpc.packet.event.MemberQuitEvent
|
|
|
|
|
-import net.mamoe.mirai.network.qqAccount
|
|
|
|
|
-import net.mamoe.mirai.network.sessionKey
|
|
|
|
|
|
|
+import net.mamoe.mirai.message.data.ImageId
|
|
|
|
|
+import net.mamoe.mirai.message.data.MessageChain
|
|
|
|
|
+import net.mamoe.mirai.network.data.FriendNameRemark
|
|
|
|
|
+import net.mamoe.mirai.network.data.GroupInfo
|
|
|
|
|
+import net.mamoe.mirai.network.data.PreviousNameList
|
|
|
|
|
+import net.mamoe.mirai.network.data.Profile
|
|
|
import net.mamoe.mirai.qqAccount
|
|
import net.mamoe.mirai.qqAccount
|
|
|
-import net.mamoe.mirai.sendPacket
|
|
|
|
|
-import net.mamoe.mirai.utils.MiraiInternalAPI
|
|
|
|
|
-import net.mamoe.mirai.withSession
|
|
|
|
|
|
|
+import net.mamoe.mirai.timpc.TIMPCBot
|
|
|
|
|
+import net.mamoe.mirai.timpc.internal.RawGroupInfo
|
|
|
|
|
+import net.mamoe.mirai.timpc.network.packet.action.*
|
|
|
|
|
+import net.mamoe.mirai.timpc.network.packet.event.MemberJoinEventPacket
|
|
|
|
|
+import net.mamoe.mirai.timpc.network.packet.event.MemberQuitEvent
|
|
|
|
|
+import net.mamoe.mirai.timpc.sendPacket
|
|
|
|
|
+import net.mamoe.mirai.timpc.utils.assertUnreachable
|
|
|
|
|
+import net.mamoe.mirai.timpc.withTIMPCBot
|
|
|
|
|
+import net.mamoe.mirai.utils.*
|
|
|
|
|
+import net.mamoe.mirai.utils.io.toUHexString
|
|
|
import kotlin.coroutines.CoroutineContext
|
|
import kotlin.coroutines.CoroutineContext
|
|
|
|
|
|
|
|
internal sealed class ContactImpl : Contact {
|
|
internal sealed class ContactImpl : Contact {
|
|
@@ -30,22 +35,12 @@ internal sealed class ContactImpl : Contact {
|
|
|
internal abstract suspend fun startUpdater()
|
|
internal abstract suspend fun startUpdater()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * 构造 [Group]
|
|
|
|
|
- */
|
|
|
|
|
-@Suppress("FunctionName")
|
|
|
|
|
-@PublishedApi
|
|
|
|
|
-internal fun CoroutineScope.Group(bot: Bot, groupId: GroupId, info: RawGroupInfo, context: CoroutineContext): Group =
|
|
|
|
|
- GroupImpl(bot, groupId, context).apply {
|
|
|
|
|
- [email protected] = info.parseBy(this@apply)
|
|
|
|
|
- launch { startUpdater() }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
|
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
|
|
-internal data class GroupImpl internal constructor(override val bot: Bot, val groupId: GroupId, override val coroutineContext: CoroutineContext) :
|
|
|
|
|
|
|
+internal class GroupImpl internal constructor(bot: TIMPCBot, val groupId: GroupId, override val coroutineContext: CoroutineContext) :
|
|
|
ContactImpl(), Group, CoroutineScope {
|
|
ContactImpl(), Group, CoroutineScope {
|
|
|
- override val id: UInt get() = groupId.value
|
|
|
|
|
|
|
+ override val bot: TIMPCBot by bot.unsafeWeakRef()
|
|
|
|
|
+
|
|
|
|
|
+ override val id: Long get() = groupId.value
|
|
|
override val internalId = GroupId(id).toInternalId()
|
|
override val internalId = GroupId(id).toInternalId()
|
|
|
|
|
|
|
|
internal lateinit var info: GroupInfo
|
|
internal lateinit var info: GroupInfo
|
|
@@ -56,19 +51,45 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr
|
|
|
override val announcement: String get() = info.announcement
|
|
override val announcement: String get() = info.announcement
|
|
|
override val members: ContactList<Member> get() = info.members
|
|
override val members: ContactList<Member> get() = info.members
|
|
|
|
|
|
|
|
- override fun getMember(id: UInt): Member =
|
|
|
|
|
- members.getOrNull(id) ?: throw NoSuchElementException("No such member whose id is ${id.toLong()} in group ${groupId.value.toLong()}")
|
|
|
|
|
|
|
+ override fun getMember(id: Long): Member =
|
|
|
|
|
+ members.getOrNull(id) ?: throw NoSuchElementException("No such member whose id is $id in group ${groupId.value}")
|
|
|
|
|
|
|
|
override suspend fun sendMessage(message: MessageChain) {
|
|
override suspend fun sendMessage(message: MessageChain) {
|
|
|
bot.sendPacket(GroupPacket.Message(bot.qqAccount, internalId, bot.sessionKey, message))
|
|
bot.sendPacket(GroupPacket.Message(bot.qqAccount, internalId, bot.sessionKey, message))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- override suspend fun updateGroupInfo(): GroupInfo = bot.withSession {
|
|
|
|
|
|
|
+ override suspend fun uploadImage(image: ExternalImage): ImageId = withTIMPCBot {
|
|
|
|
|
+ val userContext = coroutineContext
|
|
|
|
|
+ val response = GroupImagePacket.RequestImageId(bot.qqAccount, internalId, image, sessionKey).sendAndExpect<GroupImageResponse>()
|
|
|
|
|
+
|
|
|
|
|
+ withContext(userContext) {
|
|
|
|
|
+ when (response) {
|
|
|
|
|
+ is ImageUploadInfo -> response.uKey?.let {
|
|
|
|
|
+ Http.postImage(
|
|
|
|
|
+ htcmd = "0x6ff0071",
|
|
|
|
|
+ uin = bot.qqAccount,
|
|
|
|
|
+ groupId = GroupId(id),
|
|
|
|
|
+ imageInput = image.input,
|
|
|
|
|
+ inputSize = image.inputSize,
|
|
|
|
|
+ uKeyHex = it.toUHexString("")
|
|
|
|
|
+ )
|
|
|
|
|
+ } // if null: image already exists
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: 2019/11/17 超过大小的情况
|
|
|
|
|
+ //is Overfile -> throw OverFileSizeMaxException()
|
|
|
|
|
+ else -> assertUnreachable()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return image.groupImageId
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override suspend fun updateGroupInfo(): GroupInfo = withTIMPCBot {
|
|
|
GroupPacket.QueryGroupInfo(qqAccount, internalId, sessionKey).sendAndExpect<RawGroupInfo>().parseBy(this@GroupImpl).also { info = it }
|
|
GroupPacket.QueryGroupInfo(qqAccount, internalId, sessionKey).sendAndExpect<RawGroupInfo>().parseBy(this@GroupImpl).also { info = it }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- override suspend fun quit(): QuitGroupResponse = bot.withSession {
|
|
|
|
|
- GroupPacket.QuitGroup(qqAccount, sessionKey, internalId).sendAndExpect()
|
|
|
|
|
|
|
+ override suspend fun quit(): Boolean = withTIMPCBot {
|
|
|
|
|
+ GroupPacket.QuitGroup(qqAccount, sessionKey, internalId).sendAndExpect<GroupPacket.QuitGroupResponse>().isSuccess
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@UseExperimental(MiraiInternalAPI::class)
|
|
@UseExperimental(MiraiInternalAPI::class)
|
|
@@ -84,25 +105,45 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr
|
|
|
override fun toString(): String = "Group(${this.id})"
|
|
override fun toString(): String = "Group(${this.id})"
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
|
|
|
-internal inline fun CoroutineScope.QQ(bot: Bot, id: UInt, coroutineContext: CoroutineContext): QQ = QQImpl(bot, id, coroutineContext).apply { launch { startUpdater() } }
|
|
|
|
|
-
|
|
|
|
|
@PublishedApi
|
|
@PublishedApi
|
|
|
-internal data class QQImpl @PublishedApi internal constructor(override val bot: Bot, override val id: UInt, override val coroutineContext: CoroutineContext) :
|
|
|
|
|
|
|
+internal class QQImpl @PublishedApi internal constructor(bot: TIMPCBot, override val id: Long, override val coroutineContext: CoroutineContext) :
|
|
|
ContactImpl(),
|
|
ContactImpl(),
|
|
|
QQ, CoroutineScope {
|
|
QQ, CoroutineScope {
|
|
|
|
|
+ override val bot: TIMPCBot by bot.unsafeWeakRef()
|
|
|
|
|
+
|
|
|
override suspend fun sendMessage(message: MessageChain) =
|
|
override suspend fun sendMessage(message: MessageChain) =
|
|
|
bot.sendPacket(SendFriendMessagePacket(bot.qqAccount, id, bot.sessionKey, message))
|
|
bot.sendPacket(SendFriendMessagePacket(bot.qqAccount, id, bot.sessionKey, message))
|
|
|
|
|
|
|
|
- override suspend fun queryProfile(): Profile = bot.withSession {
|
|
|
|
|
|
|
+ override suspend fun uploadImage(image: ExternalImage): ImageId = withTIMPCBot {
|
|
|
|
|
+ FriendImagePacket.RequestImageId(qqAccount, sessionKey, id, image).sendAndExpect<FriendImageResponse>().let {
|
|
|
|
|
+ when (it) {
|
|
|
|
|
+ is FriendImageUKey -> {
|
|
|
|
|
+ Http.postImage(
|
|
|
|
|
+ htcmd = "0x6ff0070",
|
|
|
|
|
+ uin = bot.qqAccount,
|
|
|
|
|
+ groupId = null,
|
|
|
|
|
+ uKeyHex = it.uKey.toUHexString(""),
|
|
|
|
|
+ imageInput = image.input,
|
|
|
|
|
+ inputSize = image.inputSize
|
|
|
|
|
+ )
|
|
|
|
|
+ it.imageId
|
|
|
|
|
+ }
|
|
|
|
|
+ is FriendImageAlreadyExists -> it.imageId
|
|
|
|
|
+ is FriendImageOverFileSizeMax -> throw OverFileSizeMaxException()
|
|
|
|
|
+ else -> error("This shouldn't happen")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override suspend fun queryProfile(): Profile = withTIMPCBot {
|
|
|
RequestProfileDetailsPacket(bot.qqAccount, id, sessionKey).sendAndExpect<RequestProfileDetailsResponse>().profile
|
|
RequestProfileDetailsPacket(bot.qqAccount, id, sessionKey).sendAndExpect<RequestProfileDetailsResponse>().profile
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- override suspend fun queryPreviousNameList(): PreviousNameList = bot.withSession {
|
|
|
|
|
|
|
+ override suspend fun queryPreviousNameList(): PreviousNameList = withTIMPCBot {
|
|
|
QueryPreviousNamePacket(bot.qqAccount, sessionKey, id).sendAndExpect()
|
|
QueryPreviousNamePacket(bot.qqAccount, sessionKey, id).sendAndExpect()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- override suspend fun queryRemark(): FriendNameRemark = bot.withSession {
|
|
|
|
|
|
|
+ override suspend fun queryRemark(): FriendNameRemark = withTIMPCBot {
|
|
|
QueryFriendRemarkPacket(bot.qqAccount, sessionKey, id).sendAndExpect()
|
|
QueryFriendRemarkPacket(bot.qqAccount, sessionKey, id).sendAndExpect()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -114,10 +155,6 @@ internal data class QQImpl @PublishedApi internal constructor(override val bot:
|
|
|
override fun toString(): String = "QQ(${this.id})"
|
|
override fun toString(): String = "QQ(${this.id})"
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-@Suppress("FunctionName", "NOTHING_TO_INLINE")
|
|
|
|
|
-internal inline fun Group.Member(delegate: QQ, permission: MemberPermission, coroutineContext: CoroutineContext): Member =
|
|
|
|
|
- MemberImpl(delegate, this, permission, coroutineContext).apply { launch { startUpdater() } }
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* 群成员
|
|
* 群成员
|
|
|
*/
|
|
*/
|
|
@@ -130,13 +167,13 @@ internal data class MemberImpl(
|
|
|
) : QQ by delegate, CoroutineScope, Member, ContactImpl() {
|
|
) : QQ by delegate, CoroutineScope, Member, ContactImpl() {
|
|
|
override fun toString(): String = "Member(id=${this.id}, group=${group.id}, permission=$permission)"
|
|
override fun toString(): String = "Member(id=${this.id}, group=${group.id}, permission=$permission)"
|
|
|
|
|
|
|
|
- override suspend fun mute(durationSeconds: Int): Boolean = bot.withSession {
|
|
|
|
|
|
|
+ override suspend fun mute(durationSeconds: Int): Boolean = withTIMPCBot {
|
|
|
require(durationSeconds > 0) { "duration must be greater than 0 second" }
|
|
require(durationSeconds > 0) { "duration must be greater than 0 second" }
|
|
|
require(durationSeconds <= 30 * 24 * 3600) { "duration must be no more than 30 days" }
|
|
require(durationSeconds <= 30 * 24 * 3600) { "duration must be no more than 30 days" }
|
|
|
|
|
|
|
|
if (permission == MemberPermission.OWNER) return false
|
|
if (permission == MemberPermission.OWNER) return false
|
|
|
val operator = group.getMember(bot.qqAccount)
|
|
val operator = group.getMember(bot.qqAccount)
|
|
|
- check(operator.id != id) { "The bot is the owner of group ${group.id.toLong()}, it cannot mute itself!" }
|
|
|
|
|
|
|
+ check(operator.id != id) { "The bot is the owner of group ${group.id}, it cannot mute itself!" }
|
|
|
when (operator.permission) {
|
|
when (operator.permission) {
|
|
|
MemberPermission.MEMBER -> return false
|
|
MemberPermission.MEMBER -> return false
|
|
|
MemberPermission.ADMINISTRATOR -> if (permission == MemberPermission.ADMINISTRATOR) return false
|
|
MemberPermission.ADMINISTRATOR -> if (permission == MemberPermission.ADMINISTRATOR) return false
|
|
@@ -153,7 +190,7 @@ internal data class MemberImpl(
|
|
|
// TODO: 2019/12/6 更新群成员信息
|
|
// TODO: 2019/12/6 更新群成员信息
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- override suspend fun unmute(): Unit = bot.withSession {
|
|
|
|
|
|
|
+ override suspend fun unmute(): Unit = withTIMPCBot {
|
|
|
GroupPacket.Mute(qqAccount, group.internalId, sessionKey, id, 0u).sendAndExpect<GroupPacket.MuteResponse>()
|
|
GroupPacket.Mute(qqAccount, group.internalId, sessionKey, id, 0u).sendAndExpect<GroupPacket.MuteResponse>()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|