Add corresponding events

This commit is contained in:
Him188 2019-11-02 12:30:59 +08:00
parent 8bc59820bc
commit 4ddfc0b509
14 changed files with 177 additions and 101 deletions

View File

@ -32,6 +32,7 @@ Mirai 在 JVM 平台采用插件模式运行,同时提供独立的跨平台核
- Android客户端上线/下线(10/18) - Android客户端上线/下线(10/18)
- 上传并发送好友/群图片(10/21, 10/26) - 上传并发送好友/群图片(10/21, 10/26)
- 群员权限改变(11/2) - 群员权限改变(11/2)
- 发起会话(11/2)
计划中: 添加好友 计划中: 添加好友

View File

@ -1,9 +1,18 @@
package net.mamoe.mirai.event.events package net.mamoe.mirai.event.events
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.Cancellable
import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.Event
import net.mamoe.mirai.message.ImageId
abstract class BotEvent(val bot: Bot) : Event() abstract class BotEvent(val bot: Bot) : Event()
class BotLoginSucceedEvent(bot: Bot) : BotEvent(bot) class BotLoginSucceedEvent(bot: Bot) : BotEvent(bot)
/**
* 上传好友图片时, 服务器返回好友图片 ID 事件.
*
* 在这之后图片将会被上传到服务器.
*/
class FriendImageIdObtainedEvent(bot: Bot, val imageId: ImageId) : BotEvent(bot), Cancellable

View File

@ -1,12 +1,17 @@
@file:Suppress("unused")
package net.mamoe.mirai.event.events package net.mamoe.mirai.event.events
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.utils.OnlineStatus
/**
abstract class FriendEvent(bot: Bot, val sender: QQ) : BotEvent(bot) * 好友事件
*/
sealed class FriendEvent(bot: Bot, val sender: QQ) : BotEvent(bot)
/** /**
* 接受好友消息事件 * 接受好友消息事件
@ -20,3 +25,13 @@ class FriendMessageEvent(bot: Bot, sender: QQ, val message: MessageChain) : Frie
suspend inline fun reply(message: MessageChain) = sender.sendMessage(message)//shortcut suspend inline fun reply(message: MessageChain) = sender.sendMessage(message)//shortcut
} }
/**
* 好友发起会话事件. 即好友在消息输入框内输入任意内容.
*/
class FriendConversationInitializedEvent(bot: Bot, sender: QQ) : FriendEvent(bot, sender)
/**
* 好友在线状态改变事件
*/
class FriendOnlineStatusChangedEvent(bot: Bot, sender: QQ, val newStatus: OnlineStatus) : FriendEvent(bot, sender)

View File

@ -1,3 +1,5 @@
@file:Suppress("unused")
package net.mamoe.mirai.event.events package net.mamoe.mirai.event.events
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
@ -10,8 +12,9 @@ import net.mamoe.mirai.network.protocol.tim.packet.event.SenderPermission
abstract class GroupEvent(bot: Bot, val group: Group) : BotEvent(bot) abstract class GroupEvent(bot: Bot, val group: Group) : BotEvent(bot)
/**
@Suppress("unused") * 群消息
*/
class GroupMessageEvent( class GroupMessageEvent(
bot: Bot, bot: Bot,
group: Group, group: Group,
@ -26,3 +29,24 @@ class GroupMessageEvent(
suspend inline fun reply(message: MessageChain) = group.sendMessage(message) suspend inline fun reply(message: MessageChain) = group.sendMessage(message)
} }
/**
* 群成员权限改变
*/
class MemberPermissionChangedEvent(
bot: Bot,
group: Group,
val member: QQ,
val kind: Kind
) : GroupEvent(bot, group) {
enum class Kind {
/**
* 变成管理员
*/
BECOME_OPERATOR,
/**
* 不再是管理员
*/
NO_LONGER_OPERATOR,
} // TODO: 2019/11/2 变成群主的情况
}

View File

@ -8,27 +8,45 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
/* Abstract */ /* Abstract */
/**
sealed class PacketEvent<out P : Packet>(bot: Bot, open val packet: P) : BotEvent(bot) * 数据包相关事件
*
* @param P 指代数据包的类型. 这个类型是 **invariant(不变的)**
*/
sealed class PacketEvent<P : Packet>(bot: Bot, open val packet: P) : BotEvent(bot)
/* Client to Server */ /* Client to Server */
sealed class ClientPacketEvent(bot: Bot, packet: OutgoingPacket) : PacketEvent<OutgoingPacket>(bot, packet) /**
* 发送给服务器的数据包的相关事件
*/
sealed class OutgoingPacketEvent(bot: Bot, packet: OutgoingPacket) : PacketEvent<OutgoingPacket>(bot, packet)
/** /**
* 包已发送. 不可被取消 * 包已发送, 此时包数据已完全发送至服务器, 且包已被关闭.
*
* 不可被取消
*/ */
class PacketSentEvent(bot: Bot, packet: OutgoingPacket) : ClientPacketEvent(bot, packet) class PacketSentEvent(bot: Bot, packet: OutgoingPacket) : OutgoingPacketEvent(bot, packet)
/** /**
* 包发送前. 可被取消 * 包发送前, 此时包数据已经编码完成.
*
* 可被取消
*/ */
class BeforePacketSendEvent(bot: Bot, packet: OutgoingPacket) : ClientPacketEvent(bot, packet), Cancellable class BeforePacketSendEvent(bot: Bot, packet: OutgoingPacket) : OutgoingPacketEvent(bot, packet), Cancellable
/* Server to Client */ /* Server to Client */
sealed class ServerPacketEvent<out P : ServerPacket>(bot: Bot, packet: P) : PacketEvent<P>(bot, packet) /**
* 来自服务器的数据包的相关事件
*/
sealed class ServerPacketEvent<P : ServerPacket>(bot: Bot, packet: P) : PacketEvent<P>(bot, packet)
class ServerPacketReceivedEvent(bot: Bot, packet: ServerPacket) : ServerPacketEvent<ServerPacket>(bot, packet) /**
* 服务器数据包接收事件. 此时包已经解密完成.
*/
class ServerPacketReceivedEvent<P : ServerPacket>(bot: Bot, packet: P) : ServerPacketEvent<P>(bot, packet),
Cancellable

View File

@ -186,8 +186,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
} }
private suspend inline fun <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> { private suspend inline fun <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> {
val receiving = CompletableDeferred<P>() val receiving = CompletableDeferred<P>(coroutineContext[Job])
subscribe<ServerPacketReceivedEvent> { subscribe<ServerPacketReceivedEvent<*>> {
if (it.packet is P && it.bot === bot) { if (it.packet is P && it.bot === bot) {
receiving.complete(it.packet) receiving.complete(it.packet)
ListeningStatus.STOPPED ListeningStatus.STOPPED

View File

@ -29,14 +29,8 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
@ExperimentalStdlibApi @ExperimentalStdlibApi
override suspend fun onPacketReceived(packet: ServerPacket) = with(session) { override suspend fun onPacketReceived(packet: ServerPacket): Unit = with(session) {
when (packet) { when (packet) {
//is AddFriendPacket.Response -> {
// this.uploadImageSessions.forEach {
// it.onPacketReceived(packet)
// }
//}
is RequestSKeyPacket.Response -> { is RequestSKeyPacket.Response -> {
sKey = packet.sKey sKey = packet.sKey
cookies = "uin=o$qqAccount;skey=$sKey;" cookies = "uin=o$qqAccount;skey=$sKey;"

View File

@ -3,9 +3,9 @@ package net.mamoe.mirai.network.protocol.tim.handler
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.GroupId import net.mamoe.mirai.contact.GroupId
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.groupId
import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.FriendMessageEvent import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.event.events.GroupMessageEvent
import net.mamoe.mirai.getGroup import net.mamoe.mirai.getGroup
import net.mamoe.mirai.getQQ import net.mamoe.mirai.getQQ
import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.MessageChain
@ -13,12 +13,10 @@ import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.distributePacket import net.mamoe.mirai.network.distributePacket
import net.mamoe.mirai.network.protocol.tim.packet.FriendOnlineStatusChangedPacket import net.mamoe.mirai.network.protocol.tim.packet.FriendOnlineStatusChangedPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket
import net.mamoe.mirai.network.protocol.tim.packet.action.SendFriendMessagePacket import net.mamoe.mirai.network.protocol.tim.packet.action.SendFriendMessagePacket
import net.mamoe.mirai.network.protocol.tim.packet.action.SendGroupMessagePacket import net.mamoe.mirai.network.protocol.tim.packet.action.SendGroupMessagePacket
import net.mamoe.mirai.network.protocol.tim.packet.event.FriendMessageEventPacket import net.mamoe.mirai.network.protocol.tim.packet.event.*
import net.mamoe.mirai.network.protocol.tim.packet.event.GroupMessageEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.event.IgnoredServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.event.ServerGroupUploadFileEventPacket
import net.mamoe.mirai.network.qqAccount import net.mamoe.mirai.network.qqAccount
/** /**
@ -37,35 +35,27 @@ class EventPacketHandler(session: BotSession) : PacketHandler(session) {
} }
is FriendMessageEventPacket -> { is FriendMessageEventPacket -> {
if (!packet.isPrevious) { if (!packet.isPrevious) FriendMessageEvent(bot, bot.getQQ(packet.qq), packet.message).broadcast()
FriendMessageEvent(bot, bot.getQQ(packet.qq), packet.message).broadcast()
}
} }
is GroupMessageEventPacket -> { is GroupMessageEventPacket -> {
if (packet.qq == bot.account.id) return if (packet.qq == bot.account.id) return
GroupMessageEvent( GroupMessageEvent(
bot, bot, bot.getGroup(GroupId(packet.groupNumber)), bot.getQQ(packet.qq), packet.message, packet.senderPermission, packet.senderName
group = bot.getGroup(GroupId(packet.groupNumber)),
sender = bot.getQQ(packet.qq),
message = packet.message,
senderName = packet.senderName,
senderPermission = packet.senderPermission
).broadcast() ).broadcast()
} }
is FriendConversationInitializedEventPacket -> FriendConversationInitializedEvent(bot, bot.getQQ(packet.qq)).broadcast()
is FriendOnlineStatusChangedPacket -> FriendOnlineStatusChangedEvent(bot, bot.getQQ(packet.qq), packet.status).broadcast()
is FriendImageIdRequestPacket.Response -> packet.imageId?.let { FriendImageIdObtainedEvent(bot, it) }
is GroupMemberPermissionChangedEventPacket -> MemberPermissionChangedEvent(
bot, bot.getGroup(packet.groupId.groupId()), bot.getQQ(packet.qq), packet.kind
).broadcast()
is FriendOnlineStatusChangedPacket.Encrypted -> distributePacket(packet.decrypt(sessionKey)) is FriendOnlineStatusChangedPacket.Encrypted -> distributePacket(packet.decrypt(sessionKey))
is FriendOnlineStatusChangedPacket -> {
//TODO
}
is IgnoredServerEventPacket -> {
}
else -> {
//ignored
}
} }
} }

View File

@ -0,0 +1,46 @@
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused")
package net.mamoe.mirai.network.protocol.tim.packet
import net.mamoe.mirai.event.Event
import kotlin.reflect.KClass
/**
* ID. 除特殊外, [OutgoingPacketBuilder] 都需要这个注解来指定包 ID.
*/
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class AnnotatedId(
val id: KnownPacketId
)
inline val AnnotatedId.value: UShort get() = id.value
/**
* 标记这个包对应的事件.
* 这个注解应该被标记在 [ServerPacket]
*/
@MustBeDocumented
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS)
annotation class CorrespondingEvent(
val eventClass: KClass<out Event>
)
/**
* 版本信息
*/
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
internal annotation class PacketVersion(val date: String, val timVersion: String)
/**
* 带有这个注解的 [Packet], 将不会被记录在 log .
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class NoLog

View File

@ -11,8 +11,6 @@ import kotlin.properties.Delegates
/** /**
* 好友在线状态改变 * 好友在线状态改变
*
* TODO 真的是在线状态改变么
*/ */
@AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE) @AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE)
class FriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket(input) { class FriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket(input) {

View File

@ -37,17 +37,6 @@ val Packet.idHexString: String get() = (packetId.value.toInt().shl(16) or sequen
// region Packet id // region Packet id
/**
* ID
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS)
annotation class AnnotatedId(
val id: KnownPacketId
)
inline val AnnotatedId.value: UShort get() = id.value
/** /**
* 通过 [value] 匹配一个 [KnownPacketId], 无匹配则返回一个 [UnknownPacketId]. * 通过 [value] 匹配一个 [KnownPacketId], 无匹配则返回一个 [UnknownPacketId].
*/ */
@ -110,15 +99,6 @@ enum class KnownPacketId(override inline val value: UShort, internal inline val
// region Internal utils // region Internal utils
/**
* 版本信息
*/
@Suppress("unused")
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
internal annotation class PacketVersion(val date: String, val timVersion: String)
private object PacketNameFormatter { private object PacketNameFormatter {
private var longestNameLength: Int = 43 private var longestNameLength: Int = 43
fun adjustName(name: String): String = fun adjustName(name: String): String =
@ -160,13 +140,6 @@ private object IgnoreIdListInclude : List<String> by listOf(
"RefVolatile" "RefVolatile"
) )
/**
* 带有这个注解的 [Packet], 将不会被记录在 log .
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class NoLog
/** /**
* 这个方法会翻倍内存占用, 考虑修改. * 这个方法会翻倍内存占用, 考虑修改.
*/ */

View File

@ -10,6 +10,7 @@ import io.ktor.http.userAgent
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.events.FriendImageIdObtainedEvent
import net.mamoe.mirai.message.ImageId import net.mamoe.mirai.message.ImageId
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.*
@ -254,6 +255,7 @@ object FriendImageIdRequestPacket : OutgoingPacketBuilder {
} }
} }
@CorrespondingEvent(FriendImageIdObtainedEvent::class)
@AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID) @AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
@PacketVersion(date = "2019.11.1", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.11.1", timVersion = "2.3.2.21173")
class Response(input: ByteReadPacket) : ResponsePacket(input) { class Response(input: ByteReadPacket) : ResponsePacket(input) {

View File

@ -5,8 +5,12 @@ package net.mamoe.mirai.network.protocol.tim.packet.event
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import kotlinx.io.core.readUInt import kotlinx.io.core.readUInt
import net.mamoe.mirai.event.events.FriendConversationInitializedEvent
import net.mamoe.mirai.event.events.MemberPermissionChangedEvent
import net.mamoe.mirai.network.protocol.tim.packet.CorrespondingEvent
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
import net.mamoe.mirai.utils.io.readString import net.mamoe.mirai.utils.io.readString
import kotlin.properties.Delegates
/** /**
@ -32,30 +36,36 @@ class GroupMemberNickChangedEventPacket(input: ByteReadPacket, eventIdentity: Ev
override fun decode() { override fun decode() {
// GroupId VarInt // GroupId VarInt
// 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 01 00 00 F3 66 00 00 00 05 00 00 00 EE 00 00 00 05 // 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 01 00 00 F3 66 00 00 00 05 00 00 00 EE 00 00 00 05
// ? 数据中没有哪个人的昵称改变了 // TODO ? 数据中没有哪个人的昵称改变了
} }
} }
/**
* 好友发起会话, 即在输入框输入了任意内容.
*/
@CorrespondingEvent(FriendConversationInitializedEvent::class)
@PacketVersion(date = "2019.11.2", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.11.2", timVersion = "2.3.2.21173")
class GroupMemberPermissionChangedPacket internal constructor( class FriendConversationInitializedEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) :
ServerEventPacket(input, eventIdentity) {
var qq: UInt by Delegates.notNull()
// 00 00 00 00 3E 03 3F A2 00
override fun decode() = with(input) {
discardExact(4)// 00 00 00 00
qq = readUInt()
}
}
@CorrespondingEvent(MemberPermissionChangedEvent::class)
@PacketVersion(date = "2019.11.2", timVersion = "2.3.2.21173")
class GroupMemberPermissionChangedEventPacket internal constructor(
input: ByteReadPacket, input: ByteReadPacket,
eventIdentity: EventPacketIdentity eventIdentity: EventPacketIdentity
) : ) :
ServerEventPacket(input, eventIdentity) { ServerEventPacket(input, eventIdentity) {
val groupId: UInt get() = eventIdentity.from val groupId: UInt get() = eventIdentity.from
var qq: UInt = 0u var qq: UInt = 0u
lateinit var kind: Kind lateinit var kind: MemberPermissionChangedEvent.Kind
enum class Kind {
/**
* 变成管理员
*/
BECOME_OPERATOR,
/**
* 不再是管理员
*/
NO_LONGER_OPERATOR,
} // TODO: 2019/11/2 变成群主的情况
override fun decode(): Unit = with(input) { override fun decode(): Unit = with(input) {
// 群里一个人变成管理员: // 群里一个人变成管理员:
@ -65,8 +75,8 @@ class GroupMemberPermissionChangedPacket internal constructor(
discardExact(remaining - 5) discardExact(remaining - 5)
qq = readUInt() qq = readUInt()
kind = when (readByte().toInt()) { kind = when (readByte().toInt()) {
0x00 -> Kind.NO_LONGER_OPERATOR 0x00 -> MemberPermissionChangedEvent.Kind.NO_LONGER_OPERATOR
0x01 -> Kind.BECOME_OPERATOR 0x01 -> MemberPermissionChangedEvent.Kind.BECOME_OPERATOR
else -> { else -> {
error("Could not determine permission change kind") error("Could not determine permission change kind")
} }

View File

@ -58,21 +58,17 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
} }
} }
0x002Du -> ServerGroupUploadFileEventPacket(input, eventIdentity) 0x002Du -> ServerGroupUploadFileEventPacket(input, eventIdentity)
0x002Cu -> GroupMemberPermissionChangedEventPacket(input, eventIdentity)
0x002Cu -> GroupMemberPermissionChangedPacket(input, eventIdentity)
/* /*
* *
inline GROUP_MEMBER_NICK_CHANGED(0x002Fu, null), inline GROUP_MEMBER_NICK_CHANGED(0x002Fu, null),
inline GROUP_MEMBER_PERMISSION_CHANGED(0x002Cu, null), inline GROUP_MEMBER_PERMISSION_CHANGED(0x002Cu, null),
*/ */
0x0052u -> GroupMessageEventPacket(input, eventIdentity) 0x0052u -> GroupMessageEventPacket(input, eventIdentity)
0x00A6u -> FriendMessageEventPacket(input, eventIdentity) 0x00A6u -> FriendMessageEventPacket(input, eventIdentity)
0x0079u -> FriendConversationInitializedEventPacket(input, eventIdentity)
0x0079u,
0x0210u // "对方正在输入..." 0x0210u // "对方正在输入..."
-> IgnoredServerEventPacket(input, eventIdentity) -> IgnoredServerEventPacket(input, eventIdentity)
@ -125,7 +121,7 @@ class IgnoredServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacket
* Unknown event * Unknown event
*/ */
class UnknownServerEventPacket( class UnknownServerEventPacket(
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")// 让它能被日志记录
val eventId: ByteArray, val eventId: ByteArray,
private val showData: Boolean = false, private val showData: Boolean = false,
input: ByteReadPacket, input: ByteReadPacket,