Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
2b477b3b
Commit
2b477b3b
authored
Nov 08, 2019
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Redesign events
parent
745200f6
Changes
58
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
58 changed files
with
1154 additions
and
1198 deletions
+1154
-1198
mirai-core/build.gradle.kts
mirai-core/build.gradle.kts
+2
-0
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/network/protocol/tim/NetworkDispatcherAndroid.kt
...oe/mirai/network/protocol/tim/NetworkDispatcherAndroid.kt
+1
-1
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/PlatformLoggerAndroid.kt
...ain/kotlin/net/mamoe/mirai/utils/PlatformLoggerAndroid.kt
+11
-11
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
.../src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
+24
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
...onMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
+33
-98
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
...c/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
+11
-4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
...rc/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
+31
-25
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
...mmonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+10
-10
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/FriendEvents.kt
...nMain/kotlin/net.mamoe.mirai/event/events/FriendEvents.kt
+0
-42
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/GroupEvents.kt
...onMain/kotlin/net.mamoe.mirai/event/events/GroupEvents.kt
+0
-52
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/PacketEvents.kt
...nMain/kotlin/net.mamoe.mirai/event/events/PacketEvents.kt
+1
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
.../net.mamoe.mirai/event/internal/InternalEventListeners.kt
+12
-12
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
.../src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
+17
-9
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
...n/net.mamoe.mirai/message/internal/MessageDataInternal.kt
+6
-5
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
....mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
+44
-27
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt
....mirai/network/protocol/tim/handler/EventPacketHandler.kt
+0
-69
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Annotations.kt
...et.mamoe.mirai/network/protocol/tim/packet/Annotations.kt
+3
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Decrypters.kt
...net.mamoe.mirai/network/protocol/tim/packet/Decrypters.kt
+1
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/OutgoingPacket.kt
...mamoe.mirai/network/protocol/tim/packet/OutgoingPacket.kt
+8
-9
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt
...lin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt
+4
-305
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketFactory.kt
....mamoe.mirai/network/protocol/tim/packet/PacketFactory.kt
+73
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt
...n/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt
+82
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt
...t.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt
+0
-32
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt
....mirai/network/protocol/tim/packet/UnknownServerPacket.kt
+0
-42
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/AddContact.kt
...oe.mirai/network/protocol/tim/packet/action/AddContact.kt
+5
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GradeInfo.kt
...moe.mirai/network/protocol/tim/packet/action/GradeInfo.kt
+0
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadImage.kt
...e.mirai/network/protocol/tim/packet/action/UploadImage.kt
+92
-76
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/AndroidOnlineStatusChange.kt
...rk/protocol/tim/packet/event/AndroidOnlineStatusChange.kt
+33
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/EventPacket.kt
...oe.mirai/network/protocol/tim/packet/event/EventPacket.kt
+6
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/EventPacketFactory.kt
...i/network/protocol/tim/packet/event/EventPacketFactory.kt
+106
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/FriendConversationIniliaze.kt
...k/protocol/tim/packet/event/FriendConversationIniliaze.kt
+23
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/FriendOnlineStatusChanged.kt
...rk/protocol/tim/packet/event/FriendOnlineStatusChanged.kt
+17
-18
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/GroupFileUpload.kt
...irai/network/protocol/tim/packet/event/GroupFileUpload.kt
+22
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Ignored.kt
....mamoe.mirai/network/protocol/tim/packet/event/Ignored.kt
+18
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberPermission.kt
...rai/network/protocol/tim/packet/event/MemberPermission.kt
+46
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Message.kt
....mamoe.mirai/network/protocol/tim/packet/event/Message.kt
+173
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt
...i/network/protocol/tim/packet/event/ServerEventPackets.kt
+0
-237
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Unknown.kt
....mamoe.mirai/network/protocol/tim/packet/event/Unknown.kt
+34
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Captcha.kt
....mamoe.mirai/network/protocol/tim/packet/login/Captcha.kt
+2
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ChangeOnlineStatusPacket.kt
...ork/protocol/tim/packet/login/ChangeOnlineStatusPacket.kt
+3
-1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Heartbeat.kt
...amoe.mirai/network/protocol/tim/packet/login/Heartbeat.kt
+0
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/PasswordSubmission.kt
...i/network/protocol/tim/packet/login/PasswordSubmission.kt
+86
-39
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt
...et.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt
+2
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
...rc/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
+6
-3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
.../commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
+3
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
...rc/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
+2
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformLogger.kt
...commonMain/kotlin/net.mamoe.mirai/utils/PlatformLogger.kt
+0
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt
...rc/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt
+3
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/TEA.kt
...i-core/src/commonMain/kotlin/net.mamoe.mirai/utils/TEA.kt
+51
-14
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
...monMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
+2
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt
...monMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt
+2
-1
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/SubscribersJvm.kt
...rc/jvmMain/kotlin/net/mamoe/mirai/event/SubscribersJvm.kt
+1
-1
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/NetworkDispatcherJvm.kt
.../mamoe/mirai/network/protocol/tim/NetworkDispatcherJvm.kt
+1
-2
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
...n/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
+18
-3
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
...rc/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
+13
-22
mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
...i-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
+7
-6
mirai-demos/mirai-demo-gentleman/build.gradle
mirai-demos/mirai-demo-gentleman/build.gradle
+1
-0
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
...rai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
+2
-2
No files found.
mirai-core/build.gradle.kts
View file @
2b477b3b
...
...
@@ -68,6 +68,8 @@ kotlin {
dependsOn
(
commonMain
)
implementation
(
"org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
)
implementation
(
"org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
)
implementation
(
"io.ktor:ktor-client-android:$ktorVersion"
)
}
...
...
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/network/protocol/tim/NetworkDispatcherAndroid.kt
View file @
2b477b3b
...
...
@@ -9,4 +9,4 @@ import java.util.concurrent.Executors
*
* JVM: 独立的 4 thread 调度器
*/
actual
val
NetworkDispatcher
:
CoroutineDispatcher
=
Executors
.
newFixedThreadPool
(
4
).
asCoroutineDispatcher
()
\ No newline at end of file
internal
actual
val
NetworkDispatcher
:
CoroutineDispatcher
=
Executors
.
newFixedThreadPool
(
4
).
asCoroutineDispatcher
()
\ No newline at end of file
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/PlatformLoggerAndroid.kt
View file @
2b477b3b
...
...
@@ -8,44 +8,44 @@ actual typealias PlatformLogger = AndroidLogger
* Android 平台的默认的日志记录器, 使用 [Log]
* 不应该直接构造这个类的实例. 需使用 [DefaultLogger]
*/
open
class
AndroidLogger
(
override
val
identity
:
String
?)
:
MiraiLoggerPlatformBase
()
{
open
class
AndroidLogger
(
override
val
identity
:
String
?
=
"Mirai"
)
:
MiraiLoggerPlatformBase
()
{
override
fun
verbose0
(
any
:
Any
?)
{
Log
.
v
(
identity
,
any
.
toString
()
)
Log
.
v
(
identity
,
any
?.
toString
()
?:
""
)
}
override
fun
verbose0
(
message
:
String
?,
e
:
Throwable
?)
{
Log
.
v
(
identity
,
message
.
toString
()
,
e
)
Log
.
v
(
identity
,
message
?:
""
,
e
)
}
override
fun
debug0
(
any
:
Any
?)
{
Log
.
d
(
identity
,
any
.
toString
()
)
Log
.
d
(
identity
,
any
?.
toString
()
?:
""
)
}
override
fun
debug0
(
message
:
String
?,
e
:
Throwable
?)
{
Log
.
d
(
identity
,
message
.
toString
()
,
e
)
Log
.
d
(
identity
,
message
?:
""
,
e
)
}
override
fun
info0
(
any
:
Any
?)
{
Log
.
i
(
identity
,
any
.
toString
()
)
Log
.
i
(
identity
,
any
?.
toString
()
?:
""
)
}
override
fun
info0
(
message
:
String
?,
e
:
Throwable
?)
{
Log
.
i
(
identity
,
message
.
toString
()
,
e
)
Log
.
i
(
identity
,
message
?:
""
,
e
)
}
override
fun
warning0
(
any
:
Any
?)
{
Log
.
w
(
identity
,
any
.
toString
()
)
Log
.
w
(
identity
,
any
?.
toString
()
?:
""
)
}
override
fun
warning0
(
message
:
String
?,
e
:
Throwable
?)
{
Log
.
w
(
identity
,
message
.
toString
()
,
e
)
Log
.
w
(
identity
,
message
?:
""
,
e
)
}
override
fun
error0
(
any
:
Any
?)
{
Log
.
e
(
identity
,
any
.
toString
()
)
Log
.
e
(
identity
,
any
?.
toString
()
?:
""
)
}
override
fun
error0
(
message
:
String
?,
e
:
Throwable
?)
{
Log
.
e
(
identity
,
message
.
toString
()
,
e
)
Log
.
e
(
identity
,
message
?:
""
,
e
)
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
View file @
2b477b3b
...
...
@@ -9,10 +9,13 @@ import net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.message.singleChain
import
net.mamoe.mirai.network.BotSession
import
net.mamoe.mirai.network.protocol.tim.handler.EventPacketHandler
import
net.mamoe.mirai.network.protocol.tim.packet.action.RequestProfileDetailsPacket
import
net.mamoe.mirai.network.protocol.tim.packet.action.RequestProfileDetailsResponse
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.sessionKey
import
net.mamoe.mirai.qqAccount
import
net.mamoe.mirai.sendPacket
import
net.mamoe.mirai.utils.SuspendLazy
import
net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
import
net.mamoe.mirai.withSession
...
...
@@ -89,7 +92,7 @@ class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot,
get
()
=
TODO
(
"Implementing group members is less important"
)
override
suspend
fun
sendMessage
(
message
:
MessageChain
)
{
bot
.
network
[
EventPacketHandler
].
sendGroupMessage
(
this
,
message
)
bot
.
sendPacket
(
SendGroupMessagePacket
(
bot
.
qqAccount
,
internalId
,
bot
.
sessionKey
,
message
)
)
}
companion
object
...
...
@@ -114,7 +117,7 @@ open class QQ internal constructor(bot: Bot, id: UInt) : Contact(bot, id) {
val
profile
:
Deferred
<
Profile
>
by
bot
.
network
.
SuspendLazy
{
updateProfile
()
}
override
suspend
fun
sendMessage
(
message
:
MessageChain
)
{
bot
.
network
[
EventPacketHandler
].
sendFriendMessage
(
this
,
message
)
bot
.
sendPacket
(
SendFriendMessagePacket
(
bot
.
qqAccount
,
id
,
bot
.
sessionKey
,
message
)
)
}
/**
...
...
@@ -154,6 +157,24 @@ class Member internal constructor(bot: Bot, id: UInt, val group: Group) : QQ(bot
}
}
/**
* 群成员的权限
*/
enum
class
MemberPermission
{
/**
* 群主
*/
OWNER
,
/**
* 管理员
*/
OPERATOR
,
/**
* 一般群成员
*/
MEMBER
;
}
/**
* 个人资料
*/
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
View file @
2b477b3b
...
...
@@ -3,83 +3,21 @@
package
net.mamoe.mirai.event
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.event.events.BotEvent
import
net.mamoe.mirai.event.events.FriendMessageEvent
import
net.mamoe.mirai.event.events.GroupMessageEvent
import
net.mamoe.mirai.message.*
import
net.mamoe.mirai.utils.ExternalImage
import
net.mamoe.mirai.utils.sendTo
import
net.mamoe.mirai.utils.upload
import
net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.any
import
net.mamoe.mirai.network.protocol.tim.packet.event.FriendMessage
import
net.mamoe.mirai.network.protocol.tim.packet.event.GroupMessage
import
net.mamoe.mirai.network.protocol.tim.packet.event.MessageEventPacket
import
kotlin.jvm.JvmName
/**
* 消息事件时创建的临时容器.
*/
abstract
class
SenderAndMessage
<
TContact
:
Contact
>(
/**
* 发送这条消息的用户.
*/
val
sender
:
QQ
,
/**
* 消息事件主体. 对于好友消息, 这个属性为 [QQ] 的实例; 对于群消息, 这个属性为 [Group] 的实例
*/
val
subject
:
TContact
,
val
message
:
MessageChain
)
{
/**
* 给这个消息事件的主体发送消息
* 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
*/
suspend
inline
fun
reply
(
message
:
MessageChain
)
=
subject
.
sendMessage
(
message
)
suspend
fun
reply
(
message
:
Message
)
=
subject
.
sendMessage
(
message
.
singleChain
())
suspend
fun
reply
(
plain
:
String
)
=
subject
.
sendMessage
(
plain
.
toMessage
())
// region Send to subject
suspend
inline
fun
ExternalImage
.
send
()
=
this
.
sendTo
(
subject
)
suspend
inline
fun
ExternalImage
.
upload
():
Image
=
this
.
upload
(
subject
)
suspend
inline
fun
Image
.
send
()
=
this
.
sendTo
(
subject
)
suspend
inline
fun
ImageId
.
send
()
=
this
.
sendTo
(
subject
)
suspend
inline
fun
Message
.
send
()
=
this
.
sendTo
(
subject
)
suspend
inline
fun
String
.
send
()
=
this
.
toMessage
().
sendTo
(
subject
)
// endregion
}
/**
* [subject] = [sender] = [QQ]
*/
class
FriendSenderAndMessage
(
sender
:
QQ
,
message
:
MessageChain
)
:
SenderAndMessage
<
QQ
>(
sender
,
sender
,
message
)
/**
* [subject] = [group] = [Group]
*/
class
GroupSenderAndMessage
(
val
group
:
Group
,
sender
:
QQ
,
message
:
MessageChain
)
:
SenderAndMessage
<
Group
>(
sender
,
group
,
message
)
/**
* 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
*/
@MessageDsl
suspend
inline
fun
subscribeMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
SenderAndMessage
<
*
>>.()
->
Unit
)
{
MessageSubscribersBuilder
<
SenderAndMessage
<
*
>>
{
listener
->
subscribeAlways
<
BotEvent
>
{
when
(
it
)
{
is
FriendMessageEvent
->
listener
(
FriendSenderAndMessage
(
it
.
sender
,
it
.
message
))
is
GroupMessageEvent
->
listener
(
GroupSenderAndMessage
(
it
.
group
,
it
.
sender
,
it
.
message
))
}
suspend
inline
fun
subscribeMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
MessageEventPacket
<
*
>>.()
->
Unit
)
{
MessageSubscribersBuilder
<
MessageEventPacket
<
*
>>
{
listener
->
subscribeAlways
<
MessageEventPacket
<
*
>>
{
listener
(
it
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -88,10 +26,10 @@ suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubsc
* 订阅来自所有 [Bot] 的所有群消息事件
*/
@MessageDsl
suspend
inline
fun
subscribeGroupMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
Group
SenderAnd
Message
>.()
->
Unit
)
{
MessageSubscribersBuilder
<
Group
SenderAnd
Message
>
{
listener
->
subscribeAlways
<
GroupMessage
Event
>
{
listener
(
GroupSenderAndMessage
(
it
.
group
,
it
.
sender
,
it
.
message
)
)
suspend
inline
fun
subscribeGroupMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
GroupMessage
>.()
->
Unit
)
{
MessageSubscribersBuilder
<
GroupMessage
>
{
listener
->
subscribeAlways
<
GroupMessage
>
{
listener
(
it
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -100,10 +38,10 @@ suspend inline fun subscribeGroupMessages(crossinline listeners: suspend Message
* 订阅来自所有 [Bot] 的所有好友消息事件
*/
@MessageDsl
suspend
inline
fun
subscribeFriendMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
Friend
SenderAnd
Message
>.()
->
Unit
)
{
MessageSubscribersBuilder
<
Friend
SenderAnd
Message
>
{
listener
->
subscribeAlways
<
FriendMessage
Event
>
{
listener
(
FriendSenderAndMessage
(
it
.
sender
,
it
.
message
)
)
suspend
inline
fun
subscribeFriendMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
FriendMessage
>.()
->
Unit
)
{
MessageSubscribersBuilder
<
FriendMessage
>
{
listener
->
subscribeAlways
<
FriendMessage
>
{
listener
(
it
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -112,13 +50,10 @@ suspend inline fun subscribeFriendMessages(crossinline listeners: suspend Messag
* 订阅来自这个 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
*/
@MessageDsl
suspend
inline
fun
Bot
.
subscribeMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
SenderAndMessage
<
*
>>.()
->
Unit
)
{
MessageSubscribersBuilder
<
SenderAndMessage
<
*
>>
{
listener
->
this
.
subscribeAlways
<
BotEvent
>
{
when
(
it
)
{
is
FriendMessageEvent
->
listener
(
FriendSenderAndMessage
(
it
.
sender
,
it
.
message
))
is
GroupMessageEvent
->
listener
(
GroupSenderAndMessage
(
it
.
group
,
it
.
sender
,
it
.
message
))
}
suspend
inline
fun
Bot
.
subscribeMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
MessageEventPacket
<
*
>>.()
->
Unit
)
{
MessageSubscribersBuilder
<
MessageEventPacket
<
*
>>
{
listener
->
this
.
subscribeAlways
<
MessageEventPacket
<
*
>>
{
listener
(
it
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -127,10 +62,10 @@ suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageS
* 订阅来自这个 [Bot] 的所有群消息事件
*/
@MessageDsl
suspend
inline
fun
Bot
.
subscribeGroupMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
Group
SenderAnd
Message
>.()
->
Unit
)
{
MessageSubscribersBuilder
<
Group
SenderAnd
Message
>
{
listener
->
this
.
subscribeAlways
<
GroupMessage
Event
>
{
listener
(
GroupSenderAndMessage
(
it
.
group
,
it
.
sender
,
it
.
message
)
)
suspend
inline
fun
Bot
.
subscribeGroupMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
GroupMessage
>.()
->
Unit
)
{
MessageSubscribersBuilder
<
GroupMessage
>
{
listener
->
this
.
subscribeAlways
<
GroupMessage
>
{
listener
(
it
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -139,10 +74,10 @@ suspend inline fun Bot.subscribeGroupMessages(crossinline listeners: suspend Mes
* 订阅来自这个 [Bot] 的所有好友消息事件.
*/
@MessageDsl
suspend
inline
fun
Bot
.
subscribeFriendMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
Friend
SenderAnd
Message
>.()
->
Unit
)
{
MessageSubscribersBuilder
<
Friend
SenderAnd
Message
>
{
listener
->
this
.
subscribeAlways
<
FriendMessage
Event
>
{
listener
(
FriendSenderAndMessage
(
it
.
sender
,
it
.
message
)
)
suspend
inline
fun
Bot
.
subscribeFriendMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
FriendMessage
>.()
->
Unit
)
{
MessageSubscribersBuilder
<
FriendMessage
>
{
listener
->
this
.
subscribeAlways
<
FriendMessage
>
{
listener
(
it
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -151,11 +86,11 @@ internal typealias MessageReplier<T> = @MessageDsl suspend T.(String) -> Message
internal
typealias
StringReplier
<
T
>
=
@MessageDsl
suspend
T
.(
String
)
->
String
internal
suspend
inline
operator
fun
<
T
:
SenderAndMessage
<
*
>>
(@
MessageDsl
suspend
T
.(
String
)
->
Unit
).
invoke
(
t
:
T
)
=
internal
suspend
inline
operator
fun
<
T
:
MessageEventPacket
<
*
>>
(@
MessageDsl
suspend
T
.(
String
)
->
Unit
).
invoke
(
t
:
T
)
=
this
.
invoke
(
t
,
t
.
message
.
stringValue
)
@JvmName
(
"invoke1"
)
//Avoid Platform declaration clash
internal
suspend
inline
operator
fun
<
T
:
SenderAndMessage
<
*
>>
StringReplier
<
T
>.
invoke
(
t
:
T
):
String
=
internal
suspend
inline
operator
fun
<
T
:
MessageEventPacket
<
*
>>
StringReplier
<
T
>.
invoke
(
t
:
T
):
String
=
this
.
invoke
(
t
,
t
.
message
.
stringValue
)
/**
...
...
@@ -166,7 +101,7 @@ internal suspend inline operator fun <T : SenderAndMessage<*>> StringReplier<T>.
*/
@Suppress
(
"unused"
)
@MessageDsl
class
MessageSubscribersBuilder
<
T
:
SenderAndMessage
<
*
>>(
class
MessageSubscribersBuilder
<
T
:
MessageEventPacket
<
*
>>(
inline
val
subscriber
:
suspend
(
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
->
Unit
)
{
/**
...
...
@@ -221,7 +156,7 @@ class MessageSubscribersBuilder<T : SenderAndMessage<*>>(
* 如果是来自这个群的消息, 就执行 [onEvent]
*/
suspend
fun
sentFrom
(
id
:
UInt
,
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
content
({
if
(
this
is
Group
SenderAnd
Message
)
group
.
id
==
id
else
false
},
onEvent
)
content
({
if
(
this
is
GroupMessage
)
group
.
id
==
id
else
false
},
onEvent
)
/**
* 如果是来自这个群的消息, 就执行 [onEvent]
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/
Event
.kt
→
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/
Subscribable
.kt
View file @
2b477b3b
...
...
@@ -10,6 +10,13 @@ import kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
import
kotlin.jvm.JvmOverloads
/**
* 可被监听的.
*
* 可以是任何 class 或 object.
*/
interface
Subscribable
/**
* 所有事件的基类.
* 若监听这个类, 监听器将会接收所有事件的广播.
...
...
@@ -17,7 +24,7 @@ import kotlin.jvm.JvmOverloads
* @see [broadcast] 广播事件
* @see [subscribe] 监听事件
*/
abstract
class
Event
{
abstract
class
Event
:
Subscribable
{
/**
* 事件是否已取消. 事件需实现 [Cancellable] 才可以被取消, 否则这个字段为常量值 false
...
...
@@ -51,9 +58,9 @@ private val EventDebuggingFlag: Boolean by lazy {
}
/**
* 实现这个接口的事件可以被取消.
* 实现这个接口的事件可以被取消.
在广播中取消不会影响广播过程.
*/
interface
Cancellable
{
interface
Cancellable
:
Subscribable
{
val
cancelled
:
Boolean
fun
cancel
()
...
...
@@ -67,7 +74,7 @@ interface Cancellable {
*/
@Suppress
(
"UNCHECKED_CAST"
)
@JvmOverloads
suspend
fun
<
E
:
Event
>
E
.
broadcast
(
context
:
CoroutineContext
=
EmptyCoroutineContext
):
E
{
suspend
fun
<
E
:
Subscribable
>
E
.
broadcast
(
context
:
CoroutineContext
=
EmptyCoroutineContext
):
E
{
if
(
EventDebuggingFlag
)
{
EventLogger
.
debug
(
this
::
class
.
simpleName
+
" pre broadcast"
)
}
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
View file @
2b477b3b
...
...
@@ -26,22 +26,26 @@ enum class ListeningStatus {
* 订阅所有 [E] 及其子类事件.
* 在
*/
suspend
inline
fun
<
reified
E
:
Event
>
subscribe
(
noinline
handler
:
suspend
(
E
)
->
ListeningStatus
)
=
E
::
class
.
subscribe
(
handler
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribe
(
noinline
handler
:
suspend
(
E
)
->
ListeningStatus
)
=
E
::
class
.
subscribe
(
handler
)
suspend
inline
fun
<
reified
E
:
Event
>
subscribeAlways
(
noinline
listener
:
suspend
(
E
)
->
Unit
)
=
E
::
class
.
subscribeAlways
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeAlways
(
noinline
listener
:
suspend
(
E
)
->
Unit
)
=
E
::
class
.
subscribeAlways
(
listener
)
suspend
inline
fun
<
reified
E
:
Event
>
subscribeOnce
(
noinline
listener
:
suspend
(
E
)
->
Unit
)
=
E
::
class
.
subscribeOnce
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeOnce
(
noinline
listener
:
suspend
(
E
)
->
Unit
)
=
E
::
class
.
subscribeOnce
(
listener
)
suspend
inline
fun
<
reified
E
:
Event
,
T
>
subscribeUntil
(
valueIfStop
:
T
,
noinline
listener
:
suspend
(
E
)
->
T
)
=
E
::
class
.
subscribeUntil
(
valueIfStop
,
listener
)
suspend
inline
fun
<
reified
E
:
Event
>
subscribeUntilFalse
(
noinline
listener
:
suspend
(
E
)
->
Boolean
)
=
E
::
class
.
subscribeUntilFalse
(
listener
)
suspend
inline
fun
<
reified
E
:
Event
>
subscribeUntilTrue
(
noinline
listener
:
suspend
(
E
)
->
Boolean
)
=
E
::
class
.
subscribeUntilTrue
(
listener
)
suspend
inline
fun
<
reified
E
:
Event
>
subscribeUntilNull
(
noinline
listener
:
suspend
(
E
)
->
Any
?)
=
E
::
class
.
subscribeUntilNull
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
,
T
>
subscribeUntil
(
valueIfStop
:
T
,
noinline
listener
:
suspend
(
E
)
->
T
)
=
E
::
class
.
subscribeUntil
(
valueIfStop
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeUntilFalse
(
noinline
listener
:
suspend
(
E
)
->
Boolean
)
=
E
::
class
.
subscribeUntilFalse
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeUntilTrue
(
noinline
listener
:
suspend
(
E
)
->
Boolean
)
=
E
::
class
.
subscribeUntilTrue
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeUntilNull
(
noinline
listener
:
suspend
(
E
)
->
Any
?)
=
E
::
class
.
subscribeUntilNull
(
listener
)
suspend
inline
fun
<
reified
E
:
Event
,
T
>
subscribeWhile
(
valueIfContinue
:
T
,
noinline
listener
:
suspend
(
E
)
->
T
)
=
E
::
class
.
subscribeWhile
(
valueIfContinue
,
listener
)
suspend
inline
fun
<
reified
E
:
Event
>
subscribeWhileFalse
(
noinline
listener
:
suspend
(
E
)
->
Boolean
)
=
E
::
class
.
subscribeWhileFalse
(
listener
)
suspend
inline
fun
<
reified
E
:
Event
>
subscribeWhileTrue
(
noinline
listener
:
suspend
(
E
)
->
Boolean
)
=
E
::
class
.
subscribeWhileTrue
(
listener
)
suspend
inline
fun
<
reified
E
:
Event
>
subscribeWhileNull
(
noinline
listener
:
suspend
(
E
)
->
Any
?)
=
E
::
class
.
subscribeWhileNull
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
,
T
>
subscribeWhile
(
valueIfContinue
:
T
,
noinline
listener
:
suspend
(
E
)
->
T
)
=
E
::
class
.
subscribeWhile
(
valueIfContinue
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeWhileFalse
(
noinline
listener
:
suspend
(
E
)
->
Boolean
)
=
E
::
class
.
subscribeWhileFalse
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeWhileTrue
(
noinline
listener
:
suspend
(
E
)
->
Boolean
)
=
E
::
class
.
subscribeWhileTrue
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeWhileNull
(
noinline
listener
:
suspend
(
E
)
->
Any
?)
=
E
::
class
.
subscribeWhileNull
(
listener
)
// endregion
...
...
@@ -49,40 +53,42 @@ suspend inline fun <reified E : Event> subscribeWhileNull(noinline listener: sus
// region KClass 的扩展方法 (不推荐)
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribe
(
handler
:
suspend
(
E
)
->
ListeningStatus
)
=
this
.
subscribeInternal
(
Handler
(
handler
))
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribe
(
handler
:
suspend
(
E
)
->
ListeningStatus
)
=
this
.
subscribeInternal
(
Handler
(
handler
))
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeAlways
(
listener
:
suspend
(
E
)
->
Unit
)
=
this
.
subscribeInternal
(
Handler
{
listener
(
it
);
ListeningStatus
.
LISTENING
})
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeAlways
(
listener
:
suspend
(
E
)
->
Unit
)
=
this
.
subscribeInternal
(
Handler
{
listener
(
it
);
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeOnce
(
listener
:
suspend
(
E
)
->
Unit
)
=
this
.
subscribeInternal
(
Handler
{
listener
(
it
);
ListeningStatus
.
STOPPED
})
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeOnce
(
listener
:
suspend
(
E
)
->
Unit
)
=
this
.
subscribeInternal
(
Handler
{
listener
(
it
);
ListeningStatus
.
STOPPED
})
@PublishedApi
internal
suspend
fun
<
E
:
Event
,
T
>
KClass
<
E
>.
subscribeUntil
(
valueIfStop
:
T
,
listener
:
suspend
(
E
)
->
T
)
=
internal
suspend
fun
<
E
:
Subscribable
,
T
>
KClass
<
E
>.
subscribeUntil
(
valueIfStop
:
T
,
listener
:
suspend
(
E
)
->
T
)
=
subscribeInternal
(
Handler
{
if
(
listener
(
it
)
===
valueIfStop
)
ListeningStatus
.
STOPPED
else
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeUntilFalse
(
listener
:
suspend
(
E
)
->
Boolean
)
=
subscribeUntil
(
false
,
listener
)
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilFalse
(
listener
:
suspend
(
E
)
->
Boolean
)
=
subscribeUntil
(
false
,
listener
)
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeUntilTrue
(
listener
:
suspend
(
E
)
->
Boolean
)
=
subscribeUntil
(
true
,
listener
)
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilTrue
(
listener
:
suspend
(
E
)
->
Boolean
)
=
subscribeUntil
(
true
,
listener
)
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeUntilNull
(
listener
:
suspend
(
E
)
->
Any
?)
=
subscribeUntil
(
null
,
listener
)
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilNull
(
listener
:
suspend
(
E
)
->
Any
?)
=
subscribeUntil
(
null
,
listener
)
@PublishedApi
internal
suspend
fun
<
E
:
Event
,
T
>
KClass
<
E
>.
subscribeWhile
(
valueIfContinue
:
T
,
listener
:
suspend
(
E
)
->
T
)
=
internal
suspend
fun
<
E
:
Subscribable
,
T
>
KClass
<
E
>.
subscribeWhile
(
valueIfContinue
:
T
,
listener
:
suspend
(
E
)
->
T
)
=
subscribeInternal
(
Handler
{
if
(
listener
(
it
)
!==
valueIfContinue
)
ListeningStatus
.
STOPPED
else
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeWhileFalse
(
listener
:
suspend
(
E
)
->
Boolean
)
=
subscribeWhile
(
false
,
listener
)
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileFalse
(
listener
:
suspend
(
E
)
->
Boolean
)
=
subscribeWhile
(
false
,
listener
)
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeWhileTrue
(
listener
:
suspend
(
E
)
->
Boolean
)
=
subscribeWhile
(
true
,
listener
)
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileTrue
(
listener
:
suspend
(
E
)
->
Boolean
)
=
subscribeWhile
(
true
,
listener
)
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeWhileNull
(
listener
:
suspend
(
E
)
->
Any
?)
=
subscribeWhile
(
null
,
listener
)
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileNull
(
listener
:
suspend
(
E
)
->
Any
?)
=
subscribeWhile
(
null
,
listener
)
// endregion
...
...
@@ -94,7 +100,7 @@ internal suspend fun <E : Event> KClass<E>.subscribeWhileNull(listener: suspend
*/
@ListenersBuilderDsl
@PublishedApi
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeAll
(
listeners
:
suspend
ListenerBuilder
<
E
>.()
->
Unit
)
{
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeAll
(
listeners
:
suspend
ListenerBuilder
<
E
>.()
->
Unit
)
{
with
(
ListenerBuilder
<
E
>
{
this
.
subscribeInternal
(
it
)
})
{
listeners
()
}
...
...
@@ -105,7 +111,7 @@ internal suspend fun <E : Event> KClass<E>.subscribeAll(listeners: suspend Liste
* @see ListenerBuilder
*/
@ListenersBuilderDsl
suspend
inline
fun
<
reified
E
:
Event
>
subscribeAll
(
noinline
listeners
:
suspend
ListenerBuilder
<
E
>.()
->
Unit
)
=
E
::
class
.
subscribeAll
(
listeners
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeAll
(
noinline
listeners
:
suspend
ListenerBuilder
<
E
>.()
->
Unit
)
=
E
::
class
.
subscribeAll
(
listeners
)
/**
* 监听构建器. 可同时进行多种方式的监听
...
...
@@ -125,7 +131,7 @@ suspend inline fun <reified E : Event> subscribeAll(noinline listeners: suspend
*/
@ListenersBuilderDsl
@Suppress
(
"MemberVisibilityCanBePrivate"
,
"unused"
)
class
ListenerBuilder
<
out
E
:
Event
>(
class
ListenerBuilder
<
out
E
:
Subscribable
>(
@PublishedApi
internal
val
handlerConsumer
:
suspend
(
Listener
<
E
>)
->
Unit
)
{
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
View file @
2b477b3b
package
net.mamoe.mirai.event.events
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.event.Cancellable
import
net.mamoe.mirai.event.Event
import
net.mamoe.mirai.message.ImageId
abstract
class
BotEvent
(
val
bot
:
Bot
)
:
Event
()
abstract
class
BotEvent
:
Event
{
private
lateinit
var
_bot
:
Bot
open
val
bot
:
Bot
get
()
=
_bot
class
BotLoginSucceedEvent
(
bot
:
Bot
)
:
BotEvent
(
bot
)
constructor
(
bot
:
Bot
)
:
super
()
{
this
.
_bot
=
bot
}
/**
* 上传好友图片时, 服务器返回好友图片 ID 事件.
*
* 在这之后图片将会被上传到服务器.
*/
class
FriendImageIdObtainedEvent
(
bot
:
Bot
,
val
imageId
:
ImageId
)
:
BotEvent
(
bot
),
Cancellable
\ No newline at end of file
constructor
()
:
super
()
}
class
BotLoginSucceedEvent
(
bot
:
Bot
)
:
BotEvent
(
bot
)
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/FriendEvents.kt
deleted
100644 → 0
View file @
745200f6
@
file
:
Suppress
(
"unused"
)
package
net.mamoe.mirai.event.events
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.utils.OnlineStatus
/**
* 好友事件
*/
sealed
class
FriendEvent
(
bot
:
Bot
,
val
sender
:
QQ
)
:
BotEvent
(
bot
)
/**
* 接受好友消息事件
*
* @author Him188moe
*/
class
FriendMessageEvent
(
bot
:
Bot
,
sender
:
QQ
,
val
message
:
MessageChain
)
:
FriendEvent
(
bot
,
sender
)
{
suspend
inline
fun
reply
(
message
:
Message
)
=
sender
.
sendMessage
(
message
)
suspend
inline
fun
reply
(
message
:
String
)
=
sender
.
sendMessage
(
message
)
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
)
/**
* 机器人账号被 [sender] 删除好友
*/
class
DeletedByFriendEvent
(
bot
:
Bot
,
qq
:
QQ
)
:
FriendEvent
(
bot
,
qq
)
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/GroupEvents.kt
deleted
100644 → 0
View file @
745200f6
@
file
:
Suppress
(
"unused"
)
package
net.mamoe.mirai.event.events
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.network.protocol.tim.packet.event.SenderPermission
abstract
class
GroupEvent
(
bot
:
Bot
,
val
group
:
Group
)
:
BotEvent
(
bot
)
/**
* 群消息
*/
class
GroupMessageEvent
(
bot
:
Bot
,
group
:
Group
,
val
sender
:
QQ
,
val
message
:
MessageChain
,
val
senderPermission
:
SenderPermission
,
val
senderName
:
String
//若他有群名片就是群名片, 没有就是昵称
)
:
GroupEvent
(
bot
,
group
)
{
suspend
inline
fun
reply
(
message
:
Message
)
=
group
.
sendMessage
(
message
)
suspend
inline
fun
reply
(
message
:
String
)
=
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 变成群主的情况
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/PacketEvents.kt
View file @
2b477b3b
...
...
@@ -47,5 +47,4 @@ sealed class ServerPacketEvent<P : Packet>(bot: Bot, packet: P) : PacketEvent<P>
/**
* 服务器数据包接收事件. 此时包已经解密完成.
*/
class
ServerPacketReceivedEvent
<
P
:
Packet
>(
bot
:
Bot
,
packet
:
P
)
:
ServerPacketEvent
<
P
>(
bot
,
packet
),
Cancellable
\ No newline at end of file
class
ServerPacketReceivedEvent
<
P
:
Packet
>(
bot
:
Bot
,
packet
:
P
)
:
ServerPacketEvent
<
P
>(
bot
,
packet
),
Cancellable
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
View file @
2b477b3b
...
...
@@ -5,10 +5,10 @@ import kotlinx.coroutines.launch
import
kotlinx.coroutines.sync.Mutex
import
kotlinx.coroutines.sync.withLock
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.event.Event
import
net.mamoe.mirai.event.EventLogger
import
net.mamoe.mirai.event.EventScope
import
net.mamoe.mirai.event.ListeningStatus
import
net.mamoe.mirai.event.Subscribable
import
net.mamoe.mirai.event.events.BotEvent
import
net.mamoe.mirai.utils.internal.inlinedRemoveIf
import
kotlin.jvm.JvmField
...
...
@@ -23,7 +23,7 @@ import kotlin.reflect.KFunction
*
* @author Him188moe
*/
internal
suspend
fun
<
E
:
Event
>
KClass
<
E
>.
subscribeInternal
(
listener
:
Listener
<
E
>):
Unit
=
with
(
this
.
listeners
())
{
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeInternal
(
listener
:
Listener
<
E
>):
Unit
=
with
(
this
.
listeners
())
{
if
(
mainMutex
.
tryLock
(
listener
))
{
//能锁则代表这个事件目前没有正在广播.
try
{
add
(
listener
)
//直接修改主监听者列表
...
...
@@ -60,13 +60,13 @@ internal suspend fun <E : Event> KClass<E>.subscribeInternal(listener: Listener<
*
* @author Him188moe
*/
sealed
class
Listener
<
in
E
:
Event
>
{
sealed
class
Listener
<
in
E
:
Subscribable
>
{
internal
val
lock
=
Mutex
()
abstract
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
}
@PublishedApi
internal
class
Handler
<
in
E
:
Event
>(
@JvmField
val
handler
:
suspend
(
E
)
->
ListeningStatus
)
:
Listener
<
E
>()
{
internal
class
Handler
<
in
E
:
Subscribable
>(
@JvmField
val
handler
:
suspend
(
E
)
->
ListeningStatus
)
:
Listener
<
E
>()
{
override
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
=
handler
.
invoke
(
event
)
}
...
...
@@ -76,7 +76,7 @@ internal class Handler<in E : Event>(@JvmField val handler: suspend (E) -> Liste
* 所有的 [BotEvent.bot] `!==` `bot` 的事件都不会被处理
*/
@PublishedApi
internal
class
HandlerWithBot
<
E
:
Event
>(
val
bot
:
Bot
,
@JvmField
val
handler
:
suspend
Bot
.(
E
)
->
ListeningStatus
)
:
internal
class
HandlerWithBot
<
E
:
Subscribable
>(
val
bot
:
Bot
,
@JvmField
val
handler
:
suspend
Bot
.(
E
)
->
ListeningStatus
)
:
Listener
<
E
>()
{
override
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
=
with
(
bot
)
{
if
(
event
!
is
BotEvent
||
event
.
bot
!==
this
)
{
...
...
@@ -94,9 +94,9 @@ internal class HandlerWithBot<E : Event>(val bot: Bot, @JvmField val handler: su
/**
* 这个事件类的监听器 list
*/
internal
suspend
fun
<
E
:
Event
>
KClass
<
out
E
>.
listeners
():
EventListeners
<
E
>
=
EventListenerManger
.
get
(
this
)
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
out
E
>.
listeners
():
EventListeners
<
E
>
=
EventListenerManger
.
get
(
this
)
internal
class
EventListeners
<
E
:
Event
>
:
MutableList
<
Listener
<
E
>>
by
mutableListOf
()
{
internal
class
EventListeners
<
E
:
Subscribable
>
:
MutableList
<
Listener
<
E
>>
by
mutableListOf
()
{
/**
* 主监听者列表.
* 广播事件时使用这个锁.
...
...
@@ -125,11 +125,11 @@ internal class EventListeners<E : Event> : MutableList<Listener<E>> by mutableLi
* [EventListeners] 是 lazy 的: 它们只会在被需要的时候才创建和存储.
*/
internal
object
EventListenerManger
{
private
val
registries
:
MutableMap
<
KClass
<
out
Event
>,
EventListeners
<
*
>>
=
mutableMapOf
()
private
val
registries
:
MutableMap
<
KClass
<
out
Subscribable
>,
EventListeners
<
*
>>
=
mutableMapOf
()
private
val
registriesMutex
=
Mutex
()
@Suppress
(
"UNCHECKED_CAST"
)
internal
suspend
fun
<
E
:
Event
>
get
(
clazz
:
KClass
<
out
E
>):
EventListeners
<
E
>
=
registriesMutex
.
withLock
{
internal
suspend
fun
<
E
:
Subscribable
>
get
(
clazz
:
KClass
<
out
E
>):
EventListeners
<
E
>
=
registriesMutex
.
withLock
{
if
(
registries
.
containsKey
(
clazz
))
{
return
registries
[
clazz
]
as
EventListeners
<
E
>
}
else
{
...
...
@@ -141,7 +141,7 @@ internal object EventListenerManger {
}
}
internal
suspend
fun
<
E
:
Event
>
E
.
broadcastInternal
():
E
{
internal
suspend
fun
<
E
:
Subscribable
>
E
.
broadcastInternal
():
E
{
suspend
fun
callListeners
(
listeners
:
EventListeners
<
in
E
>)
{
suspend
fun
callAndRemoveIfRequired
()
=
listeners
.
inlinedRemoveIf
{
if
(
it
.
lock
.
tryLock
())
{
...
...
@@ -177,12 +177,12 @@ internal suspend fun <E : Event> E.broadcastInternal(): E {
/**
* apply [block] to all the [EventListeners] in [clazz]'s superclasses
*/
private
tailrec
suspend
fun
<
E
:
Event
>
applySuperListeners
(
private
tailrec
suspend
fun
<
E
:
Subscribable
>
applySuperListeners
(
clazz
:
KClass
<
out
E
>,
block
:
suspend
(
EventListeners
<
in
E
>)
->
Unit
)
{
val
superEventClass
=
clazz
.
supertypes
.
map
{
it
.
classifier
}.
filterIsInstance
<
KClass
<
out
Event
>>().
firstOrNull
()
?:
return
clazz
.
supertypes
.
map
{
it
.
classifier
}.
filterIsInstance
<
KClass
<
out
Subscribable
>>().
firstOrNull
()
?:
return
@Suppress
(
"UNCHECKED_CAST"
)
block
(
superEventClass
.
listeners
()
as
EventListeners
<
in
E
>)
applySuperListeners
(
superEventClass
,
block
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
View file @
2b477b3b
...
...
@@ -6,6 +6,7 @@ import net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket
import
net.mamoe.mirai.utils.ExternalImage
import
kotlin.jvm.Volatile
// region Message Base
/**
...
...
@@ -210,20 +211,24 @@ inline class Face(val id: FaceID) : Message {
* 构造无初始元素的可修改的 [MessageChain]. 初始大小将会被设定为 8
*/
@Suppress
(
"FunctionName"
)
fun
MessageChain
():
MessageChain
=
MessageChainImpl
(
ArrayList
(
8
)
)
fun
MessageChain
():
MessageChain
=
EmptyMessageChain
(
)
/**
* 构造无初始元素的可修改的 [MessageChain]. 初始大小将会被设定为 [initialCapacity]
*/
@Suppress
(
"FunctionName"
)
fun
MessageChain
(
initialCapacity
:
Int
):
MessageChain
=
MessageChainImpl
(
ArrayList
(
initialCapacity
))
fun
MessageChain
(
initialCapacity
:
Int
):
MessageChain
=
if
(
initialCapacity
==
0
)
EmptyMessageChain
()
else
MessageChainImpl
(
ArrayList
(
initialCapacity
))
/**
* 构造 [MessageChain]
* 若仅提供一个参数, 请考虑使用 [Message.toChain] 以优化性能
*/
@Suppress
(
"FunctionName"
)
fun
MessageChain
(
vararg
messages
:
Message
):
MessageChain
=
MessageChainImpl
(
messages
.
toMutableList
())
fun
MessageChain
(
vararg
messages
:
Message
):
MessageChain
=
if
(
messages
.
isEmpty
())
EmptyMessageChain
()
else
MessageChainImpl
(
messages
.
toMutableList
())
/**
* 构造 [MessageChain]
...
...
@@ -351,15 +356,17 @@ interface MessageChain : Message, MutableList<Message> {
/**
* 空的 [Message].
*
* 它不包含任何元素, 但维护一个 'lazy' 的 [
delegate
].
* 它不包含任何元素, 但维护一个 'lazy' 的 [
MessageChainImpl
].
*
* 只有在必要的时候
才会创建 list, 如迭代([iterator]), 插入([add]), 连接([concat], [plus], [plusAssign])时.
* 只有在必要的时候
(如迭代([iterator]), 插入([add]), 连接([concat], [plus], [plusAssign]))才会创建这个对象代表的 list
*
* 它是一个正常的 [Message] 和 [Message]. 可以做所有 [Message] 能做的事.
* 它是一个正常的 [Message] 和 [Message
Chain
]. 可以做所有 [Message] 能做的事.
*/
class
EmptyMessageChain
:
MessageChain
{
private
val
delegate
:
MessageChainImpl
by
lazy
{
MessageChainImpl
()
}
private
inline
val
initialized
:
Boolean
get
()
=
(
::
delegate
as
Lazy
<
*
>).
isInitialized
()
private
val
delegate
:
MessageChain
by
lazy
{
MessageChainImpl
().
also
{
initialized
=
true
}
}
@Volatile
private
var
initialized
:
Boolean
=
false
override
fun
subList
(
fromIndex
:
Int
,
toIndex
:
Int
):
MutableList
<
Message
>
=
if
(
initialized
)
delegate
.
subList
(
...
...
@@ -498,8 +505,9 @@ internal inline class MessageChainImpl constructor(
*/
private
val
delegate
:
MutableList
<
Message
>
)
:
Message
,
MutableList
<
Message
>,
MessageChain
{
constructor
()
:
this
(
ArrayList
(
8
))
//
constructor() : this(ArrayList(8))
constructor
(
initialCapacity
:
Int
)
:
this
(
ArrayList
(
initialCapacity
))
constructor
(
vararg
messages
:
Message
)
:
this
(
messages
.
toMutableList
())
constructor
(
messages
:
Iterable
<
Message
>)
:
this
(
messages
.
toMutableList
())
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
View file @
2b477b3b
...
...
@@ -168,6 +168,7 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket {
is
Image
->
buildPacket
{
when
(
id
.
value
.
length
)
{
// "{F61593B5-5B98-1798-3F47-2A91D32ED2FC}.jpg"
42
->
{
writeUByte
(
MessageType
.
GROUP_IMAGE
.
value
)
...
...
@@ -188,6 +189,7 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket {
}
}
// "/01ee6426-5ff1-4cf0-8278-e8634d2909ef"
37
->
{
writeUByte
(
MessageType
.
FRIEND_IMAGE
.
value
)
...
...
@@ -225,8 +227,7 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket {
writeByte
(
0
x02
)
//"46 52 25 46 60 30 59 4F 4A 5A 51 48 31 46 4A 53 4C 51 4C 4A 33 46 31 2E 6A 70 67".hexToBytes().stringOfWitch()
// writeShortLVString(filename)//图片文件名 FR%F`0YOJZQH1FJSLQLJ3F1.jpg
require
(
id
.
value
.
length
==
37
)
{
"Illegal ImageId: ${id.value}"
}
writeShortLVString
(
id
.
value
.
substring
(
1
..
24
)
+
".gif"
)
//图片文件名. 后缀不影响. 但无后缀会导致 PC QQ 无法显示这个图片
writeShortLVString
(
id
.
value
.
substring
(
1
..
24
)
+
".gif"
)
// 图片文件名. 后缀不影响. 但无后缀会导致 PC QQ 无法显示这个图片
writeHex
(
"03 00 04 00 00 02 A2 04"
)
writeShortLVString
(
id
.
value
)
writeHex
(
"14 00 04 03 00 00 00 0B 00 00 18"
)
...
...
@@ -234,10 +235,10 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket {
writeHex
(
"19 00 04 00 00 00 4E 1A 00 04 00 00 00 23 FF 00 63 16 20 20 39 39 31 30 20 38 38 31 43 42 20 20 20 20 20 20 20 "
)
writeStringUtf8
(
"674e"
)
//没有 "e" 服务器就不回复
writeStringUtf8
(
"674e"
)
//
没有 "e" 服务器就不回复
writeStringUtf8
(
id
.
value
.
substring
(
1
..
id
.
value
.
lastIndex
-
4
))
//这一串文件名决定手机 QQ 保存的图片. 可以随意
writeStringUtf8
(
".gif"
)
//
后缀
要有
writeStringUtf8
(
".gif"
)
//
后缀似乎必须
要有
writeUByte
(
0
x66u
)
//有时候 PC QQ 看不到发这些消息, 但手机可以. 可能是 ID 过期了, 手机有缓存而电脑没有
...
...
@@ -256,7 +257,7 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket {
writeUByte
(
0
x41u
)
}
}
else
->
error
(
"Illegal ImageId ${id.value}"
)
else
->
error
(
"Illegal ImageId
:
${id.value}"
)
}
}
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
View file @
2b477b3b
...
...
@@ -8,13 +8,11 @@ import kotlinx.coroutines.sync.withLock
import
kotlinx.io.core.*
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.event.ListeningStatus
import
net.mamoe.mirai.event.broadcast
import
net.mamoe.mirai.event.*
import
net.mamoe.mirai.event.events.BeforePacketSendEvent
import
net.mamoe.mirai.event.events.BotLoginSucceedEvent
import
net.mamoe.mirai.event.events.PacketSentEvent
import
net.mamoe.mirai.event.events.ServerPacketReceivedEvent
import
net.mamoe.mirai.event.subscribe
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.BotSession
import
net.mamoe.mirai.network.protocol.tim.handler.*
...
...
@@ -33,7 +31,7 @@ import kotlin.properties.Delegates
*
* JVM: 独立的 4 thread 调度器
*/
expect
val
NetworkDispatcher
:
CoroutineDispatcher
internal
expect
val
NetworkDispatcher
:
CoroutineDispatcher
/**
* [BotNetworkHandler] 的 TIM PC 协议实现
...
...
@@ -75,7 +73,6 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
socket
.
resendTouch
().
takeIf
{
it
!=
LoginResult
.
TIMEOUT
}
?.
let
{
return
@
withContext
it
}
println
()
bot
.
logger
.
warning
(
"Timeout. Retrying next server"
)
socket
.
close
()
...
...
@@ -91,7 +88,6 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
require
(
size
==
0
)
{
"Already logged in"
}
val
session
=
BotSession
(
bot
,
sessionKey
,
socket
)
add
(
EventPacketHandler
(
session
).
asNode
(
EventPacketHandler
))
add
(
ActionPacketHandler
(
session
).
asNode
(
ActionPacketHandler
))
bot
.
logger
.
info
(
"Successfully logged in"
)
}
...
...
@@ -144,6 +140,8 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
}
catch
(
e
:
ReadPacketInternalException
)
{
bot
.
logger
.
error
(
"Socket channel read failed: ${e.message}"
)
continue
}
catch
(
e
:
CancellationException
)
{
return
}
catch
(
e
:
Throwable
)
{
bot
.
logger
.
error
(
"Caught unexpected exceptions"
,
e
)
continue
...
...
@@ -154,6 +152,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
continue
}
// sometimes exceptions are thrown without this `if` clause
}
//buffer.resetForRead()
launch
{
// `.use`: Ensure that the packet is consumed **totally**
...
...
@@ -164,9 +163,27 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
buffer
.
resetForWrite
()
buffer
.
writeFully
(
it
,
0
,
length
)
}
ByteReadPacket
(
buffer
,
IoBuffer
.
Pool
).
use
{
ByteReadPacket
(
buffer
,
IoBuffer
.
Pool
).
use
{
input
->
try
{
processPacket
(
it
)
input
.
discardExact
(
3
)
val
id
=
matchPacketId
(
input
.
readUShort
())
val
sequenceId
=
input
.
readUShort
()
input
.
discardExact
(
7
)
//4 for qq number, 3 for 0x00 0x00 0x00
val
packet
=
try
{
with
(
id
.
factory
)
{
loginHandler
.
provideDecrypter
(
id
.
factory
)
.
decrypt
(
input
)
.
decode
(
id
,
sequenceId
,
this
@TIMBotNetworkHandler
)
}
}
finally
{
input
.
close
()
}
handlePacket0
(
sequenceId
,
packet
,
id
.
factory
)
}
catch
(
e
:
Exception
)
{
bot
.
logger
.
error
(
e
)
}
...
...
@@ -205,25 +222,19 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
return
receiving
}
private
suspend
inline
fun
processPacket
(
input
:
ByteReadPacket
)
=
with
(
input
)
{
discardExact
(
3
)
val
id
=
PacketId
(
readUShort
())
val
sequenceId
=
readUShort
()
discardExact
(
7
)
//4 for qq number, 3 for 0x00 0x00 0x00. 但更可能是应该 discard 8
val
packet
:
Packet
=
with
(
id
.
factory
)
{
try
{
loginHandler
.
provideDecrypter
(
id
.
factory
)
.
decrypt
(
input
)
.
decode
(
id
,
sequenceId
,
this
@TIMBotNetworkHandler
)
}
finally
{
input
.
close
()
}
private
suspend
fun
<
TPacket
:
Packet
>
handlePacket0
(
sequenceId
:
UShort
,
packet
:
TPacket
,
factory
:
PacketFactory
<
TPacket
,
*
>
)
{
if
(!
packet
::
class
.
annotations
.
filterIsInstance
<
NoLog
>().
any
())
{
bot
.
logger
.
verbose
(
"Packet received: $packet"
)
}
bot
.
logger
.
verbose
(
"Packet received: $packet"
)
when
(
packet
)
{
is
Cancellable
->
if
((
packet
as
Cancellable
).
broadcast
(
coroutineContext
).
cancelled
)
return
is
Subscribable
->
packet
.
broadcast
(
coroutineContext
)
}
// Remove first to release the lock
handlersLock
.
withLock
{
...
...
@@ -236,6 +247,12 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
if
(
ServerPacketReceivedEvent
(
bot
,
packet
).
broadcast
().
cancelled
)
return
if
(
factory
is
SessionPacketFactory
<
*
>)
{
with
(
factory
as
SessionPacketFactory
<
TPacket
>)
{
processPacket
(
packet
)
}
}
// They should be called in sequence because packet is lock-free
loginHandler
.
onPacketReceived
(
packet
)
this
@TIMBotNetworkHandler
.
forEach
{
...
...
@@ -270,7 +287,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
it
::
class
.
annotations
.
filterIsInstance
<
NoLog
>().
any
()
}
}
?.
let
{
bot
.
logger
.
verbose
(
"Packet sent: $
it
"
)
bot
.
logger
.
verbose
(
"Packet sent: $
{it::class.simpleName ?: "
[
OutgoingPacket
]
"}
"
)
}
PacketSentEvent
(
bot
,
packet
).
broadcast
()
...
...
@@ -455,7 +472,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
//是ClientPasswordSubmissionPacket之后服务器回复的可能之一
is
SubmitPasswordPacket
.
LoginResponse
.
KeyExchange
->
{
this
.
privateKey
=
packet
.
privateKeyUpdate
!!
this
.
privateKey
=
packet
.
privateKeyUpdate
socket
.
sendPacket
(
SubmitPasswordPacket
(
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt
deleted
100644 → 0
View file @
745200f6
package
net.mamoe.mirai.network.protocol.tim.handler
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.GroupId
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.contact.groupId
import
net.mamoe.mirai.event.broadcast
import
net.mamoe.mirai.event.events.*
import
net.mamoe.mirai.getGroup
import
net.mamoe.mirai.getQQ
import
net.mamoe.mirai.message.MessageChain
import
net.mamoe.mirai.network.BotSession
import
net.mamoe.mirai.network.protocol.tim.packet.EventPacket
import
net.mamoe.mirai.network.protocol.tim.packet.FriendStatusChanged
import
net.mamoe.mirai.network.protocol.tim.packet.Packet
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.SendGroupMessagePacket
import
net.mamoe.mirai.network.qqAccount
/**
* 处理消息事件, 承担消息发送任务.
*
* @author Him188moe
*/
@Suppress
(
"EXPERIMENTAL_API_USAGE"
)
class
EventPacketHandler
(
session
:
BotSession
)
:
PacketHandler
(
session
)
{
companion
object
Key
:
PacketHandler
.
Key
<
EventPacketHandler
>
override
suspend
fun
onPacketReceived
(
packet
:
Packet
):
Unit
=
with
(
session
)
{
when
(
packet
)
{
is
EventPacket
.
FriendMessage
->
{
if
(!
packet
.
isPrevious
)
FriendMessageEvent
(
bot
,
bot
.
getQQ
(
packet
.
qq
),
packet
.
message
)
else
null
}
is
EventPacket
.
GroupMessage
->
{
if
(
packet
.
qq
==
bot
.
account
.
id
)
return
GroupMessageEvent
(
bot
,
bot
.
getGroup
(
GroupId
(
packet
.
groupNumber
)),
bot
.
getQQ
(
packet
.
qq
),
packet
.
message
,
packet
.
senderPermission
,
packet
.
senderName
)
}
is
EventPacket
.
FriendConversationInitialize
->
FriendConversationInitializedEvent
(
bot
,
bot
.
getQQ
(
packet
.
qq
))
is
FriendStatusChanged
->
FriendOnlineStatusChangedEvent
(
bot
,
bot
.
getQQ
(
packet
.
qq
),
packet
.
status
)
is
FriendImageIdRequestPacket
.
Response
->
packet
.
imageId
?.
let
{
FriendImageIdObtainedEvent
(
bot
,
it
)
}
is
EventPacket
.
MemberPermissionChange
->
MemberPermissionChangedEvent
(
bot
,
bot
.
getGroup
(
packet
.
groupId
.
groupId
()),
bot
.
getQQ
(
packet
.
qq
),
packet
.
kind
)
else
->
null
}
?.
broadcast
()
}
suspend
fun
sendFriendMessage
(
qq
:
QQ
,
message
:
MessageChain
)
{
session
.
socket
.
sendPacket
(
SendFriendMessagePacket
(
session
.
qqAccount
,
qq
.
id
,
session
.
sessionKey
,
message
))
}
suspend
fun
sendGroupMessage
(
group
:
Group
,
message
:
MessageChain
)
{
session
.
socket
.
sendPacket
(
SendGroupMessagePacket
(
session
.
qqAccount
,
group
.
internalId
,
session
.
sessionKey
,
message
)
)
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Annotations.kt
View file @
2b477b3b
...
...
@@ -2,7 +2,7 @@
package
net.mamoe.mirai.network.protocol.tim.packet
import
net.mamoe.mirai.event.
Event
import
net.mamoe.mirai.event.
Subscribable
import
kotlin.reflect.KClass
...
...
@@ -27,11 +27,11 @@ inline val AnnotatedId.value: UShort get() = id.value
@Retention
(
AnnotationRetention
.
BINARY
)
@Target
(
AnnotationTarget
.
CLASS
)
annotation
class
CorrespondingEvent
(
val
eventClass
:
KClass
<
out
Event
>
val
eventClass
:
KClass
<
out
Subscribable
>
)
/**
*
版本信息
*
包的最后一次修改时间, 和分析时使用的 TIM 版本
*/
@MustBeDocumented
@Target
(
AnnotationTarget
.
FUNCTION
,
AnnotationTarget
.
CLASS
,
AnnotationTarget
.
PROPERTY
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Decrypters.kt
View file @
2b477b3b
...
...
@@ -2,6 +2,7 @@ package net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.IoBuffer
import
net.mamoe.mirai.utils.decryptBy
/**
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/OutgoingPacket.kt
View file @
2b477b3b
...
...
@@ -6,6 +6,7 @@ import kotlinx.io.core.BytePacketBuilder
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.use
import
kotlinx.io.core.writeUShort
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.utils.io.encryptAndWrite
import
net.mamoe.mirai.utils.io.writeHex
...
...
@@ -24,21 +25,19 @@ class OutgoingPacket(
private
val
name
:
String
by
lazy
{
name
?:
packetId
.
toString
()
}
constructor
(
id
:
PacketId
,
sequenceId
:
UShort
,
delegate
:
ByteReadPacket
)
:
this
(
null
,
id
,
sequenceId
,
delegate
)
constructor
(
annotation
:
AnnotatedId
,
sequenceId
:
UShort
,
delegate
:
ByteReadPacket
)
:
this
(
annotation
.
toString
(),
annotation
.
id
,
sequenceId
,
delegate
)
override
fun
toString
():
String
=
packetToString
(
packetId
.
value
,
sequenceId
,
name
)
}
/**
* 登录完成建立 session 之后发出的包.
* 均使用 sessionKey 加密
*
* @param TPacket invariant
*/
abstract
class
SessionPacketFactory
<
out
TPacket
:
Packet
>
:
PacketFactory
<
TPacket
,
SessionKey
>(
SessionKey
)
{
final
override
fun
decrypt
(
input
:
ByteReadPacket
,
decrypter
:
SessionKey
):
ByteReadPacket
=
decrypter
.
decrypt
(
input
)
abstract
class
SessionPacketFactory
<
TPacket
:
Packet
>
:
PacketFactory
<
TPacket
,
SessionKey
>(
SessionKey
)
{
/**
* 在 [BotNetworkHandler] 下处理这个包. 广播事件等.
*/
open
suspend
fun
BotNetworkHandler
<*>.
processPacket
(
packet
:
TPacket
)
{}
}
/**
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt
View file @
2b477b3b
This diff is collapsed.
Click to expand it.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketFactory.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.atomicfu.atomic
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.utils.io.ByteArrayPool
import
net.mamoe.mirai.utils.io.toUHexString
object
PacketFactoryList
:
MutableList
<
PacketFactory
<
*
,
*
>>
by
mutableListOf
()
/**
* 一种数据包的处理工厂. 它可以解密解码服务器发来的这个包, 也可以编码加密要发送给服务器的这个包
* 应由一个 `object` 实现, 且实现 `operator fun invoke`
*
* @param TPacket 服务器回复包解析结果
* @param TDecrypter 服务器回复包解密器
*/
abstract
class
PacketFactory
<
out
TPacket
:
Packet
,
TDecrypter
:
Decrypter
>(
internal
val
decrypterType
:
DecrypterType
<
TDecrypter
>)
{
/**
* 2 Ubyte.
* 读取注解 [AnnotatedId]
*/
private
val
annotatedId
:
AnnotatedId
get
()
=
(
this
::
class
.
annotations
.
firstOrNull
{
it
is
AnnotatedId
}
as
?
AnnotatedId
)
?:
error
(
"Annotation AnnotatedId not found"
)
/**
* 包 ID.
*/
open
val
id
:
PacketId
by
lazy
{
annotatedId
.
id
}
init
{
@Suppress
(
"LeakingThis"
)
PacketFactoryList
.
add
(
this
)
}
/**
* **解码**服务器的回复数据包
*/
abstract
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
TPacket
companion
object
{
private
val
sequenceIdInternal
=
atomic
(
1
)
@PublishedApi
internal
fun
atomicNextSequenceId
():
UShort
=
sequenceIdInternal
.
getAndIncrement
().
toUShort
()
}
}
object
UnknownPacketFactory
:
SessionPacketFactory
<
UnknownPacket
>()
{
override
suspend
fun
BotNetworkHandler
<*>.
processPacket
(
packet
:
UnknownPacket
)
{
ByteArrayPool
.
useInstance
{
packet
.
body
.
readAvailable
(
it
)
bot
.
logger
.
debug
(
"Unknown packet(${packet.id.value}) body = "
+
it
.
toUHexString
())
}
packet
.
body
.
close
()
}
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
UnknownPacket
{
return
UnknownPacket
(
id
,
this
)
}
}
object
IgnoredPacketFactory
:
SessionPacketFactory
<
IgnoredPacket
>()
{
override
suspend
fun
BotNetworkHandler
<*>.
processPacket
(
packet
:
IgnoredPacket
)
{
}
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
IgnoredPacket
=
IgnoredPacket
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
net.mamoe.mirai.network.protocol.tim.packet.action.*
import
net.mamoe.mirai.network.protocol.tim.packet.event.EventPacketFactory
import
net.mamoe.mirai.network.protocol.tim.packet.event.FriendOnlineStatusChangedPacket
import
net.mamoe.mirai.network.protocol.tim.packet.login.*
/**
* 通过 [value] 匹配一个 [IgnoredPacketId] 或 [KnownPacketId], 无匹配则返回一个 [UnknownPacketId].
*/
fun
matchPacketId
(
value
:
UShort
):
PacketId
=
IgnoredPacketIds
.
firstOrNull
{
it
.
value
==
value
}
?:
KnownPacketId
.
values
().
firstOrNull
{
it
.
value
==
value
}
?:
UnknownPacketId
(
value
)
/**
* 包 ID.
*/
interface
PacketId
{
val
value
:
UShort
val
factory
:
PacketFactory
<
*
,
*
>
}
/**
* 用于代表 `null`. 调用任何属性时都将会得到一个 [error]
*/
@Suppress
(
"unused"
)
object
NullPacketId
:
PacketId
{
override
val
factory
:
PacketFactory
<
*
,
*
>
get
()
=
error
(
"uninitialized"
)
override
val
value
:
UShort
get
()
=
error
(
"uninitialized"
)
}
/**
* 未知的 [PacketId]
*/
inline
class
UnknownPacketId
(
override
inline
val
value
:
UShort
)
:
PacketId
{
override
val
factory
:
PacketFactory
<
*
,
*
>
get
()
=
UnknownPacketFactory
}
object
IgnoredPacketIds
:
List
<
IgnoredPacketId
>
by
{
listOf
<
UShort
>(
).
map
{
IgnoredPacketId
(
it
.
toUShort
())
}
}()
inline
class
IgnoredPacketId
constructor
(
override
val
value
:
UShort
)
:
PacketId
{
override
val
factory
:
PacketFactory
<
*
,
*
>
get
()
=
IgnoredPacketFactory
}
/**
* 已知的 [matchPacketId]. 所有在 Mirai 中实现过的包都会使用这些 Id
*/
@Suppress
(
"unused"
)
enum
class
KnownPacketId
(
override
inline
val
value
:
UShort
,
override
inline
val
factory
:
PacketFactory
<
*
,
*
>)
:
PacketId
{
inline
TOUCH
(
0
x0825u
,
TouchPacket
),
inline
SESSION_KEY
(
0
x0828u
,
RequestSessionPacket
),
inline
LOGIN
(
0
x0836u
,
SubmitPasswordPacket
),
inline
CAPTCHA
(
0
x00BAu
,
CaptchaPacket
),
inline
SERVER_EVENT_1
(
0
x00CEu
,
EventPacketFactory
),
inline
SERVER_EVENT_2
(
0
x0017u
,
EventPacketFactory
),
inline
FRIEND_ONLINE_STATUS_CHANGE
(
0
x0081u
,
FriendOnlineStatusChangedPacket
),
inline
CHANGE_ONLINE_STATUS
(
0
x00ECu
,
ChangeOnlineStatusPacket
),
inline
HEARTBEAT
(
0
x0058u
,
HeartbeatPacket
),
inline
S_KEY
(
0
x001Du
,
RequestSKeyPacket
),
inline
ACCOUNT_INFO
(
0
x005Cu
,
RequestAccountInfoPacket
),
inline
SEND_GROUP_MESSAGE
(
0
x0002u
,
SendGroupMessagePacket
),
inline
SEND_FRIEND_MESSAGE
(
0
x00CDu
,
SendFriendMessagePacket
),
inline
CAN_ADD_FRIEND
(
0
x00A7u
,
CanAddFriendPacket
),
inline
GROUP_IMAGE_ID
(
0
x0388u
,
GroupImageIdRequestPacket
),
inline
FRIEND_IMAGE_ID
(
0
x0352u
,
FriendImageIdRequestPacket
),
inline
REQUEST_PROFILE_AVATAR
(
0
x0031u
,
RequestProfilePicturePacket
),
inline
REQUEST_PROFILE_DETAILS
(
0
x003Cu
,
RequestProfilePicturePacket
),
// @Suppress("DEPRECATION")
// inline SUBMIT_IMAGE_FILE_NAME(0x01BDu, SubmitImageFilenamePacket),
;
override
fun
toString
():
String
=
factory
.
let
{
it
::
class
.
simpleName
}
?:
this
.
name
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt
deleted
100644 → 0
View file @
745200f6
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.decryptBy
import
net.mamoe.mirai.utils.io.ByteArrayPool
import
net.mamoe.mirai.utils.io.hexToBytes
fun
ByteReadPacket
.
decryptBy
(
key
:
ByteArray
):
ByteReadPacket
=
decryptAsByteArray
(
key
)
{
data
->
ByteReadPacket
(
data
,
0
)
}
fun
ByteReadPacket
.
decryptBy
(
key
:
IoBuffer
):
ByteReadPacket
=
decryptAsByteArray
(
key
)
{
data
->
ByteReadPacket
(
data
,
0
)
}
fun
ByteReadPacket
.
decryptBy
(
keyHex
:
String
):
ByteReadPacket
=
decryptBy
(
keyHex
.
hexToBytes
())
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
key
:
ByteArray
,
consumer
:
(
ByteArray
)
->
R
):
R
=
ByteArrayPool
.
useInstance
{
val
length
=
remaining
.
toInt
()
readFully
(
it
,
0
,
length
)
consumer
(
it
.
decryptBy
(
key
,
length
))
}.
also
{
close
()
}
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
keyHex
:
String
,
consumer
:
(
ByteArray
)
->
R
):
R
=
this
.
decryptAsByteArray
(
keyHex
.
hexToBytes
(),
consumer
)
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
key
:
IoBuffer
,
consumer
:
(
ByteArray
)
->
R
):
R
=
ByteArrayPool
.
useInstance
{
val
length
=
remaining
.
toInt
()
readFully
(
it
,
0
,
length
)
consumer
(
it
.
decryptBy
(
key
,
length
))
}.
also
{
close
()
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt
deleted
100644 → 0
View file @
745200f6
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet
/*
ID: 00 17
长度 95
76 E4 B8 DD //1994701021
76 E4 B8 DD //1994701021
00 0B B9 A9 09 90 BB 54 1F //类似Event的uniqueId?
40 02
10 00 00 00
18 00
08 00
02 00
01 00
09 00
06 41 4B DA 4C 00 00
00 0A
00 04
01 00 00 00 00 00
00 06 00 00
00 0E
08 02
1A 02 08 49 0A 0C 08 A2 FF 8C F0
03 10 CA EB 8B ED 05
或者
长度63
00 00 27 10 76 E4 B8 DD
00 09 ED 26 64 73 0E CA 1F 40
00 12 00 00
00 08
00 0A
00 04
01 00 00
00 02
值都是一样的.
*/
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/AddContact.kt
View file @
2b477b3b
...
...
@@ -162,6 +162,11 @@ object AddFriendPacket : SessionPacketFactory<AddFriendPacket.AddFriendResponse>
}
}
override
suspend
fun
BotNetworkHandler
<*>.
processPacket
(
packet
:
AddFriendResponse
)
{
}
class
AddFriendResponse
:
Packet
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/GradeInfo.kt
→
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/
action/
GradeInfo.kt
View file @
2b477b3b
File moved
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadImage.kt
View file @
2b477b3b
This diff is collapsed.
Click to expand it.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/AndroidOnlineStatusChange.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.network.protocol.tim.packet.Packet
import
net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
import
net.mamoe.mirai.utils.io.readBoolean
@PacketVersion
(
date
=
"2019.11.2"
,
timVersion
=
"2.3.2.21173"
)
data class
AndroidDeviceStatusChangePacket
(
val
kind
:
Kind
)
:
Packet
{
enum
class
Kind
{
ONLINE
,
OFFLINE
}
}
/**
* Android 客户端在线状态改变
*/
@PacketVersion
(
date
=
"2019.10.31"
,
timVersion
=
"2.3.2.21173"
)
object
AndroidDeviceOnlineStatusChangedEventFactory
:
KnownEventParserAndHandler
<
AndroidDeviceStatusChangePacket
>(
0
x00C4u
)
{
override
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
AndroidDeviceStatusChangePacket
{
discardExact
(
13
)
return
AndroidDeviceStatusChangePacket
(
if
(
readBoolean
())
AndroidDeviceStatusChangePacket
.
Kind
.
OFFLINE
else
AndroidDeviceStatusChangePacket
.
Kind
.
ONLINE
)
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/EventPacket.kt
0 → 100644
View file @
2b477b3b
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
net.mamoe.mirai.event.Subscribable
import
net.mamoe.mirai.network.protocol.tim.packet.Packet
interface
EventPacket
:
Subscribable
,
Packet
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/EventPacketFactory.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
kotlinx.io.core.*
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.network.sessionKey
import
net.mamoe.mirai.qqAccount
import
net.mamoe.mirai.utils.io.encryptAndWrite
import
net.mamoe.mirai.utils.io.readIoBuffer
import
net.mamoe.mirai.utils.io.writeHex
import
net.mamoe.mirai.utils.io.writeQQ
/**
* 事件的识别 ID. 在 ACK 时使用
*/
class
EventPacketIdentity
(
val
from
:
UInt
,
//对于好友消息, 这个是发送人
val
to
:
UInt
,
//对于好友消息, 这个是bot
internal
val
uniqueId
:
IoBuffer
//8
)
{
override
fun
toString
():
String
=
"($from->$to)"
}
fun
BytePacketBuilder
.
writeEventPacketIdentity
(
identity
:
EventPacketIdentity
)
=
with
(
identity
)
{
writeUInt
(
from
)
writeUInt
(
to
)
writeFully
(
uniqueId
)
}
@Suppress
(
"FunctionName"
)
fun
matchEventPacketFactory
(
value
:
UShort
):
EventParserAndHandler
<
*
>
=
KnownEventParserAndHandler
.
firstOrNull
{
it
.
id
==
value
}
?:
IgnoredEventIds
.
firstOrNull
{
it
.
id
==
value
}
?:
UnknownEventParserAndHandler
(
value
)
/**
* 事件包, 它将会分析事件 ID 并解析事件为 [Packet]
*/
@Suppress
(
"FunctionName"
)
object
EventPacketFactory
:
PacketFactory
<
Packet
,
SessionKey
>(
SessionKey
)
{
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
Packet
{
val
eventIdentity
=
EventPacketIdentity
(
from
=
readUInt
(),
to
=
readUInt
(),
uniqueId
=
readIoBuffer
(
8
)
)
handler
.
sendPacket
(
EventPacketFactory
(
id
,
sequenceId
,
handler
.
bot
.
qqAccount
,
handler
.
sessionKey
,
eventIdentity
))
discardExact
(
2
)
return
with
(
matchEventPacketFactory
(
readUShort
()))
{
parse
(
handler
.
bot
,
eventIdentity
)
}.
also
{
if
(
it
is
MessageEventPacket
<
*
>)
{
it
.
botVar
=
handler
.
bot
}
if
(
it
is
EventParserAndHandler
<
*
>)
{
@Suppress
(
"UNCHECKED_CAST"
)
with
(
it
as
EventParserAndHandler
<
in
Packet
>)
{
with
(
handler
)
{
handlePacket
(
it
)
}
}
}
}
}
operator
fun
invoke
(
id
:
PacketId
,
sequenceId
:
UShort
,
bot
:
UInt
,
sessionKey
:
SessionKey
,
identity
:
EventPacketIdentity
):
OutgoingPacket
=
buildOutgoingPacket
(
name
=
"EventPacket"
,
id
=
id
,
sequenceId
=
sequenceId
)
{
writeQQ
(
bot
)
writeHex
(
TIMProtocol
.
fixVer2
)
encryptAndWrite
(
sessionKey
)
{
writeEventPacketIdentity
(
identity
)
}
}
}
interface
EventParserAndHandler
<
TPacket
:
Packet
>
{
val
id
:
UShort
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
TPacket
/**
* 在 [BotNetworkHandler] 下处理这个包. 广播事件等.
*/
suspend
fun
BotNetworkHandler
<*>.
handlePacket
(
packet
:
TPacket
)
{}
}
abstract
class
KnownEventParserAndHandler
<
TPacket
:
Packet
>(
override
val
id
:
UShort
)
:
EventParserAndHandler
<
TPacket
>
{
companion
object
FactoryList
:
MutableList
<
KnownEventParserAndHandler
<
*
>>
by
mutableListOf
(
AndroidDeviceOnlineStatusChangedEventFactory
,
FriendConversationInitializedEventParserAndHandler
,
GroupFileUploadEventFactory
,
GroupMemberPermissionChangedEventFactory
,
GroupMessageEventParserAndHandler
,
FriendMessageEventParserAndHandler
)
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/FriendConversationIniliaze.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readUInt
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
@PacketVersion
(
date
=
"2019.11.2"
,
timVersion
=
"2.3.2.21173"
)
data class
FriendConversationInitialize
(
val
qq
:
UInt
)
:
EventPacket
object
FriendConversationInitializedEventParserAndHandler
:
KnownEventParserAndHandler
<
FriendConversationInitialize
>(
0
x0079u
)
{
override
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
FriendConversationInitialize
{
discardExact
(
4
)
// 00 00 00 00
return
FriendConversationInitialize
(
readUInt
())
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/FriendOnlineStatusChanged.kt
→
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/
event/
FriendOnlineStatusChanged.kt
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
,
"JoinDeclarationAndAssignment"
)
package
net.mamoe.mirai.network.protocol.tim.packet
package
net.mamoe.mirai.network.protocol.tim.packet
.event
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readUByte
import
kotlinx.io.core.readUInt
import
net.mamoe.mirai.event.events.FriendOnlineStatusChangedEvent
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.getQQ
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.protocol.tim.packet.AnnotatedId
import
net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId
import
net.mamoe.mirai.network.protocol.tim.packet.PacketId
import
net.mamoe.mirai.network.protocol.tim.packet.SessionPacketFactory
import
net.mamoe.mirai.utils.OnlineStatus
@CorrespondingEvent
(
FriendOnlineStatusChangedEvent
::
class
)
abstract
class
FriendStatusChanged
:
Packet
{
abstract
val
qq
:
UInt
abstract
val
status
:
OnlineStatus
}
data class
FriendStatusChanged
(
val
qq
:
QQ
,
val
status
:
OnlineStatus
)
:
EventPacket
/**
* 好友在线状态改变
...
...
@@ -22,17 +26,12 @@ abstract class FriendStatusChanged : Packet {
@AnnotatedId
(
KnownPacketId
.
FRIEND_ONLINE_STATUS_CHANGE
)
object
FriendOnlineStatusChangedPacket
:
SessionPacketFactory
<
FriendStatusChanged
>()
{
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
FriendStatusChanged
=
object
:
FriendStatusChanged
()
{
override
val
qq
:
UInt
override
val
status
:
OnlineStatus
init
{
qq
=
readUInt
()
discardExact
(
8
)
val
id
=
readUByte
()
status
=
OnlineStatus
.
ofId
(
id
)
?:
error
(
"Unknown online status id $id"
)
}
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
FriendStatusChanged
{
val
qq
=
readUInt
()
discardExact
(
8
)
val
statusId
=
readUByte
()
val
status
=
OnlineStatus
.
ofId
(
statusId
)
?:
error
(
"Unknown online status id $statusId"
)
return
FriendStatusChanged
(
handler
.
bot
.
getQQ
(
qq
),
status
)
}
//在线 XX XX XX XX 01 00 00 00 00 00 00 00 0A 15 E3 10 00 01 2E 01 00 00 00 00 00 00 00 00 00 00 00 13 08 02 C2 76 E4 B8 DD 00 00 00 00 00 00 00 00 00 00 00
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/GroupFileUpload.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
import
net.mamoe.mirai.utils.io.readString
data class
GroupFileUploadPacket
(
inline
val
xmlMessage
:
String
)
:
EventPacket
@PacketVersion
(
date
=
"2019.7.1"
,
timVersion
=
"2.3.2.21173"
)
object
GroupFileUploadEventFactory
:
KnownEventParserAndHandler
<
GroupFileUploadPacket
>(
0
x002Du
)
{
override
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
GroupFileUploadPacket
{
discardExact
(
60
)
val
size
=
readShort
().
toInt
()
discardExact
(
3
)
return
GroupFileUploadPacket
(
xmlMessage
=
readString
(
size
))
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Ignored.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
kotlinx.io.core.ByteReadPacket
import
net.mamoe.mirai.Bot
object
IgnoredEventPacket
:
EventPacket
object
IgnoredEventIds
:
List
<
IgnoredEventParserAndHandler
>
by
{
listOf
(
0
x0021u
).
map
{
IgnoredEventParserAndHandler
(
it
.
toUShort
())
}
}()
inline
class
IgnoredEventParserAndHandler
(
override
val
id
:
UShort
)
:
EventParserAndHandler
<
IgnoredEventPacket
>
{
override
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
IgnoredEventPacket
=
IgnoredEventPacket
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MemberPermission.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_UNSIGNED_LITERALS"
,
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readUInt
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.network.protocol.tim.packet.Packet
import
net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
data class
MemberPermissionChangePacket
(
val
groupId
:
UInt
,
val
qq
:
UInt
,
val
kind
:
Kind
)
:
Packet
{
enum
class
Kind
{
/**
* 变成管理员
*/
BECOME_OPERATOR
,
/**
* 不再是管理员
*/
NO_LONGER_OPERATOR
,
}
// TODO: 2019/11/2 变成群主的情况
}
@PacketVersion
(
date
=
"2019.11.1"
,
timVersion
=
"2.3.2.21173"
)
object
GroupMemberPermissionChangedEventFactory
:
KnownEventParserAndHandler
<
MemberPermissionChangePacket
>(
0
x002Cu
)
{
override
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
MemberPermissionChangePacket
{
// 群里一个人变成管理员:
// 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 01 76 E4 B8 DD 01
// 取消管理员
// 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 00 76 E4 B8 DD 00
discardExact
(
remaining
-
5
)
val
qq
=
readUInt
()
val
kind
=
when
(
readByte
().
toInt
())
{
0
x00
->
MemberPermissionChangePacket
.
Kind
.
NO_LONGER_OPERATOR
0
x01
->
MemberPermissionChangePacket
.
Kind
.
BECOME_OPERATOR
else
->
error
(
"Could not determine permission change kind"
)
}
return
MemberPermissionChangePacket
(
identity
.
from
,
qq
,
kind
)
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Message.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_UNSIGNED_LITERALS"
,
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
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.Contact
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.event.events.BotEvent
import
net.mamoe.mirai.getGroup
import
net.mamoe.mirai.getQQ
import
net.mamoe.mirai.message.*
import
net.mamoe.mirai.message.internal.readMessageChain
import
net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
import
net.mamoe.mirai.utils.ExternalImage
import
net.mamoe.mirai.utils.MiraiLogger
import
net.mamoe.mirai.utils.io.printTLVMap
import
net.mamoe.mirai.utils.io.read
import
net.mamoe.mirai.utils.io.readTLVMap
import
net.mamoe.mirai.utils.io.readUShortLVByteArray
import
net.mamoe.mirai.utils.sendTo
import
net.mamoe.mirai.utils.upload
sealed
class
MessageEventPacket
<
TSubject
:
Contact
>
:
EventPacket
,
BotEvent
()
{
internal
lateinit
var
botVar
:
Bot
override
val
bot
:
Bot
get
()
=
botVar
/**
* 消息事件主体.
*
* 对于好友消息, 这个属性为 [QQ] 的实例;
* 对于群消息, 这个属性为 [Group] 的实例
*
* 在回复消息时, 可通过 [subject] 作为回复对象
*/
abstract
val
subject
:
TSubject
/**
* 发送人
*/
abstract
val
sender
:
QQ
abstract
val
message
:
MessageChain
// region Send to subject
/**
* 给这个消息事件的主体发送消息
* 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
* 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
*/
suspend
inline
fun
reply
(
message
:
MessageChain
)
=
subject
.
sendMessage
(
message
)
suspend
fun
reply
(
message
:
Message
)
=
subject
.
sendMessage
(
message
.
singleChain
())
suspend
fun
reply
(
plain
:
String
)
=
subject
.
sendMessage
(
plain
.
toMessage
())
suspend
inline
fun
ExternalImage
.
send
()
=
this
.
sendTo
(
subject
)
suspend
inline
fun
ExternalImage
.
upload
():
Image
=
this
.
upload
(
subject
)
suspend
inline
fun
Image
.
send
()
=
this
.
sendTo
(
subject
)
suspend
inline
fun
ImageId
.
send
()
=
this
.
sendTo
(
subject
)
suspend
inline
fun
Message
.
send
()
=
this
.
sendTo
(
subject
)
suspend
inline
fun
String
.
send
()
=
this
.
toMessage
().
sendTo
(
subject
)
// endregion
}
// region group message
data class
GroupMessage
(
val
group
:
Group
,
val
senderName
:
String
,
/**
* 发送方权限.
*/
val
permission
:
MemberPermission
,
override
val
sender
:
QQ
,
override
val
message
:
MessageChain
=
NullMessageChain
)
:
MessageEventPacket
<
Group
>()
{
override
val
subject
:
Group
get
()
=
group
}
@PacketVersion
(
date
=
"2019.11.2"
,
timVersion
=
"2.3.2.21173"
)
object
GroupMessageEventParserAndHandler
:
KnownEventParserAndHandler
<
GroupMessage
>(
0
x0052u
)
{
override
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
GroupMessage
{
discardExact
(
31
)
val
groupNumber
=
readUInt
()
discardExact
(
1
)
val
qq
=
readUInt
()
discardExact
(
48
)
readUShortLVByteArray
()
discardExact
(
2
)
//2个0x00
val
message
=
readMessageChain
()
var
senderPermission
:
MemberPermission
=
MemberPermission
.
MEMBER
var
senderName
=
""
val
map
=
readTLVMap
(
true
)
if
(
map
.
containsKey
(
18
u
))
{
map
.
getValue
(
18
u
).
read
{
val
tlv
=
readTLVMap
(
true
)
senderPermission
=
when
(
tlv
.
takeIf
{
it
.
containsKey
(
0
x04u
)
}
?.
get
(
0
x04u
)
?.
getOrNull
(
3
)
?.
toUInt
())
{
null
->
MemberPermission
.
MEMBER
0
x08u
->
MemberPermission
.
OWNER
0
x10u
->
MemberPermission
.
OPERATOR
else
->
{
tlv
.
printTLVMap
(
"TLV(tag=18) Map"
)
MiraiLogger
.
warning
(
"Could not determine member permission, default permission MEMBER is being used"
)
MemberPermission
.
MEMBER
}
}
senderName
=
when
{
tlv
.
containsKey
(
0
x01u
)
->
kotlinx
.
io
.
core
.
String
(
tlv
.
getValue
(
0
x01u
))
//这个人的qq昵称
tlv
.
containsKey
(
0
x02u
)
->
kotlinx
.
io
.
core
.
String
(
tlv
.
getValue
(
0
x02u
))
//这个人的群名片
else
->
{
tlv
.
printTLVMap
(
"TLV(tag=18) Map"
)
MiraiLogger
.
warning
(
"Could not determine senderName"
)
"null"
}
}
}
}
return
GroupMessage
(
bot
.
getGroup
(
groupNumber
),
senderName
,
senderPermission
,
bot
.
getQQ
(
qq
),
message
)
}
}
// endregion
// region friend message
data class
FriendMessage
(
/**
* 是否是在这次登录之前的消息, 即消息记录
*/
val
isPrevious
:
Boolean
,
override
val
sender
:
QQ
,
override
val
message
:
MessageChain
)
:
MessageEventPacket
<
QQ
>()
{
override
val
subject
:
QQ
get
()
=
sender
}
@Suppress
(
"unused"
)
@PacketVersion
(
date
=
"2019.11.2"
,
timVersion
=
"2.3.2.21173"
)
object
FriendMessageEventParserAndHandler
:
KnownEventParserAndHandler
<
FriendMessage
>(
0
x00A6u
)
{
override
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
FriendMessage
{
discardExact
(
2
)
val
l1
=
readShort
()
discardExact
(
1
)
//0x00
val
previous
=
readByte
().
toInt
()
==
0
x08
discardExact
(
l1
.
toInt
()
-
2
)
//java.io.EOFException: Only 49 bytes were discarded of 69 requested
//抖动窗口消息
discardExact
(
69
)
readUShortLVByteArray
()
//font
discardExact
(
2
)
//2个0x00
val
message
=
readMessageChain
()
return
FriendMessage
(
isPrevious
=
previous
,
sender
=
bot
.
getQQ
(
identity
.
from
),
message
=
message
)
}
}
// endregion
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt
deleted
100644 → 0
View file @
745200f6
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
kotlinx.io.core.*
import
net.mamoe.mirai.event.events.MemberPermissionChangedEvent
import
net.mamoe.mirai.message.internal.readMessageChain
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.network.protocol.tim.TIMProtocol
import
net.mamoe.mirai.network.protocol.tim.packet.*
import
net.mamoe.mirai.network.sessionKey
import
net.mamoe.mirai.qqAccount
import
net.mamoe.mirai.utils.MiraiLogger
import
net.mamoe.mirai.utils.io.*
/**
* 事件的识别 ID. 在 [事件确认包][ServerEventPacket.EventResponse] 中被使用.
*/
class
EventPacketIdentity
(
val
from
:
UInt
,
//对于好友消息, 这个是发送人
val
to
:
UInt
,
//对于好友消息, 这个是bot
internal
val
uniqueId
:
IoBuffer
//8
)
{
override
fun
toString
():
String
=
"($from->$to)"
}
enum
class
SenderPermission
{
OWNER
,
OPERATOR
,
MEMBER
;
}
object
IgnoredEventPacket
:
Packet
class
UnknownEventPacket
(
val
id
:
UnknownEventId
// TODO: 2019/11/5 补充包数据 , 用于输出
)
:
Packet
/**
* 事件包, 它将会分析 [事件ID][KnownEventId] 并解析事件为 [Packet]
*/
@NoLog
@Suppress
(
"FunctionName"
)
object
EventPacketFactory
:
PacketFactory
<
Packet
,
SessionKey
>(
SessionKey
)
{
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
Packet
{
val
eventIdentity
=
EventPacketIdentity
(
from
=
readUInt
(),
to
=
readUInt
(),
uniqueId
=
readIoBuffer
(
8
)
)
handler
.
sendPacket
(
EventPacketFactory
(
id
,
sequenceId
,
handler
.
bot
.
qqAccount
,
handler
.
sessionKey
,
eventIdentity
))
discardExact
(
2
)
return
when
(
val
type
=
EventId
(
readUShort
()))
{
is
KnownEventId
->
type
.
parser
(
this
,
eventIdentity
)
is
UnknownEventId
->
UnknownEventPacket
(
type
)
is
IgnoredEventId
->
IgnoredEventPacket
else
->
throw
AssertionError
(
"Unknown EventId type"
)
}
}
operator
fun
invoke
(
id
:
PacketId
,
sequenceId
:
UShort
,
bot
:
UInt
,
sessionKey
:
SessionKey
,
identity
:
EventPacketIdentity
):
OutgoingPacket
=
buildOutgoingPacket
(
name
=
"EventPacket"
,
id
=
id
,
sequenceId
=
sequenceId
)
{
writeQQ
(
bot
)
writeHex
(
TIMProtocol
.
fixVer2
)
encryptAndWrite
(
sessionKey
)
{
writeEventPacketIdentity
(
identity
)
}
}
}
fun
BytePacketBuilder
.
writeEventPacketIdentity
(
identity
:
EventPacketIdentity
)
=
with
(
identity
)
{
writeUInt
(
from
)
writeUInt
(
to
)
writeFully
(
uniqueId
)
}
typealias
EventPacketParser
=
ByteReadPacket
.(
EventPacketIdentity
)
->
Packet
interface
EventId
{
val
value
:
UShort
val
parser
:
EventPacketParser
?
}
// TODO: 2019/11/5 整理文件
@Suppress
(
"FunctionName"
)
fun
EventId
(
value
:
UShort
):
EventId
=
KnownEventId
.
ofValueOrNull
(
value
)
?:
IgnoredEventIds
.
firstOrNull
{
it
.
value
==
value
}
?:
UnknownEventId
(
value
)
object
IgnoredEventIds
:
List
<
IgnoredEventId
>
by
{
listOf
(
0
x0021u
).
map
{
IgnoredEventId
(
it
.
toUShort
())
}
}()
inline
class
IgnoredEventId
(
override
val
value
:
UShort
)
:
EventId
{
override
val
parser
:
EventPacketParser
get
()
=
{
IgnoredPacket
}
}
inline
class
UnknownEventId
(
override
val
value
:
UShort
)
:
EventId
{
override
val
parser
:
EventPacketParser
get
()
=
{
MiraiLogger
.
debug
(
"UnknownEventPacket type = ${value.toUHexString()}"
)
MiraiLogger
.
debug
(
"UnknownEventPacket data = ${readBytes().toUHexString()}"
)
UnknownEventPacket
(
UnknownEventId
(
value
))
}
}
/**
*
* @param parser 解析器. 解析 [数据包][ByteReadPacket] 为 [Packet]
*/
@Suppress
(
"unused"
)
enum
class
KnownEventId
(
override
inline
val
value
:
UShort
,
override
val
parser
:
EventPacketParser
)
:
EventId
{
/**
* Android 客户端在线状态改变
*/
ANDROID_DEVICE_ONLINE_STATUS_CHANGE
(
0
x00C4u
,
{
discardExact
(
13
)
EventPacket
.
AndroidDeviceStatusChange
(
if
(
readBoolean
())
EventPacket
.
AndroidDeviceStatusChange
.
Kind
.
OFFLINE
else
EventPacket
.
AndroidDeviceStatusChange
.
Kind
.
ONLINE
)
}),
GROUP_FILE_UPLOAD
(
0
x002Du
,
{
discardExact
(
60
)
val
size
=
readShort
().
toInt
()
discardExact
(
3
)
EventPacket
.
GroupFileUpload
(
xmlMessage
=
readString
(
size
))
}),
@PacketVersion
(
date
=
"2019.11.2"
,
timVersion
=
"2.3.2.21173"
)
GROUP_MEMBER_PERMISSION_CHANGE
(
0
x002Cu
,
{
// 群里一个人变成管理员:
// 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 01 76 E4 B8 DD 01
// 取消管理员
// 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 00 76 E4 B8 DD 00
discardExact
(
remaining
-
5
)
EventPacket
.
MemberPermissionChange
().
apply
{
groupId
=
it
.
from
qq
=
readUInt
()
kind
=
when
(
readByte
().
toInt
())
{
0
x00
->
MemberPermissionChangedEvent
.
Kind
.
NO_LONGER_OPERATOR
0
x01
->
MemberPermissionChangedEvent
.
Kind
.
BECOME_OPERATOR
else
->
error
(
"Could not determine permission change kind"
)
}
}
}),
@PacketVersion
(
date
=
"2019.11.2"
,
timVersion
=
"2.3.2.21173"
)
GROUP_MESSAGE
(
0
x0052u
,
{
EventPacket
.
GroupMessage
().
apply
{
discardExact
(
31
)
groupNumber
=
readUInt
()
discardExact
(
1
)
qq
=
readUInt
()
discardExact
(
48
)
readUShortLVByteArray
()
discardExact
(
2
)
//2个0x00
message
=
readMessageChain
()
val
map
=
readTLVMap
(
true
)
if
(
map
.
containsKey
(
18
u
))
{
map
.
getValue
(
18
u
).
read
{
val
tlv
=
readTLVMap
(
true
)
senderPermission
=
when
(
tlv
.
takeIf
{
it
.
containsKey
(
0
x04u
)
}
?.
get
(
0
x04u
)
?.
getOrNull
(
3
)
?.
toUInt
())
{
null
->
SenderPermission
.
MEMBER
0
x08u
->
SenderPermission
.
OWNER
0
x10u
->
SenderPermission
.
OPERATOR
else
->
{
tlv
.
printTLVMap
(
"TLV(tag=18) Map"
)
MiraiLogger
.
warning
(
"Could not determine member permission, default permission MEMBER is being used"
)
SenderPermission
.
MEMBER
}
}
senderName
=
when
{
tlv
.
containsKey
(
0
x01u
)
->
String
(
tlv
.
getValue
(
0
x01u
))
//这个人的qq昵称
tlv
.
containsKey
(
0
x02u
)
->
String
(
tlv
.
getValue
(
0
x02u
))
//这个人的群名片
else
->
{
tlv
.
printTLVMap
(
"TLV(tag=18) Map"
)
MiraiLogger
.
warning
(
"Could not determine senderName"
)
"null"
}
}
}
}
}
}),
@PacketVersion
(
date
=
"2019.11.2"
,
timVersion
=
"2.3.2.21173"
)
FRIEND_MESSAGE
(
0
x00A6u
,
{
discardExact
(
2
)
val
l1
=
readShort
()
discardExact
(
1
)
//0x00
val
previous
=
readByte
().
toInt
()
==
0
x08
discardExact
(
l1
.
toInt
()
-
2
)
//java.io.EOFException: Only 49 bytes were discarded of 69 requested
//抖动窗口消息
discardExact
(
69
)
readUShortLVByteArray
()
//font
discardExact
(
2
)
//2个0x00
EventPacket
.
FriendMessage
(
isPrevious
=
previous
,
qq
=
it
.
from
,
message
=
readMessageChain
()
)
}),
FRIEND_CONVERSATION_INITIALIZE
(
0
x0079u
,
{
discardExact
(
4
)
// 00 00 00 00
EventPacket
.
FriendConversationInitialize
().
apply
{
qq
=
readUInt
()
}
}),
;
companion
object
{
fun
ofValueOrNull
(
value
:
UShort
):
KnownEventId
?
=
values
().
firstOrNull
{
it
.
value
==
value
}
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Unknown.kt
0 → 100644
View file @
2b477b3b
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.network.protocol.tim.packet.event
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.readBytes
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.network.BotNetworkHandler
import
net.mamoe.mirai.utils.MiraiLogger
import
net.mamoe.mirai.utils.io.ByteArrayPool
import
net.mamoe.mirai.utils.io.toUHexString
data class
UnknownEventPacket
(
val
id
:
UShort
,
val
body
:
ByteReadPacket
)
:
EventPacket
//TODO This class should be declared with `inline`, but a CompilationException will be thrown
class
UnknownEventParserAndHandler
(
override
val
id
:
UShort
)
:
EventParserAndHandler
<
UnknownEventPacket
>
{
override
suspend
fun
ByteReadPacket
.
parse
(
bot
:
Bot
,
identity
:
EventPacketIdentity
):
UnknownEventPacket
{
MiraiLogger
.
debug
(
"UnknownEventPacket type = ${id.toUHexString()}"
)
MiraiLogger
.
debug
(
"UnknownEventPacket data = ${readBytes().toUHexString()}"
)
return
UnknownEventPacket
(
id
,
this
)
//TODO the cause is that `this` reference.
}
override
suspend
fun
BotNetworkHandler
<*>.
handlePacket
(
packet
:
UnknownEventPacket
)
{
ByteArrayPool
.
useInstance
{
packet
.
body
.
readAvailable
(
it
)
bot
.
logger
.
debug
(
"Unknown packet(${packet.id}) data = "
+
it
.
toUHexString
())
}
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Captcha.kt
View file @
2b477b3b
...
...
@@ -110,7 +110,7 @@ object CaptchaPacket : PacketFactory<CaptchaPacket.CaptchaResponse, CaptchaKey>(
}
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
CaptchaResponse
=
when
(
val
id
=
readByte
().
toUInt
())
{
when
(
val
flag
=
readByte
().
toUInt
())
{
0
x14u
->
{
//00 05 00 00 00 00 00 00 38
CaptchaResponse
.
Correct
().
apply
{
discardExact
(
9
)
...
...
@@ -134,7 +134,7 @@ object CaptchaPacket : PacketFactory<CaptchaPacket.CaptchaResponse, CaptchaKey>(
}
}
else
->
error
(
"Unable to analyze RequestCaptchaTransmissionPacket, unknown id: $
id
"
)
else
->
error
(
"Unable to analyze RequestCaptchaTransmissionPacket, unknown id: $
flag
"
)
}
}
/*
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ChangeOnlineStatusPacket.kt
View file @
2b477b3b
...
...
@@ -31,7 +31,9 @@ object ChangeOnlineStatusPacket : PacketFactory<ChangeOnlineStatusPacket.ChangeO
}
}
object
ChangeOnlineStatusResponse
:
Packet
object
ChangeOnlineStatusResponse
:
Packet
{
override
fun
toString
():
String
=
this
::
class
.
simpleName
!!
}
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
ChangeOnlineStatusResponse
=
ChangeOnlineStatusResponse
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt
→
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/
login/
Heartbeat.kt
View file @
2b477b3b
File moved
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/PasswordSubmission.kt
View file @
2b477b3b
...
...
@@ -12,7 +12,6 @@ import net.mamoe.mirai.utils.decryptBy
import
net.mamoe.mirai.utils.encryptBy
import
net.mamoe.mirai.utils.io.*
import
net.mamoe.mirai.utils.writeCRC32
import
kotlin.properties.Delegates
object
ShareKey
:
DecrypterByteArray
,
DecrypterType
<
ShareKey
>
{
override
val
value
:
ByteArray
=
TIMProtocol
.
shareKey
.
hexToBytes
(
withCache
=
false
)
...
...
@@ -27,9 +26,14 @@ inline class SubmitPasswordResponseDecrypter(private val privateKey: PrivateKey)
var
decrypted
=
ShareKey
.
decrypt
(
packet
)
(
decrypted
.
remaining
).
let
{
if
(
it
.
toInt
()
%
8
==
0
&&
it
>=
16
)
{
decrypted
=
privateKey
.
decrypt
(
decrypted
)
decrypted
=
try
{
privateKey
.
decrypt
(
decrypted
)
}
catch
(
e
:
Exception
)
{
// 某些情况不需要这次解密
decrypted
}
}
}
// TODO: 2019/11/5 优化: 某些情况下并不需要这次解密. 根据长度判断会导致一些问题
}
return
decrypted
}
...
...
@@ -59,7 +63,7 @@ object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse,
writeZero
(
2
)
writeShort
(
16
);
writeHex
(
TIMProtocol
.
key0836
)
//=16
//
TODO
shareKey 极大可能为 publicKey, key0836 计算得到
// shareKey 极大可能为 publicKey, key0836 计算得到
encryptAndWrite
(
TIMProtocol
.
shareKey
)
{
writePart1
(
bot
,
password
,
loginTime
,
loginIP
,
privateKey
,
token0825
,
randomDeviceName
,
tlv0006
)
if
(
token00BA
!=
null
)
{
...
...
@@ -73,71 +77,112 @@ object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse,
}
sealed
class
LoginResponse
:
Packet
{
class
KeyExchange
:
LoginResponse
()
{
lateinit
var
tlv0006
:
IoBuffer
//120bytes
var
tokenUnknown
:
ByteArray
?
=
null
data class
KeyExchange
(
val
tlv0006
:
IoBuffer
,
//120bytes
val
tokenUnknown
:
ByteArray
?,
val
privateKeyUpdate
:
PrivateKey
//16bytes
)
:
LoginResponse
()
{
override
fun
equals
(
other
:
Any
?):
Boolean
{
if
(
this
===
other
)
return
true
if
(
other
!
is
KeyExchange
)
return
false
if
(
tlv0006
!=
other
.
tlv0006
)
return
false
if
(
tokenUnknown
!=
null
)
{
if
(
other
.
tokenUnknown
==
null
)
return
false
if
(!
tokenUnknown
.
contentEquals
(
other
.
tokenUnknown
))
return
false
}
else
if
(
other
.
tokenUnknown
!=
null
)
return
false
if
(
privateKeyUpdate
!=
other
.
privateKeyUpdate
)
return
false
return
true
}
var
privateKeyUpdate
:
PrivateKey
?
=
null
//16bytes
override
fun
hashCode
():
Int
{
var
result
=
tlv0006
.
hashCode
()
result
=
31
*
result
+
(
tokenUnknown
?.
contentHashCode
()
?:
0
)
result
=
31
*
result
+
privateKeyUpdate
.
hashCode
()
return
result
}
}
class
CaptchaInit
:
LoginResponse
()
{
lateinit
var
captchaPart1
:
IoBuffer
lateinit
var
token00BA
:
ByteArray
var
unknownBoolean
:
Boolean
by
Delegates
.
notNull
()
}
data class
CaptchaInit
(
val
captchaPart1
:
IoBuffer
,
val
token00BA
:
ByteArray
,
val
unknownBoolean
:
Boolean
)
:
LoginResponse
()
{
override
fun
equals
(
other
:
Any
?):
Boolean
{
if
(
this
===
other
)
return
true
if
(
other
!
is
CaptchaInit
)
return
false
class
Success
:
LoginResponse
()
{
var
sessionResponseDecryptionKey
:
SessionResponseDecryptionKey
by
Delegates
.
notNull
()
//16 bytes|
if
(
captchaPart1
!=
other
.
captchaPart1
)
return
false
if
(!
token00BA
.
contentEquals
(
other
.
token00BA
))
return
false
if
(
unknownBoolean
!=
other
.
unknownBoolean
)
return
false
lateinit
var
token38
:
IoBuffer
//56
lateinit
var
token88
:
IoBuffer
//136
lateinit
var
encryptionKey
:
IoBuffer
//16
return
true
}
lateinit
var
nickname
:
String
var
age
:
Short
by
Delegates
.
notNull
()
lateinit
var
gender
:
Gender
override
fun
hashCode
():
Int
{
var
result
=
captchaPart1
.
hashCode
()
result
=
31
*
result
+
token00BA
.
contentHashCode
()
result
=
31
*
result
+
unknownBoolean
.
hashCode
()
return
result
}
}
class
Failed
(
val
result
:
LoginResult
)
:
LoginResponse
()
data class
Success
(
val
sessionResponseDecryptionKey
:
SessionResponseDecryptionKey
,
val
token38
:
IoBuffer
,
//56
val
token88
:
IoBuffer
,
//136
val
encryptionKey
:
IoBuffer
,
//16
val
nickname
:
String
,
val
age
:
Short
,
val
gender
:
Gender
)
:
LoginResponse
()
data class
Failed
(
val
result
:
LoginResult
)
:
LoginResponse
()
}
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
LoginResponse
{
val
size
=
remaining
.
toInt
()
return
when
{
size
==
229
||
size
==
271
||
size
==
207
->
LoginResponse
.
KeyExchange
().
apply
{
size
==
229
||
size
==
271
||
size
==
207
->
{
discardExact
(
5
)
//01 00 1E 00 10
privateKeyUpdate
=
PrivateKey
(
readBytes
(
0
x10
))
val
privateKeyUpdate
=
PrivateKey
(
readBytes
(
0
x10
))
discardExact
(
4
)
//00 06 00 78
tlv0006
=
readIoBuffer
(
0
x78
)
val
tlv0006
=
readIoBuffer
(
0
x78
)
try
{
return
try
{
discardExact
(
8
)
//01 10 00 3C 00 01 00 38
tokenUnknown
=
readBytes
(
56
)
LoginResponse
.
KeyExchange
(
tlv0006
,
readBytes
(
56
),
privateKeyUpdate
)
}
catch
(
e
:
EOFException
)
{
//什么都不做. 因为有的包就是没有这个数据.
LoginResponse
.
KeyExchange
(
tlv0006
,
null
,
privateKeyUpdate
)
}
}
size
==
844
||
size
==
871
->
LoginResponse
.
CaptchaInit
().
apply
{
size
==
844
||
size
==
871
->
{
discardExact
(
78
)
//println(readRemainingBytes().toUHexString())
val
captchaLength
=
readShort
()
//2bytes
this
.
captchaPart1
=
readIoBuffer
(
captchaLength
)
val
captchaPart1
=
readIoBuffer
(
captchaLength
)
discardExact
(
1
)
this
.
unknownBoolean
=
readByte
().
toInt
()
==
1
val
unknownBoolean
=
readByte
().
toInt
()
==
1
discardExact
(
remaining
-
60
)
this
.
token00BA
=
readBytes
(
40
)
return
LoginResponse
.
CaptchaInit
(
captchaPart1
,
readBytes
(
40
),
unknownBoolean
)
}
size
>
650
->
LoginResponse
.
Success
().
apply
{
size
>
650
->
{
discardExact
(
7
)
//00 01 09 00 70 00 01
//FB 01 04 03 33
encryptionKey
=
readIoBuffer
(
16
)
//C6 72 C7 73 70 01 46 A2 11 88 AC E4 92 7B BF 90
val
encryptionKey
=
readIoBuffer
(
16
)
//C6 72 C7 73 70 01 46 A2 11 88 AC E4 92 7B BF 90
discardExact
(
2
)
//00 38
token38
=
readIoBuffer
(
56
)
val
token38
=
readIoBuffer
(
56
)
discardExact
(
60
)
//00 20 01 60 C5 A1 39 7A 12 8E BC 34 C3 56 70 E3 1A ED 20 67 ED A9 DB 06 C1 70 81 3C 01 69 0D FF 63 DA 00 00 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6
...
...
@@ -159,24 +204,26 @@ object SubmitPasswordPacket : PacketFactory<SubmitPasswordPacket.LoginResponse,
discardExact
(
23
+
3
)
//01 D3 00 01 00 16 00 00 00 01 00 00 00 64 00 00 0D DE 00 09 3A 80 00
discardExact
(
2
)
//00 02
sessionResponseDecryptionKey
=
SessionResponseDecryptionKey
(
readIoBuffer
(
16
))
val
sessionResponseDecryptionKey
=
SessionResponseDecryptionKey
(
readIoBuffer
(
16
))
discardExact
(
2
)
token88
=
readIoBuffer
(
136
)
val
token88
=
readIoBuffer
(
136
)
discardExact
(
299
)
//2E 72 7A 50 41 54 5B 62 7D 47 5D 37 41 53 47 51 00 78 00 01 5D A2 DB 79 00 70 72 E7 D3 4E 6F D8 D1 DD F2 67 04 1D 23 4D E9 A7 AB 89 7A B7 E6 4B C0 79 60 3B 4F AA 31 C5 24 51 C1 4B 4F A4 32 74 BA FE 8E 06 DB 54 25 A2 56 91 E8 66 BB 23 29 EB F7 13 7B 94 1E AF B2 40 4E 69 5C 8C 35 04 D1 25 1F 60 93 F3 40 71 0B 61 60 F1 B6 A9 7A E8 B1 DA 0E 16 A2 F1 2D 69 5A 01 20 7A AB A7 37 68 D2 1A B0 4D 35 D1 E1 35 64 F6 90 2B 00 83 01 24 5B 4E 69 3D 45 54 6B 29 5E 73 23 2D 4E 42 3F 00 70 00 01 5D A2 DB 79 00 68 FD 10 8A 39 51 09 C6 69 CE 09 A4 52 8C 53 D3 B6 87 E1 7B 7E 4E 52 6D BA 9C C4 6E 6D DE 09 99 67 B4 BD 56 71 14 5A 54 01 68 1C 3C AA 0D 76 0B 86 5A C1 F1 BC 5E 0A ED E3 8C 57 86 35 D8 A5 F8 16 01 24 8B 57 56 8C A6 31 6F 65 73 03 DA ED 21 FA 6B 79 32 2B 09 01 E8 D2 D8 F0 7B F1 60 C2 7F 53 5D F6 53 50 8A 43 E2 23 2E 52 7B 60 39 56 67 2D 6A 23 43 4B 60 55 68 35 01 08 00 23 00 01 00 1F 00 17 02 5B
val
nickLength
=
readUByte
().
toInt
()
nickname
=
readString
(
nickLength
)
val
nickname
=
readString
(
nickLength
)
//后文
//00 05 00 04 00 00 00 01 01 15 00 10 49 83 5C D9 93 6C 8D FE 09 18 99 37 99 80 68 92
discardExact
(
4
)
//02 13 80 02
age
=
readShort
()
//00 05
val
age
=
readShort
()
//00 05
discardExact
(
4
)
//00 04 00 00
discardExact
(
2
)
//00 01
gender
=
if
(
readBoolean
())
Gender
.
FEMALE
else
Gender
.
MALE
val
gender
=
if
(
readBoolean
())
Gender
.
FEMALE
else
Gender
.
MALE
return
LoginResponse
.
Success
(
sessionResponseDecryptionKey
,
token38
,
token88
,
encryptionKey
,
nickname
,
age
,
gender
)
}
else
->
LoginResponse
.
Failed
(
when
(
size
)
{
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt
View file @
2b477b3b
...
...
@@ -57,7 +57,7 @@ object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey>(TouchKey
}
override
suspend
fun
ByteReadPacket
.
decode
(
id
:
PacketId
,
sequenceId
:
UShort
,
handler
:
BotNetworkHandler
<
*
>):
TouchResponse
=
TouchResponse
().
apply
{
when
(
val
id
=
readByte
().
toUByte
().
toInt
())
{
when
(
val
flag
=
readByte
().
toUByte
().
toInt
())
{
0
xFE
->
{
discardExact
(
94
)
serverIP
=
readIP
()
...
...
@@ -71,7 +71,7 @@ object TouchPacket : PacketFactory<TouchPacket.TouchResponse, TouchKey>(TouchKey
loginIP
=
readIP
()
}
else
->
throw
IllegalStateException
(
id
.
toByte
().
toUHexString
())
else
->
throw
IllegalStateException
(
flag
.
toByte
().
toUHexString
())
}
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
View file @
2b477b3b
...
...
@@ -19,11 +19,14 @@ internal fun getGTK(sKey: String): Int {
}
@Tested
fun
BytePacketBuilder
.
writeCRC32
()
=
writeCRC32
(
getRandomByteArray
(
16
))
@PublishedApi
internal
fun
BytePacketBuilder
.
writeCRC32
()
=
writeCRC32
(
getRandomByteArray
(
16
))
fun
BytePacketBuilder
.
writeCRC32
(
key
:
ByteArray
)
{
@PublishedApi
internal
fun
BytePacketBuilder
.
writeCRC32
(
key
:
ByteArray
)
{
writeFully
(
key
)
//key
writeInt
(
crc32
(
key
))
}
fun
md5
(
str
:
String
):
ByteArray
=
md5
(
str
.
toByteArray
())
\ No newline at end of file
@PublishedApi
internal
fun
md5
(
str
:
String
):
ByteArray
=
md5
(
str
.
toByteArray
())
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
View file @
2b477b3b
...
...
@@ -25,6 +25,9 @@ fun ExternalImage(
/**
* 外部图片. 图片数据还没有读取到内存.
*
* 在 JVM, 请查看 'ExternalImageJvm.kt'
*
* @see ExternalImage.sendTo 上传图片并以纯图片消息发送给联系人
* @See ExternalImage.upload 上传图片并得到 [Image] 消息
*/
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
View file @
2b477b3b
...
...
@@ -17,7 +17,7 @@ var DefaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger() }
*
* 不应该直接构造这个类的实例. 请使用 [DefaultLogger]
*/
expect
open
class
PlatformLogger
@JvmOverloads
internal
constructor
(
identity
:
String
?
=
null
)
:
MiraiLoggerPlatformBase
expect
open
class
PlatformLogger
@JvmOverloads
internal
constructor
(
identity
:
String
?
=
"Mirai"
)
:
MiraiLoggerPlatformBase
/**
...
...
@@ -32,7 +32,7 @@ interface MiraiLogger {
/**
* 顶层日志记录器
*/
companion
object
:
MiraiLogger
by
DefaultLogger
(
"
TOP Level
"
)
companion
object
:
MiraiLogger
by
DefaultLogger
(
"
Mirai
"
)
val
identity
:
String
?
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformLogger.kt
deleted
100644 → 0
View file @
745200f6
package
net.mamoe.mirai.utils
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt
View file @
2b477b3b
...
...
@@ -11,7 +11,7 @@ import net.mamoe.mirai.contact.QQ
/**
* 创建一个在当前 [CoroutineScope] 下执行的 [SuspendLazy]
*/
fun
<
R
>
CoroutineScope
.
SuspendLazy
(
initializer
:
suspend
()
->
R
):
SuspendLazy
<
R
>
=
SuspendLazy
(
this
,
initializer
)
fun
<
R
>
CoroutineScope
.
SuspendLazy
(
initializer
:
suspend
()
->
R
):
Lazy
<
Deferred
<
R
>
>
=
SuspendLazy
(
this
,
initializer
)
/**
* 挂起初始化的 [lazy], 是属性不能 `suspend` 的替代品.
...
...
@@ -26,7 +26,8 @@ fun <R> CoroutineScope.SuspendLazy(initializer: suspend () -> R): SuspendLazy<R>
*
* @sample QQ.profile
*/
class
SuspendLazy
<
R
>(
scope
:
CoroutineScope
,
val
initializer
:
suspend
()
->
R
)
:
Lazy
<
Deferred
<
R
>>
{
@PublishedApi
internal
class
SuspendLazy
<
R
>(
scope
:
CoroutineScope
,
val
initializer
:
suspend
()
->
R
)
:
Lazy
<
Deferred
<
R
>>
{
private
val
valueUpdater
:
Deferred
<
R
>
by
lazy
{
scope
.
async
{
initializer
()
}
}
@Suppress
(
"EXPERIMENTAL_API_USAGE"
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/TEA.kt
View file @
2b477b3b
...
...
@@ -25,9 +25,11 @@ class DecryptionFailedException : Exception()
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
encryptBy
(
key
:
ByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
encrypt
(
this
,
key
,
sourceLength
=
length
)
@PublishedApi
internal
fun
ByteArray
.
encryptBy
(
key
:
ByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
encrypt
(
this
,
key
,
sourceLength
=
length
)
fun
ByteArray
.
encryptBy
(
key
:
DecrypterByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
encrypt
(
this
,
key
.
value
,
sourceLength
=
length
)
@PublishedApi
internal
fun
ByteArray
.
encryptBy
(
key
:
DecrypterByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
encrypt
(
this
,
key
.
value
,
sourceLength
=
length
)
/**
* 通过 [String.hexToBytes] 将 [keyHex] 转为 [ByteArray] 后用它解密 [this].
...
...
@@ -36,7 +38,8 @@ fun ByteArray.encryptBy(key: DecrypterByteArray, length: Int = this.size): ByteA
* @param keyHex 长度至少为 16 bytes
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
encryptBy
(
keyHex
:
String
,
length
:
Int
=
this
.
size
):
ByteArray
=
encryptBy
(
keyHex
.
hexToBytes
(
withCache
=
true
),
length
=
length
)
@PublishedApi
internal
fun
ByteArray
.
encryptBy
(
keyHex
:
String
,
length
:
Int
=
this
.
size
):
ByteArray
=
encryptBy
(
keyHex
.
hexToBytes
(
withCache
=
true
),
length
=
length
)
/**
* 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密.
...
...
@@ -45,7 +48,8 @@ fun ByteArray.encryptBy(keyHex: String, length: Int = this.size): ByteArray = en
* @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程
* @throws DecryptionFailedException 解密错误时
*/
inline
fun
ByteReadPacket
.
encryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
remaining
.
toInt
()
-
offset
,
consumer
:
(
ByteArray
)
->
Unit
)
{
@PublishedApi
internal
inline
fun
ByteReadPacket
.
encryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
remaining
.
toInt
()
-
offset
,
consumer
:
(
ByteArray
)
->
Unit
)
{
ByteArrayPool
.
useInstance
{
this
.
readFully
(
it
,
offset
,
length
)
consumer
(
it
.
encryptBy
(
key
,
length
=
length
))
...
...
@@ -63,7 +67,9 @@ inline fun ByteReadPacket.encryptBy(key: ByteArray, offset: Int = 0, length: Int
* @param key 固定长度 16
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
decryptBy
(
key
:
ByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
decrypt
(
checkDataLengthAndReturnSelf
(
length
),
key
,
sourceLength
=
length
)
@PublishedApi
internal
fun
ByteArray
.
decryptBy
(
key
:
ByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
decrypt
(
checkDataLengthAndReturnSelf
(
length
),
key
,
sourceLength
=
length
)
/**
* 使用 [key] 解密 [this].
...
...
@@ -73,7 +79,8 @@ fun ByteArray.decryptBy(key: ByteArray, length: Int = this.size): ByteArray = TE
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
decryptBy
(
key
:
IoBuffer
,
length
:
Int
=
this
.
size
):
ByteArray
{
@PublishedApi
internal
fun
ByteArray
.
decryptBy
(
key
:
IoBuffer
,
length
:
Int
=
this
.
size
):
ByteArray
{
checkDataLengthAndReturnSelf
(
length
)
return
ByteArrayPool
.
useInstance
{
keyBuffer
->
key
.
readFully
(
keyBuffer
,
0
,
key
.
readRemaining
)
...
...
@@ -88,7 +95,8 @@ fun ByteArray.decryptBy(key: IoBuffer, length: Int = this.size): ByteArray {
* @param keyHex 长度至少为 16 bytes
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
decryptBy
(
keyHex
:
String
,
length
:
Int
=
this
.
size
):
ByteArray
=
decryptBy
(
keyHex
.
hexToBytes
(
withCache
=
true
),
length
=
length
)
@PublishedApi
internal
fun
ByteArray
.
decryptBy
(
keyHex
:
String
,
length
:
Int
=
this
.
size
):
ByteArray
=
decryptBy
(
keyHex
.
hexToBytes
(
withCache
=
true
),
length
=
length
)
/**
* 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 解密.
...
...
@@ -96,7 +104,8 @@ fun ByteArray.decryptBy(keyHex: String, length: Int = this.size): ByteArray = de
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
fun
IoBuffer
.
decryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
readRemaining
-
offset
):
ByteArray
{
@PublishedApi
internal
fun
IoBuffer
.
decryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
readRemaining
-
offset
):
ByteArray
{
return
ByteArrayPool
.
useInstance
{
this
.
readFully
(
it
,
offset
,
length
)
it
.
checkDataLengthAndReturnSelf
(
length
)
...
...
@@ -110,12 +119,45 @@ fun IoBuffer.decryptBy(key: ByteArray, offset: Int = 0, length: Int = readRemain
* @param keyHex 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
fun
IoBuffer
.
decryptBy
(
keyHex
:
String
,
offset
:
Int
=
0
,
length
:
Int
=
readRemaining
-
offset
):
ByteArray
=
@PublishedApi
internal
fun
IoBuffer
.
decryptBy
(
keyHex
:
String
,
offset
:
Int
=
0
,
length
:
Int
=
readRemaining
-
offset
):
ByteArray
=
decryptBy
(
keyHex
.
hexToBytes
(
withCache
=
true
),
offset
=
offset
,
length
=
length
)
// endregion
// region ByteReadPacket extension
@PublishedApi
internal
fun
ByteReadPacket
.
decryptBy
(
key
:
ByteArray
):
ByteReadPacket
=
decryptAsByteArray
(
key
)
{
data
->
ByteReadPacket
(
data
,
0
)
}
@PublishedApi
internal
fun
ByteReadPacket
.
decryptBy
(
key
:
IoBuffer
):
ByteReadPacket
=
decryptAsByteArray
(
key
)
{
data
->
ByteReadPacket
(
data
,
0
)
}
@PublishedApi
internal
fun
ByteReadPacket
.
decryptBy
(
keyHex
:
String
):
ByteReadPacket
=
decryptBy
(
keyHex
.
hexToBytes
())
@PublishedApi
internal
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
key
:
ByteArray
,
consumer
:
(
ByteArray
)
->
R
):
R
=
ByteArrayPool
.
useInstance
{
val
length
=
remaining
.
toInt
()
readFully
(
it
,
0
,
length
)
consumer
(
it
.
decryptBy
(
key
,
length
))
}.
also
{
close
()
}
@PublishedApi
internal
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
keyHex
:
String
,
consumer
:
(
ByteArray
)
->
R
):
R
=
this
.
decryptAsByteArray
(
keyHex
.
hexToBytes
(),
consumer
)
@PublishedApi
internal
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
key
:
IoBuffer
,
consumer
:
(
ByteArray
)
->
R
):
R
=
ByteArrayPool
.
useInstance
{
val
length
=
remaining
.
toInt
()
readFully
(
it
,
0
,
length
)
consumer
(
it
.
decryptBy
(
key
,
length
))
}.
also
{
close
()
}
// endregion
private
object
TEA
{
private
const
val
UINT32_MASK
=
0
xffffffffL
...
...
@@ -362,9 +404,4 @@ private object TEA {
private
fun
ByteArray
.
checkDataLengthAndReturnSelf
(
length
:
Int
=
this
.
size
):
ByteArray
{
require
(
length
%
8
==
0
&&
length
>=
16
)
{
"data must len % 8 == 0 && len >= 16 but given (length=$length) ${this.toUHexString()}"
}
return
this
}
fun
main
()
{
println
(
"A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
.
hexToBytes
().
encryptBy
(
"A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
).
decryptBy
(
"A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
).
toUHexString
()
==
"A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
)
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
View file @
2b477b3b
...
...
@@ -23,9 +23,9 @@ expect class ClosedChannelException : IOException
/**
* 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误.
*/
class
SendPacketInternalException
(
cause
:
Throwable
?)
:
Exception
(
cause
)
internal
class
SendPacketInternalException
(
cause
:
Throwable
?)
:
Exception
(
cause
)
/**
* 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误.
*/
class
ReadPacketInternalException
(
cause
:
Throwable
?)
:
Exception
(
cause
)
\ No newline at end of file
internal
class
ReadPacketInternalException
(
cause
:
Throwable
?)
:
Exception
(
cause
)
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt
View file @
2b477b3b
...
...
@@ -92,7 +92,8 @@ fun String.hexToUBytes(withCache: Boolean = true): UByteArray =
/**
* 生成长度为 [length], 元素为随机 `0..255` 的 [ByteArray]
*/
fun
getRandomByteArray
(
length
:
Int
):
ByteArray
=
ByteArray
(
length
)
{
Random
.
nextInt
(
0
..
255
).
toByte
()
}
@PublishedApi
internal
fun
getRandomByteArray
(
length
:
Int
):
ByteArray
=
ByteArray
(
length
)
{
Random
.
nextInt
(
0
..
255
).
toByte
()
}
/**
* 随机生成长度为 [length] 的 [String].
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/SubscribersJvm.kt
View file @
2b477b3b
...
...
@@ -9,6 +9,6 @@ import kotlinx.coroutines.runBlocking
*/
object
Events
{
@JvmStatic
fun
<
E
:
Event
>
subscribe
(
type
:
Class
<
E
>,
handler
:
suspend
(
E
)
->
ListeningStatus
)
=
fun
<
E
:
Subscribable
>
subscribe
(
type
:
Class
<
E
>,
handler
:
suspend
(
E
)
->
ListeningStatus
)
=
runBlocking
{
type
.
kotlin
.
subscribe
(
handler
)
}
}
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/NetworkDispatcherJvm.kt
View file @
2b477b3b
...
...
@@ -7,5 +7,4 @@ import java.util.concurrent.Executors
/**
* 独立的 4 thread 调度器
*/
actual
val
NetworkDispatcher
:
CoroutineDispatcher
get
()
=
Executors
.
newFixedThreadPool
(
4
).
asCoroutineDispatcher
()
\ No newline at end of file
internal
actual
val
NetworkDispatcher
:
CoroutineDispatcher
=
Executors
.
newFixedThreadPool
(
4
).
asCoroutineDispatcher
()
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
View file @
2b477b3b
package
net.mamoe.mirai.utils
import
io.ktor.util.KtorExperimentalAPI
import
io.ktor.util.cio.writeChannel
import
kotlinx.coroutines.CoroutineName
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.io.ByteWriteChannel
import
kotlinx.coroutines.io.jvm.nio.copyTo
import
kotlinx.coroutines.io.reader
import
kotlinx.coroutines.sync.Mutex
import
kotlinx.coroutines.sync.withLock
import
kotlinx.coroutines.withContext
import
kotlinx.io.core.use
import
java.awt.Image
import
java.awt.image.BufferedImage
import
java.io.File
import
java.io.RandomAccessFile
import
javax.imageio.ImageIO
import
kotlin.coroutines.CoroutineContext
/**
* 平台默认的验证码识别器.
*
* 可被修改, 除覆盖配置外全局生效.
*/
@KtorExperimentalAPI
actual
var
DefaultCaptchaSolver
:
CaptchaSolver
=
{
captchaLock
.
withLock
{
val
tempFile
:
File
=
createTempFile
(
suffix
=
".png"
).
apply
{
deleteOnExit
()
}
...
...
@@ -38,6 +43,16 @@ actual var DefaultCaptchaSolver: CaptchaSolver = {
}
}
// Copied from Ktor CIO
private
fun
File
.
writeChannel
(
coroutineContext
:
CoroutineContext
=
Dispatchers
.
IO
):
ByteWriteChannel
=
GlobalScope
.
reader
(
CoroutineName
(
"file-writer"
)
+
coroutineContext
,
autoFlush
=
true
)
{
RandomAccessFile
(
this
@
writeChannel
,
"rw"
).
use
{
file
->
val
copied
=
channel
.
copyTo
(
file
.
channel
)
file
.
setLength
(
copied
)
// truncate tail that could remain from the previously written data
}
}.
channel
private
val
captchaLock
=
Mutex
()
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
View file @
2b477b3b
package
net.mamoe.mirai.utils
import
java.io.ByteArrayOutputStream
import
java.io.PrintStream
import
java.text.SimpleDateFormat
import
java.util.*
...
...
@@ -11,42 +9,39 @@ actual typealias PlatformLogger = Console
* JVM 控制台日志实现
*/
open
class
Console
@JvmOverloads
internal
constructor
(
override
val
identity
:
String
?
=
null
override
val
identity
:
String
?
=
"Mirai"
)
:
MiraiLoggerPlatformBase
()
{
override
fun
verbose0
(
any
:
Any
?)
=
println
(
any
.
toString
()
,
LoggerTextFormat
.
RESET
)
override
fun
verbose0
(
any
:
Any
?)
=
println
(
any
,
LoggerTextFormat
.
RESET
)
override
fun
verbose0
(
message
:
String
?,
e
:
Throwable
?)
{
verbose
(
message
.
toString
())
if
(
message
!=
null
)
verbose
(
message
.
toString
())
e
?.
printStackTrace
()
}
override
fun
info0
(
any
:
Any
?)
=
println
(
any
.
toString
()
,
LoggerTextFormat
.
LIGHT_GREEN
)
override
fun
info0
(
any
:
Any
?)
=
println
(
any
,
LoggerTextFormat
.
LIGHT_GREEN
)
override
fun
info0
(
message
:
String
?,
e
:
Throwable
?)
{
info
(
message
.
toString
())
i
f
(
message
!=
null
)
i
nfo
(
message
.
toString
())
e
?.
printStackTrace
()
}
override
fun
warning0
(
any
:
Any
?)
=
println
(
any
.
toString
()
,
LoggerTextFormat
.
LIGHT_RED
)
override
fun
warning0
(
any
:
Any
?)
=
println
(
any
,
LoggerTextFormat
.
LIGHT_RED
)
override
fun
warning0
(
message
:
String
?,
e
:
Throwable
?)
{
warning
(
message
.
toString
())
if
(
message
!=
null
)
warning
(
message
.
toString
())
e
?.
printStackTrace
()
}
override
fun
error0
(
any
:
Any
?)
=
println
(
any
.
toString
()
,
LoggerTextFormat
.
RED
)
override
fun
error0
(
any
:
Any
?)
=
println
(
any
,
LoggerTextFormat
.
RED
)
override
fun
error0
(
message
:
String
?,
e
:
Throwable
?)
{
error
(
message
.
toString
())
if
(
message
!=
null
)
error
(
message
.
toString
())
e
?.
printStackTrace
()
}
override
fun
debug0
(
any
:
Any
?)
{
println
(
any
.
toString
(),
LoggerTextFormat
.
LIGHT_CYAN
)
}
override
fun
debug0
(
any
:
Any
?)
=
println
(
any
,
LoggerTextFormat
.
LIGHT_CYAN
)
override
fun
debug0
(
message
:
String
?,
e
:
Throwable
?)
{
debug
(
message
.
toString
())
if
(
message
!=
null
)
debug
(
message
.
toString
())
e
?.
printStackTrace
()
}
private
fun
println
(
value
:
String
?,
color
:
LoggerTextFormat
)
{
private
fun
println
(
value
:
Any
?,
color
:
LoggerTextFormat
)
{
val
time
=
SimpleDateFormat
(
"HH:mm:ss"
,
Locale
.
SIMPLIFIED_CHINESE
).
format
(
Date
())
if
(
identity
==
null
)
{
...
...
@@ -81,8 +76,4 @@ internal enum class LoggerTextFormat(private val format: String) {
;
override
fun
toString
():
String
=
format
}
@Suppress
(
"unused"
)
val
Throwable
.
stacktraceString
:
String
get
()
=
ByteArrayOutputStream
().
also
{
printStackTrace
(
PrintStream
(
it
))
}.
toString
()
\ No newline at end of file
}
\ No newline at end of file
mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
View file @
2b477b3b
...
...
@@ -8,10 +8,11 @@ import net.mamoe.mirai.Bot
import
net.mamoe.mirai.BotAccount
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.event.*
import
net.mamoe.mirai.event.events.FriendMessageEvent
import
net.mamoe.mirai.login
import
net.mamoe.mirai.message.*
import
net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
import
net.mamoe.mirai.network.protocol.tim.packet.event.FriendMessage
import
net.mamoe.mirai.network.protocol.tim.packet.event.GroupMessage
import
net.mamoe.mirai.network.protocol.tim.packet.login.ifFail
import
net.mamoe.mirai.utils.suspendToExternalImage
import
java.io.File
...
...
@@ -52,7 +53,7 @@ suspend fun main() {
directlySubscribe
(
bot
)
//DSL 监听
subscribeAll
<
FriendMessage
Event
>
{
subscribeAll
<
FriendMessage
>
{
always
{
//获取第一个纯文本消息
val
firstText
=
it
.
message
.
firstOrNull
<
PlainText
>()
...
...
@@ -102,7 +103,7 @@ suspend fun Bot.messageDSL() {
// sender: QQ
// it: String (MessageChain.toString)
if
(
this
is
Group
SenderAnd
Message
)
{
if
(
this
is
GroupMessage
)
{
//如果是群消息
// group: Group
this
.
group
.
sendMessage
(
"你在一个群里"
)
...
...
@@ -196,7 +197,7 @@ suspend fun directlySubscribe(bot: Bot) {
// 手动处理消息
// 使用 Bot 的扩展方法监听, 将在处理事件时得到一个 this: Bot.
// 这样可以调用 Bot 内的一些扩展方法如 UInt.qq():QQ
bot
.
subscribeAlways
<
FriendMessage
Event
>
{
bot
.
subscribeAlways
<
FriendMessage
>
{
// this: Bot
// it: FriendMessageEvent
...
...
@@ -257,9 +258,9 @@ suspend fun directlySubscribe(bot: Bot) {
* 对机器人说 "停止", 机器人停止
*/
suspend
fun
demo2
()
{
subscribeAlways
<
FriendMessage
Event
>
{
event
->
subscribeAlways
<
FriendMessage
>
{
event
->
if
(
event
.
message
eq
"记笔记"
)
{
subscribeUntilFalse
<
FriendMessage
Event
>
{
subscribeUntilFalse
<
FriendMessage
>
{
it
.
reply
(
"你发送了 ${it.message}"
)
it
.
message
eq
"停止"
...
...
mirai-demos/mirai-demo-gentleman/build.gradle
View file @
2b477b3b
...
...
@@ -4,6 +4,7 @@ apply plugin: "java"
dependencies
{
api
project
(
":mirai-core"
)
runtime
files
(
"../../mirai-core/build/classes/kotlin/jvm/main"
)
// mpp targeting android limitation
//runtime files("../../mirai-core/build/classes/atomicfu/jvm/main") // mpp targeting android limitation
api
group:
'org.jetbrains.kotlin'
,
name:
'kotlin-stdlib-jdk8'
,
version:
kotlin_version
api
group:
'org.jetbrains.kotlinx'
,
name:
'kotlinx-coroutines-core'
,
version:
coroutines_version
...
...
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
View file @
2b477b3b
...
...
@@ -7,7 +7,7 @@ import kotlinx.coroutines.delay
import
kotlinx.coroutines.launch
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.BotAccount
import
net.mamoe.mirai.event.
Event
import
net.mamoe.mirai.event.
Subscribable
import
net.mamoe.mirai.event.subscribeAlways
import
net.mamoe.mirai.event.subscribeMessages
import
net.mamoe.mirai.login
...
...
@@ -41,7 +41,7 @@ suspend fun main() {
/**
* 监听所有事件
*/
subscribeAlways
<
Event
>
{
subscribeAlways
<
Subscribable
>
{
//bot.logger.verbose("收到了一个事件: ${it::class.simpleName}")
}
bot
.
subscribeMessages
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment