mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-05 23:50:08 +08:00
Add mute and unmute
This commit is contained in:
parent
8fa98f5938
commit
417276acda
@ -2,5 +2,7 @@
|
|||||||
|
|
||||||
## Main version 0
|
## Main version 0
|
||||||
|
|
||||||
### 0.3.0
|
### 0.6.0
|
||||||
- 更新
|
- 新增: 禁言群成员
|
||||||
|
- 新增: 解禁群成员
|
||||||
|
- 修复: ContactList key 无法匹配
|
@ -1,7 +1,7 @@
|
|||||||
# style guide
|
# style guide
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
# config
|
# config
|
||||||
mirai_version=0.5.1
|
mirai_version=0.6.0
|
||||||
kotlin.incremental.multiplatform=true
|
kotlin.incremental.multiplatform=true
|
||||||
kotlin.parallel.tasks.in.project=true
|
kotlin.parallel.tasks.in.project=true
|
||||||
# kotlin
|
# kotlin
|
||||||
|
@ -59,7 +59,7 @@ inline fun <R> Contact.withSession(block: BotSession.() -> R): R {
|
|||||||
/**
|
/**
|
||||||
* 只读联系人列表
|
* 只读联系人列表
|
||||||
*/
|
*/
|
||||||
class ContactList<C : Contact> @PublishedApi internal constructor(internal val delegate: MutableContactList<C>) : Map<UInt, C> by delegate {
|
class ContactList<C : Contact> @PublishedApi internal constructor(internal val delegate: MutableContactList<C>) : Map<UInt, C> {
|
||||||
/**
|
/**
|
||||||
* ID 列表的字符串表示.
|
* ID 列表的字符串表示.
|
||||||
* 如:
|
* 如:
|
||||||
@ -70,12 +70,41 @@ class ContactList<C : Contact> @PublishedApi internal constructor(internal val d
|
|||||||
val idContentString: String get() = this.keys.joinToString(prefix = "[", postfix = "]") { it.toLong().toString() }
|
val idContentString: String get() = this.keys.joinToString(prefix = "[", postfix = "]") { it.toLong().toString() }
|
||||||
|
|
||||||
override fun toString(): String = delegate.toString()
|
override fun toString(): String = delegate.toString()
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: 2019/12/2 应该使用属性代理, 但属性代理会导致 UInt 内联错误. 等待 kotlin 修复后替换
|
||||||
|
|
||||||
|
override val size: Int get() = delegate.size
|
||||||
|
override fun containsKey(key: UInt): Boolean = delegate.containsKey(key)
|
||||||
|
override fun containsValue(value: C): Boolean = delegate.containsValue(value)
|
||||||
|
override fun get(key: UInt): C? = delegate[key]
|
||||||
|
override fun isEmpty(): Boolean = delegate.isEmpty()
|
||||||
|
override val entries: MutableSet<MutableMap.MutableEntry<UInt, C>> get() = delegate.entries
|
||||||
|
override val keys: MutableSet<UInt> get() = delegate.keys
|
||||||
|
override val values: MutableCollection<C> get() = delegate.values
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可修改联系人列表. 只会在内部使用.
|
* 可修改联系人列表. 只会在内部使用.
|
||||||
*/
|
*/
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal class MutableContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf() {
|
internal class MutableContactList<C : Contact> : MutableMap<UInt, C> {
|
||||||
override fun toString(): String = asIterable().joinToString(separator = ", ", prefix = "ContactList(", postfix = ")") { it.value.toString() }
|
override fun toString(): String = asIterable().joinToString(separator = ", ", prefix = "ContactList(", postfix = ")") { it.value.toString() }
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: 2019/12/2 应该使用属性代理, 但属性代理会导致 UInt 内联错误. 等待 kotlin 修复后替换
|
||||||
|
private val delegate = mutableMapOf<UInt, C>()
|
||||||
|
|
||||||
|
override val size: Int get() = delegate.size
|
||||||
|
override fun containsKey(key: UInt): Boolean = delegate.containsKey(key)
|
||||||
|
override fun containsValue(value: C): Boolean = delegate.containsValue(value)
|
||||||
|
override fun get(key: UInt): C? = delegate[key]
|
||||||
|
override fun isEmpty(): Boolean = delegate.isEmpty()
|
||||||
|
override val entries: MutableSet<MutableMap.MutableEntry<UInt, C>> get() = delegate.entries
|
||||||
|
override val keys: MutableSet<UInt> get() = delegate.keys
|
||||||
|
override val values: MutableCollection<C> get() = delegate.values
|
||||||
|
override fun clear() = delegate.clear()
|
||||||
|
override fun put(key: UInt, value: C): C? = delegate.put(key, value)
|
||||||
|
override fun putAll(from: Map<out UInt, C>) = delegate.putAll(from)
|
||||||
|
override fun remove(key: UInt): C? = delegate.remove(key)
|
||||||
}
|
}
|
@ -51,7 +51,7 @@ interface Group : Contact, Iterable<Member> {
|
|||||||
/**
|
/**
|
||||||
* 获取群成员. 若此 ID 的成员不存在, 则会抛出 [kotlin.NoSuchElementException]
|
* 获取群成员. 若此 ID 的成员不存在, 则会抛出 [kotlin.NoSuchElementException]
|
||||||
*/
|
*/
|
||||||
suspend fun getMember(id: UInt): Member
|
fun getMember(id: UInt): Member
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新群资料. 群资料会与服务器事件同步事件更新, 一般情况下不需要手动更新.
|
* 更新群资料. 群资料会与服务器事件同步事件更新, 一般情况下不需要手动更新.
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.contact
|
package net.mamoe.mirai.contact
|
||||||
|
|
||||||
|
import com.soywiz.klock.MonthSpan
|
||||||
|
import com.soywiz.klock.TimeSpan
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 群成员.
|
* 群成员.
|
||||||
*
|
|
||||||
* 使用 [QQ.equals]. 因此同 ID 的群成员和 QQ 是 `==` 的
|
|
||||||
*/
|
*/
|
||||||
interface Member : QQ, Contact {
|
interface Member : QQ, Contact {
|
||||||
/**
|
/**
|
||||||
@ -15,8 +20,45 @@ interface Member : QQ, Contact {
|
|||||||
* 权限
|
* 权限
|
||||||
*/
|
*/
|
||||||
val permission: MemberPermission
|
val permission: MemberPermission
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁言
|
||||||
|
*
|
||||||
|
* @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
|
||||||
|
* @return 若机器人无权限禁言这个群成员, 返回 `false`
|
||||||
|
*/
|
||||||
|
suspend fun mute(durationSeconds: Int): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解除禁言
|
||||||
|
*/
|
||||||
|
suspend fun unmute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalTime
|
||||||
|
suspend inline fun Member.mute(duration: Duration) {
|
||||||
|
require(duration.inDays > 30) { "duration must be at most 1 month" }
|
||||||
|
require(duration.inSeconds > 0) { "duration must be greater than 0 second" }
|
||||||
|
this.mute(duration.inSeconds.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend inline fun Member.mute(duration: TimeSpan) {
|
||||||
|
require(duration.days > 30) { "duration must be at most 1 month" }
|
||||||
|
require(duration.microseconds > 0) { "duration must be greater than 0 second" }
|
||||||
|
this.mute(duration.seconds.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend inline fun Member.mute(duration: MonthSpan) {
|
||||||
|
require(duration.totalMonths == 1) { "if you pass a MonthSpan, it must be 1 month" }
|
||||||
|
this.mute(duration.totalMonths * 30 * 24 * 3600)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
suspend inline fun Member.mute(durationSeconds: UInt) {
|
||||||
|
require(durationSeconds.toInt() <= 30 * 24 * 3600) { "duration must be at most 1 month" }
|
||||||
|
this.mute(durationSeconds.toInt())
|
||||||
|
} // same bin rep.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 群成员的权限
|
* 群成员的权限
|
||||||
*/
|
*/
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "MemberVisibilityCanBePrivate")
|
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "MemberVisibilityCanBePrivate", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||||
|
|
||||||
package net.mamoe.mirai.contact.internal
|
package net.mamoe.mirai.contact.internal
|
||||||
|
|
||||||
@ -19,6 +19,7 @@ import net.mamoe.mirai.network.sessionKey
|
|||||||
import net.mamoe.mirai.qqAccount
|
import net.mamoe.mirai.qqAccount
|
||||||
import net.mamoe.mirai.sendPacket
|
import net.mamoe.mirai.sendPacket
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||||
|
import net.mamoe.mirai.utils.io.logStacktrace
|
||||||
import net.mamoe.mirai.withSession
|
import net.mamoe.mirai.withSession
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ internal suspend fun Group(bot: Bot, groupId: GroupId, context: CoroutineContext
|
|||||||
val info: RawGroupInfo = try {
|
val info: RawGroupInfo = try {
|
||||||
bot.withSession { GroupPacket.QueryGroupInfo(qqAccount, groupId.toInternalId(), sessionKey).sendAndExpect() }
|
bot.withSession { GroupPacket.QueryGroupInfo(qqAccount, groupId.toInternalId(), sessionKey).sendAndExpect() }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
e.logStacktrace()
|
||||||
error("Cannot obtain group info for id ${groupId.value}")
|
error("Cannot obtain group info for id ${groupId.value}")
|
||||||
}
|
}
|
||||||
return GroupImpl(bot, groupId, context).apply { this.info = info.parseBy(this) }
|
return GroupImpl(bot, groupId, context).apply { this.info = info.parseBy(this) }
|
||||||
@ -62,9 +64,9 @@ 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 suspend fun getMember(id: UInt): Member =
|
override fun getMember(id: UInt): Member =
|
||||||
if (members.containsKey(id)) members[id]!!
|
if (members.containsKey(id)) members[id]!!
|
||||||
else throw NoSuchElementException("No such member whose id is $id in group ${groupId.value.toLong()}")
|
else throw NoSuchElementException("No such member whose id is ${id.toLong()} in group ${groupId.value.toLong()}")
|
||||||
|
|
||||||
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))
|
||||||
@ -124,5 +126,25 @@ internal data class QQImpl internal constructor(override val bot: Bot, override
|
|||||||
*/
|
*/
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal data class MemberImpl(private val delegate: QQ, override val group: Group, override val permission: MemberPermission) : QQ by delegate, Member {
|
internal data class MemberImpl(private val delegate: QQ, override val group: Group, override val permission: MemberPermission) : QQ by delegate, Member {
|
||||||
override fun toString(): String = "Member(id=${this.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 {
|
||||||
|
require(durationSeconds > 0) { "duration must be greater than 0 second" }
|
||||||
|
|
||||||
|
if (permission == MemberPermission.OWNER) return false
|
||||||
|
|
||||||
|
when (group.getMember(bot.qqAccount).permission) {
|
||||||
|
MemberPermission.MEMBER -> return false
|
||||||
|
MemberPermission.OPERATOR -> if (permission == MemberPermission.OPERATOR) return false
|
||||||
|
MemberPermission.OWNER -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupPacket.Mute(qqAccount, group.internalId, sessionKey, id, durationSeconds.toUInt()).sendAndExpect<GroupPacket.MuteResponse>()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unmute(): Unit = bot.withSession {
|
||||||
|
GroupPacket.Mute(qqAccount, group.internalId, sessionKey, id, 0u).sendAndExpect<GroupPacket.MuteResponse>()
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ import net.mamoe.mirai.event.ListeningStatus
|
|||||||
import net.mamoe.mirai.event.Subscribable
|
import net.mamoe.mirai.event.Subscribable
|
||||||
import net.mamoe.mirai.event.events.BotEvent
|
import net.mamoe.mirai.event.events.BotEvent
|
||||||
import net.mamoe.mirai.utils.internal.inlinedRemoveIf
|
import net.mamoe.mirai.utils.internal.inlinedRemoveIf
|
||||||
|
import net.mamoe.mirai.utils.io.logStacktrace
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.coroutineContext
|
import kotlin.coroutines.coroutineContext
|
||||||
import kotlin.jvm.JvmField
|
import kotlin.jvm.JvmField
|
||||||
@ -98,7 +99,8 @@ internal class Handler<in E : Subscribable>
|
|||||||
return try {
|
return try {
|
||||||
withContext(context) { handler.invoke(event) }.also { if (it == ListeningStatus.STOPPED) this.complete() }
|
withContext(context) { handler.invoke(event) }.also { if (it == ListeningStatus.STOPPED) this.complete() }
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
this.completeExceptionally(e)
|
e.logStacktrace()
|
||||||
|
//this.completeExceptionally(e)
|
||||||
ListeningStatus.STOPPED
|
ListeningStatus.STOPPED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +133,8 @@ internal class HandlerWithBot<E : Subscribable> @PublishedApi internal construct
|
|||||||
return try {
|
return try {
|
||||||
withContext(context) { bot.handler(event) }.also { if (it == ListeningStatus.STOPPED) complete() }
|
withContext(context) { bot.handler(event) }.also { if (it == ListeningStatus.STOPPED) complete() }
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
completeExceptionally(e)
|
e.logStacktrace()
|
||||||
|
//completeExceptionally(e)
|
||||||
ListeningStatus.STOPPED
|
ListeningStatus.STOPPED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,8 @@ data class RawGroupInfo(
|
|||||||
MemberImpl(this@RawGroupInfo.owner.qq(), group, MemberPermission.OWNER),
|
MemberImpl(this@RawGroupInfo.owner.qq(), group, MemberPermission.OWNER),
|
||||||
this@RawGroupInfo.name,
|
this@RawGroupInfo.name,
|
||||||
this@RawGroupInfo.announcement,
|
this@RawGroupInfo.announcement,
|
||||||
ContactList(this@RawGroupInfo.members.mapValuesTo(MutableContactList()) { MemberImpl(it.key.qq(), group, MemberPermission.OWNER) })
|
ContactList(this@RawGroupInfo.members.mapValuesTo(MutableContactList<Member>()) { MemberImpl(it.key.qq(), group, it.value) }
|
||||||
|
.apply { put(owner, MemberImpl(owner.qq(), group, MemberPermission.OWNER)) })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,6 +120,29 @@ object GroupPacket : SessionPacketFactory<GroupPacket.GroupPacketResponse>() {
|
|||||||
writeZero(4)
|
writeZero(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁言群成员
|
||||||
|
*/
|
||||||
|
@PacketVersion(date = "2019.12.2", timVersion = "2.3.2 (21173)")
|
||||||
|
fun Mute(
|
||||||
|
bot: UInt,
|
||||||
|
groupInternalId: GroupInternalId,
|
||||||
|
sessionKey: SessionKey,
|
||||||
|
target: UInt,
|
||||||
|
/**
|
||||||
|
* 0 为取消
|
||||||
|
*/
|
||||||
|
timeSeconds: UInt
|
||||||
|
): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "MuteMember") {
|
||||||
|
writeUByte(0x7Eu)
|
||||||
|
writeGroup(groupInternalId)
|
||||||
|
writeByte(0x20)
|
||||||
|
writeByte(0x00)
|
||||||
|
writeByte(0x01)
|
||||||
|
writeQQ(target)
|
||||||
|
writeUInt(timeSeconds)
|
||||||
|
}
|
||||||
|
|
||||||
interface GroupPacketResponse : Packet
|
interface GroupPacketResponse : Packet
|
||||||
|
|
||||||
@NoLog
|
@NoLog
|
||||||
@ -126,11 +150,17 @@ object GroupPacket : SessionPacketFactory<GroupPacket.GroupPacketResponse>() {
|
|||||||
override fun toString(): String = "GroupPacket.MessageResponse"
|
override fun toString(): String = "GroupPacket.MessageResponse"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NoLog
|
||||||
|
object MuteResponse : Packet, GroupPacketResponse {
|
||||||
|
override fun toString(): String = "GroupPacket.MuteResponse"
|
||||||
|
}
|
||||||
|
|
||||||
@PacketVersion(date = "2019.11.27", timVersion = "2.3.2 (21173)")
|
@PacketVersion(date = "2019.11.27", timVersion = "2.3.2 (21173)")
|
||||||
@UseExperimental(ExperimentalStdlibApi::class)
|
@UseExperimental(ExperimentalStdlibApi::class)
|
||||||
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): GroupPacketResponse = handler.bot.withSession {
|
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): GroupPacketResponse {
|
||||||
return when (readUByte().toUInt()) {
|
return when (readUByte().toUInt()) {
|
||||||
0x2Au -> MessageResponse
|
0x2Au -> MessageResponse
|
||||||
|
0x7Eu -> MuteResponse // 成功: 7E 00 22 96 29 7B;
|
||||||
|
|
||||||
0x09u -> {
|
0x09u -> {
|
||||||
if (readByte().toInt() == 0) {
|
if (readByte().toInt() == 0) {
|
||||||
|
@ -97,6 +97,7 @@ abstract class KnownEventParserAndHandler<TPacket : Packet>(override val id: USh
|
|||||||
FriendAddRequestEventPacket,
|
FriendAddRequestEventPacket,
|
||||||
MemberGoneEventPacketHandler,
|
MemberGoneEventPacketHandler,
|
||||||
ConnectionOccupiedPacketHandler,
|
ConnectionOccupiedPacketHandler,
|
||||||
MemberJoinPacketHandler
|
MemberJoinPacketHandler,
|
||||||
|
MemberMuteEventPacketParserAndHandler
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.network.protocol.tim.packet.event
|
||||||
|
|
||||||
|
import com.soywiz.klock.TimeSpan
|
||||||
|
import com.soywiz.klock.seconds
|
||||||
|
import com.soywiz.klock.toTimeString
|
||||||
|
import kotlinx.io.core.ByteReadPacket
|
||||||
|
import kotlinx.io.core.discardExact
|
||||||
|
import kotlinx.io.core.readUInt
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.contact.Group
|
||||||
|
import net.mamoe.mirai.contact.Member
|
||||||
|
import net.mamoe.mirai.getGroup
|
||||||
|
import net.mamoe.mirai.qqAccount
|
||||||
|
|
||||||
|
// region mute
|
||||||
|
/**
|
||||||
|
* 某群成员被禁言事件
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
class MemberMuteEvent(
|
||||||
|
val member: Member,
|
||||||
|
override val duration: TimeSpan,
|
||||||
|
override val operator: Member
|
||||||
|
) : MuteEvent() {
|
||||||
|
override val group: Group get() = operator.group
|
||||||
|
override fun toString(): String = "MemberMuteEvent(member=${member.id}, group=${group.id}, operator=${operator.id}, duration=${duration.toTimeString()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器人被禁言事件
|
||||||
|
*/
|
||||||
|
class BeingMutedEvent(
|
||||||
|
override val duration: TimeSpan,
|
||||||
|
override val operator: Member
|
||||||
|
) : MuteEvent() {
|
||||||
|
override val group: Group get() = operator.group
|
||||||
|
override fun toString(): String = "BeingMutedEvent(group=${group.id}, operator=${operator.id}, duration=${duration.toTimeString()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class MuteEvent : EventOfMute() {
|
||||||
|
abstract override val operator: Member
|
||||||
|
abstract override val group: Group
|
||||||
|
abstract val duration: TimeSpan
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region unmute
|
||||||
|
/**
|
||||||
|
* 某群成员被解除禁言事件
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
class MemberUnmuteEvent(
|
||||||
|
val member: Member,
|
||||||
|
override val operator: Member
|
||||||
|
) : UnmuteEvent() {
|
||||||
|
override val group: Group get() = operator.group
|
||||||
|
override fun toString(): String = "MemberUnmuteEvent(member=${member.id}, group=${group.id}, operator=${operator.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器人被解除禁言事件
|
||||||
|
*/
|
||||||
|
class BeingUnmutedEvent(
|
||||||
|
override val operator: Member
|
||||||
|
) : UnmuteEvent() {
|
||||||
|
override val group: Group get() = operator.group
|
||||||
|
override fun toString(): String = "BeingUnmutedEvent(group=${group.id}, operator=${operator.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class UnmuteEvent : EventOfMute() {
|
||||||
|
abstract override val operator: Member
|
||||||
|
abstract override val group: Group
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
sealed class EventOfMute : EventPacket {
|
||||||
|
abstract val operator: Member
|
||||||
|
abstract val group: Group
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandler<EventOfMute>(0x02DCu) {
|
||||||
|
override suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): EventOfMute {
|
||||||
|
//取消
|
||||||
|
//00 00 00 11 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00
|
||||||
|
// 01 01
|
||||||
|
// 22 96 29 7B
|
||||||
|
// 0C 01
|
||||||
|
// 3E 03 3F A2
|
||||||
|
// 5D E5 12 EB
|
||||||
|
// 00 01
|
||||||
|
// 76 E4 B8 DD
|
||||||
|
// 00 00 00 00
|
||||||
|
|
||||||
|
// 禁言
|
||||||
|
//00 00 00 11 00 0A 00 04 01 00 00 00 00 0C 00 05 00 01 00
|
||||||
|
// 01
|
||||||
|
// 01
|
||||||
|
// 22 96 29 7B
|
||||||
|
// 0C
|
||||||
|
// 01
|
||||||
|
// 3E 03 3F A2
|
||||||
|
// 5D E5 07 85
|
||||||
|
// 00
|
||||||
|
// 01
|
||||||
|
// 76 E4 B8 DD
|
||||||
|
// 00 27 8D 00
|
||||||
|
discardExact(19)
|
||||||
|
discardExact(2)
|
||||||
|
val group = bot.getGroup(readUInt())
|
||||||
|
discardExact(2)
|
||||||
|
val operator = group.getMember(readUInt())
|
||||||
|
discardExact(4) //time
|
||||||
|
discardExact(2)
|
||||||
|
val memberQQ = readUInt()
|
||||||
|
|
||||||
|
val durationSeconds = readUInt().toInt()
|
||||||
|
return if (durationSeconds == 0) {
|
||||||
|
if (memberQQ == bot.qqAccount) {
|
||||||
|
BeingUnmutedEvent(operator)
|
||||||
|
} else {
|
||||||
|
MemberUnmuteEvent(group.getMember(memberQQ), operator)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val duration = durationSeconds.seconds
|
||||||
|
|
||||||
|
if (memberQQ == bot.qqAccount) {
|
||||||
|
BeingMutedEvent(duration, operator)
|
||||||
|
} else {
|
||||||
|
MemberMuteEvent(group.getMember(memberQQ), duration, operator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,7 @@ import kotlinx.io.core.String
|
|||||||
import kotlinx.io.core.discardExact
|
import kotlinx.io.core.discardExact
|
||||||
import kotlinx.io.core.readUInt
|
import kotlinx.io.core.readUInt
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.contact.Contact
|
import net.mamoe.mirai.contact.*
|
||||||
import net.mamoe.mirai.contact.Group
|
|
||||||
import net.mamoe.mirai.contact.MemberPermission
|
|
||||||
import net.mamoe.mirai.contact.QQ
|
|
||||||
import net.mamoe.mirai.event.BroadcastControllable
|
import net.mamoe.mirai.event.BroadcastControllable
|
||||||
import net.mamoe.mirai.event.events.BotEvent
|
import net.mamoe.mirai.event.events.BotEvent
|
||||||
import net.mamoe.mirai.getGroup
|
import net.mamoe.mirai.getGroup
|
||||||
@ -98,6 +95,7 @@ abstract class MessagePacketBase<TSubject : Contact> : EventPacket, BotEvent() {
|
|||||||
|
|
||||||
// region group message
|
// region group message
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
data class GroupMessage(
|
data class GroupMessage(
|
||||||
val group: Group,
|
val group: Group,
|
||||||
val senderName: String,
|
val senderName: String,
|
||||||
@ -110,6 +108,11 @@ data class GroupMessage(
|
|||||||
) : MessagePacket<Group>() {
|
) : MessagePacket<Group>() {
|
||||||
|
|
||||||
override val subject: Group get() = group
|
override val subject: Group get() = group
|
||||||
|
|
||||||
|
|
||||||
|
suspend inline fun At.member(): Member = group.getMember(this.target)
|
||||||
|
suspend inline fun UInt.member(): Member = group.getMember(this)
|
||||||
|
suspend inline fun Long.member(): Member = group.getMember(this.toUInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
@PacketVersion(date = "2019.11.2", timVersion = "2.3.2 (21173)")
|
@PacketVersion(date = "2019.11.2", timVersion = "2.3.2 (21173)")
|
||||||
|
@ -22,7 +22,7 @@ import java.awt.datatransfer.DataFlavor
|
|||||||
/**
|
/**
|
||||||
* How to run:
|
* How to run:
|
||||||
*
|
*
|
||||||
* `gradle run`
|
* `gradle :mirai-debug:run`
|
||||||
*/
|
*/
|
||||||
class Application : App(HexDebuggerGui::class, Styles::class)
|
class Application : App(HexDebuggerGui::class, Styles::class)
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ class HexDebuggerGui : View("s") {
|
|||||||
|
|
||||||
override val root = hbox {
|
override val root = hbox {
|
||||||
//prefWidth = 735.0
|
//prefWidth = 735.0
|
||||||
minHeight = 240.0
|
minHeight = 300.0
|
||||||
prefHeight = minHeight
|
prefHeight = minHeight
|
||||||
|
|
||||||
input = textarea {
|
input = textarea {
|
||||||
|
@ -116,8 +116,8 @@ object PacketDebugger {
|
|||||||
* 7. 运行完 `mov eax,dword ptr ss:[ebp+10]`
|
* 7. 运行完 `mov eax,dword ptr ss:[ebp+10]`
|
||||||
* 8. 查看内存, `eax` 到 `eax+10` 的 16 字节就是 `sessionKey`
|
* 8. 查看内存, `eax` 到 `eax+10` 的 16 字节就是 `sessionKey`
|
||||||
*/
|
*/
|
||||||
val sessionKey: SessionKey = SessionKey("F7 3C 31 B5 E1 F1 E5 6A FA F7 95 79 AE 19 30 01".hexToBytes())
|
val sessionKey: SessionKey = SessionKey("06 23 F8 09 0D 2D 37 BE 2E FE 90 3A 7D E5 8F B1".hexToBytes())
|
||||||
const val qq: UInt = 761025446u
|
const val qq: UInt = 1040400290u
|
||||||
|
|
||||||
val IgnoredPacketIdList: List<PacketId> = listOf(
|
val IgnoredPacketIdList: List<PacketId> = listOf(
|
||||||
KnownPacketId.FRIEND_ONLINE_STATUS_CHANGE,
|
KnownPacketId.FRIEND_ONLINE_STATUS_CHANGE,
|
||||||
@ -152,7 +152,9 @@ object PacketDebugger {
|
|||||||
decodedBody = it.readBytes()
|
decodedBody = it.readBytes()
|
||||||
ByteReadPacket(decodedBody)
|
ByteReadPacket(decodedBody)
|
||||||
}
|
}
|
||||||
.decode(id, sequenceId, DebugNetworkHandler)
|
.runCatching {
|
||||||
|
decode(id, sequenceId, DebugNetworkHandler)
|
||||||
|
}.getOrElse { it.printStackTrace(); null }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
package demo.gentleman
|
package demo.gentleman
|
||||||
|
|
||||||
|
import com.soywiz.klock.months
|
||||||
|
import com.soywiz.klock.seconds
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@ -9,8 +11,10 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.mamoe.mirai.*
|
import net.mamoe.mirai.*
|
||||||
import net.mamoe.mirai.contact.MemberPermission
|
import net.mamoe.mirai.contact.MemberPermission
|
||||||
|
import net.mamoe.mirai.contact.mute
|
||||||
import net.mamoe.mirai.event.Subscribable
|
import net.mamoe.mirai.event.Subscribable
|
||||||
import net.mamoe.mirai.event.subscribeAlways
|
import net.mamoe.mirai.event.subscribeAlways
|
||||||
|
import net.mamoe.mirai.event.subscribeGroupMessages
|
||||||
import net.mamoe.mirai.event.subscribeMessages
|
import net.mamoe.mirai.event.subscribeMessages
|
||||||
import net.mamoe.mirai.message.At
|
import net.mamoe.mirai.message.At
|
||||||
import net.mamoe.mirai.message.Image
|
import net.mamoe.mirai.message.Image
|
||||||
@ -59,17 +63,32 @@ suspend fun main() {
|
|||||||
it.approve()
|
it.approve()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bot.subscribeGroupMessages {
|
||||||
|
"群资料" reply {
|
||||||
|
group.updateGroupInfo().toString().reply()
|
||||||
|
}
|
||||||
|
|
||||||
|
startsWith("mt2months") {
|
||||||
|
val at: At by message
|
||||||
|
at.target.member().mute(1.months)
|
||||||
|
}
|
||||||
|
|
||||||
|
startsWith("mute") {
|
||||||
|
val at: At by message
|
||||||
|
at.target.member().mute(30.seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
startsWith("unmute") {
|
||||||
|
val at: At by message
|
||||||
|
at.target.member().unmute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bot.subscribeMessages {
|
bot.subscribeMessages {
|
||||||
case("at me") { At(sender).reply() }
|
case("at me") { At(sender).reply() }
|
||||||
|
|
||||||
"你好" reply "你好!"
|
"你好" reply "你好!"
|
||||||
|
|
||||||
"群资料" reply {
|
|
||||||
if (this is GroupMessage) {
|
|
||||||
group.updateGroupInfo().toString().reply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
startsWith("profile", removePrefix = true) {
|
startsWith("profile", removePrefix = true) {
|
||||||
val account = it.trim()
|
val account = it.trim()
|
||||||
if (account.isNotEmpty()) {
|
if (account.isNotEmpty()) {
|
||||||
|
Loading…
Reference in New Issue
Block a user