Merge branch 'anonymous' into dev, close #432

This commit is contained in:
Karlatemp 2020-12-21 13:04:38 +08:00
commit 2b041f64f8
No known key found for this signature in database
GPG Key ID: 21FBDDF664FF06F8
11 changed files with 144 additions and 20 deletions

View File

@ -21,4 +21,6 @@ public interface MemberInfo : FriendInfo {
public val specialTitle: String
public val muteTimestamp: Int
public val anonymousId: String? get() = null
}

View File

@ -10,6 +10,7 @@
package net.mamoe.mirai
import kotlinx.coroutines.Job
import net.mamoe.mirai.contact.AnonymousMember
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.data.*
@ -201,4 +202,18 @@ public interface LowLevelApiAccessor {
md5: ByteArray,
groupId: Long,
)
/**
* 禁言一个匿名用户
*
* @param anonymousId [AnonymousMember.anonymousId]
*/
@LowLevelApi
public suspend fun _lowLevelMuteAnonymous(
bot: Bot,
anonymousId: String,
anonymousNick: String,
groupId: Long,
seconds: Int,
)
}

View File

@ -13,9 +13,7 @@ import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.*
import net.mamoe.mirai.*
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.*
@ -748,6 +746,36 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
}
override suspend fun _lowLevelMuteAnonymous(
bot: Bot,
anonymousId: String,
anonymousNick: String,
groupId: Long,
seconds: Int
) {
bot as QQAndroidBot
val response = MiraiPlatformUtils.Http.post<String> {
url("https://qqweb.qq.com/c/anonymoustalk/blacklist")
body = MultiPartFormDataContent(formData {
append("anony_id", anonymousId)
append("group_code", groupId)
append("seconds", seconds)
append("anony_nick", anonymousNick)
append("bkn", bot.bkn)
})
headers {
append(
"cookie",
"uin=o${bot.id}; skey=${bot.client.wLoginSigInfo.sKey.data.encodeToString()};"
)
}
}
val jsonObj = Json.decodeFromString(JsonObject.serializer(), response)
if ((jsonObj["retcode"] ?: jsonObj["cgicode"] ?: error("missing response code")).jsonPrimitive.long != 0L) {
throw IllegalStateException(response)
}
}
override fun createImage(imageId: String): Image {
return when {
imageId matches FRIEND_IMAGE_ID_REGEX_1 -> OfflineFriendImage(imageId)

View File

@ -0,0 +1,59 @@
/*
* Copyright 2019-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("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.internal.contact
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.AnonymousMember
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.internal.MiraiImpl
import net.mamoe.mirai.message.action.MemberNudge
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MemberDeprecatedApi
import kotlin.coroutines.CoroutineContext
@OptIn(MemberDeprecatedApi::class)
internal class AnonymousMemberImpl(
override val group: GroupImpl,
override val coroutineContext: CoroutineContext,
private val memberInfo: MemberInfo,
override val anonymousId: String
) : AnonymousMember {
override val nameCard: String get() = memberInfo.nameCard
override val specialTitle: String get() = memberInfo.specialTitle
override val permission: MemberPermission get() = memberInfo.permission
override val bot: Bot get() = group.bot
override val id: Long get() = memberInfo.uin
override val muteTimeRemaining: Int get() = -1
override val nick: String get() = memberInfo.nick
override val remark: String get() = memberInfo.remark
override fun nudge(): MemberNudge = notSupported("Nudge")
override suspend fun uploadImage(image: ExternalImage): Image = notSupported("Upload image to")
override suspend fun unmute() {
}
override suspend fun mute(durationSeconds: Int) {
checkBotPermissionHigherThanThis("mute")
MiraiImpl._lowLevelMuteAnonymous(bot, anonymousId, nameCard, group.uin, durationSeconds)
}
override fun toString(): String = "AnonymousMember($nameCard, $anonymousId)"
private fun notSupported(action: String): Nothing =
throw IllegalStateException("$action anonymous is not allowed")
override suspend fun sendMessage(message: Message): Nothing = notSupported("Send message to")
override suspend fun sendMessage(message: String): Nothing = notSupported("Send message to")
override suspend fun kick(message: String): Unit = notSupported("Kick")
}

View File

@ -231,6 +231,12 @@ internal class GroupImpl(
}
override fun newMember(memberInfo: MemberInfo): Member {
memberInfo.anonymousId?.let { anId ->
return AnonymousMemberImpl(
this, this.coroutineContext,
memberInfo, anId
)
}
return MemberImpl(
Mirai._lowLevelNewFriend(bot, memberInfo) as FriendImpl,
this,
@ -239,7 +245,7 @@ internal class GroupImpl(
)
}
internal fun newAnonymous(name: String): Member = newMember(
internal fun newAnonymous(name: String, id: String): Member = newMember(
object : MemberInfo {
override val nameCard = name
override val permission = MemberPermission.MEMBER
@ -248,6 +254,7 @@ internal class GroupImpl(
override val uin = 80000000L
override val nick = name
override val remark: String = "匿名"
override val anonymousId: String get() = id
}
)

View File

@ -202,15 +202,6 @@ internal class MemberImpl constructor(
net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
}
private fun checkBotPermissionHigherThanThis(operationName: String) {
check(group.botPermission > this.permission) {
throw PermissionDeniedException(
"`$operationName` operation requires a higher permission, while " +
"${group.botPermission} < ${this.permission}"
)
}
}
@JvmSynthetic
override suspend fun unmute() {
checkBotPermissionHigherThanThis("unmute")
@ -250,6 +241,15 @@ internal class MemberImpl constructor(
}
}
internal fun Member.checkBotPermissionHigherThanThis(operationName: String) {
check(group.botPermission > this.permission) {
throw PermissionDeniedException(
"`$operationName` operation requires a higher permission, while " +
"${group.botPermission} < ${this.permission}"
)
}
}
@OptIn(ExperimentalContracts::class)
internal fun Member.checkIsMemberImpl(): MemberImpl {
contract {

View File

@ -97,7 +97,10 @@ internal fun Contact.logMessageSent(message: Message) {
internal fun net.mamoe.mirai.event.events.MessageEvent.logMessageReceived() {
when (this) {
is net.mamoe.mirai.event.events.GroupMessageEvent -> bot.logger.verbose {
"[${group.name}(${group.id})] ${senderName}(${sender.id}) -> $message".replaceMagicCodes()
"[${group.name}(${group.id})] ${senderName}(${
if (sender is AnonymousMember) "匿名"
else sender.id
}) -> $message".replaceMagicCodes()
}
is net.mamoe.mirai.event.events.TempMessageEvent -> bot.logger.verbose {
"[${group.name}(${group.id})] $senderName(Temp ${sender.id}) -> $message".replaceMagicCodes()

View File

@ -18,6 +18,7 @@ import kotlinx.io.core.readUInt
import kotlinx.io.core.toByteArray
import net.mamoe.mirai.Bot
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.contact.AnonymousMember
import net.mamoe.mirai.contact.ContactOrBot
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.internal.network.protocol.data.proto.HummerCommelem
@ -159,7 +160,9 @@ internal fun MessageChain.toRichTextElems(
if (forGroup) {
when (val source = it.source) {
is OnlineMessageSource.Incoming.FromGroup -> {
transformOneMessage(At(source.sender))
val sender0 = source.sender
if (sender0 !is AnonymousMember)
transformOneMessage(At(sender0))
// transformOneMessage(PlainText(" "))
// removed by https://github.com/mamoe/mirai/issues/524
// 发送 QuoteReply 消息时无可避免的产生多余空格 #524

View File

@ -20,6 +20,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg
import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.encodeToBase64
import net.mamoe.mirai.internal.utils.encodeToString
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.data.Message
@ -163,7 +164,7 @@ internal data class MessageSourceFromGroupImpl(
) as GroupImpl).run {
get(msg.first().msgHead.fromUin)
?: msg.first().msgBody.richText.elems.firstOrNull { it.anonGroupMsg != null }?.run {
newAnonymous(anonGroupMsg!!.anonNick.encodeToString())
newAnonymous(anonGroupMsg!!.anonNick.encodeToString(), anonGroupMsg.anonId.encodeToBase64())
}
?: error("cannot find member for MessageSourceFromGroupImpl. msg=${msg._miraiContentToString()}")
}

View File

@ -29,6 +29,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush
import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x8fc
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
import net.mamoe.mirai.internal.utils._miraiContentToString
import net.mamoe.mirai.internal.utils.encodeToBase64
import net.mamoe.mirai.internal.utils.encodeToString
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
@ -78,10 +79,10 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("Onlin
}
val sender = if (anonymous != null) {
group.newAnonymous(anonymous.anonNick.encodeToString())
group.newAnonymous(anonymous.anonNick.encodeToString(), anonymous.anonId.encodeToBase64())
} else {
group[pbPushMsg.msg.msgHead.fromUin]
} as MemberImpl
group[pbPushMsg.msg.msgHead.fromUin] as MemberImpl
}
val name = if (anonymous != null) {
sender.nameCard
@ -103,7 +104,7 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("Onlin
val flags = extraInfo?.flags ?: 0
return GroupMessageEvent(
senderName = name.also {
if (it != sender.nameCard) {
if (sender is MemberImpl && it != sender.nameCard) {
val origin = sender._nameCard
sender._nameCard = name
MemberCardChangeEvent(origin, name, sender).broadcast()

View File

@ -18,6 +18,7 @@ import kotlinx.io.charsets.Charsets
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.String
import kotlinx.io.core.use
import java.util.*
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmMultifileClass
@ -94,6 +95,10 @@ internal fun UByteArray.toUHexString(separator: String = " ", offset: Int = 0, l
internal inline fun ByteArray.encodeToString(offset: Int = 0, charset: Charset = Charsets.UTF_8): String =
String(this, charset = charset, offset = offset, length = this.size - offset)
@Suppress("NOTHING_TO_INLINE")
internal inline fun ByteArray.encodeToBase64(): String =
Base64.getEncoder().encodeToString(this)
@PublishedApi
internal inline fun ByteArray.toReadPacket(offset: Int = 0, length: Int = this.size - offset) =
ByteReadPacket(this, offset = offset, length = length)