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
06205cb6
Commit
06205cb6
authored
May 10, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve Bot life cycle management, close #317
parent
aa2805b8
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
101 additions
and
53 deletions
+101
-53
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
...moe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
+3
-15
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt
...otocol/packet/chat/receive/MessageSvc.PushForceOffline.kt
+1
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
.../mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
+1
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+19
-14
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
+9
-5
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
...mmonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+8
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/javaFriendly.kt
...ore/src/commonMain/kotlin/net.mamoe.mirai/javaFriendly.kt
+4
-1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
...core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
+4
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
...Main/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
+4
-8
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/LoginFailedException.kt
...in/kotlin/net.mamoe.mirai/network/LoginFailedException.kt
+1
-1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
...mmonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
+47
-3
No files found.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
View file @
06205cb6
...
...
@@ -404,27 +404,15 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
logger
.
info
{
"Syncing friend message history: Success"
}
}
private
suspend
fun
doHeartBeat
():
Exception
?
{
val
lastException
:
Exception
?
try
{
kotlin
.
runCatching
{
Heartbeat
.
Alive
(
bot
.
client
)
.
sendAndExpect
<
Heartbeat
.
Alive
.
Response
>(
timeoutMillis
=
bot
.
configuration
.
heartbeatTimeoutMillis
,
retry
=
2
)
return
null
}
private
suspend
fun
doHeartBeat
():
Throwable
?
{
return
retryCatching
(
2
)
{
Heartbeat
.
Alive
(
bot
.
client
)
.
sendAndExpect
<
Heartbeat
.
Alive
.
Response
>(
timeoutMillis
=
bot
.
configuration
.
heartbeatTimeoutMillis
,
retry
=
2
)
return
null
}
catch
(
e
:
Exception
)
{
lastException
=
e
}
return
lastException
}.
exceptionOrNull
()
}
/**
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt
View file @
06205cb6
...
...
@@ -24,6 +24,7 @@ internal object MessageSvcPushForceOffline :
OutgoingPacketFactory
<
BotOfflineEvent
.
Force
>(
"MessageSvc.PushForceOffline"
)
{
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
):
BotOfflineEvent
.
Force
{
val
struct
=
this
.
readUniPacket
(
RequestPushForceOffline
.
serializer
())
@Suppress
(
"INVISIBLE_MEMBER"
)
return
BotOfflineEvent
.
Force
(
bot
,
title
=
struct
.
title
?:
""
,
message
=
struct
.
tips
?:
""
)
}
}
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
View file @
06205cb6
...
...
@@ -189,6 +189,7 @@ internal class StatSvc {
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
BotOfflineEvent
.
Dropped
{
val
decodeUniPacket
=
readUniPacket
(
RequestMSFForceOffline
.
serializer
())
@Suppress
(
"INVISIBLE_MEMBER"
)
return
BotOfflineEvent
.
Dropped
(
bot
,
MsfOfflineToken
(
decodeUniPacket
.
uin
,
decodeUniPacket
.
iSeqno
,
0
))
}
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
View file @
06205cb6
...
...
@@ -7,15 +7,14 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"unused"
,
"FunctionName"
,
"NOTHING_TO_INLINE"
,
"UnusedImport"
,
"EXPERIMENTAL_OVERRIDE"
)
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"unused"
,
"FunctionName"
,
"NOTHING_TO_INLINE"
,
"UnusedImport"
,
"EXPERIMENTAL_OVERRIDE"
)
package
net.mamoe.mirai
import
kotlinx.coroutines.CoroutineName
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.*
import
net.mamoe.mirai.contact.*
import
net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent
import
net.mamoe.mirai.event.events.MemberJoinRequestEvent
...
...
@@ -35,13 +34,12 @@ import kotlin.jvm.JvmSynthetic
*/
@JvmSynthetic
suspend
inline
fun
<
B
:
Bot
>
B
.
alsoLogin
():
B
=
also
{
login
()
}
// 任何人都能看到这个方法
/**
* 机器人对象. 一个机器人实例登录一个 QQ 账号.
* Mirai 为多账号设计, 可同时维护多个机器人.
*
*
注: Bot 为全协程实现, 没有其他任务时若不使用 [join], 主线程将会退出.
*
有关 [Bot] 生命管理, 请查看 [BotConfiguration.inheritCoroutineContext]
*
* @see Contact 联系人
* @see kotlinx.coroutines.isActive 判断 [Bot] 是否正常运行中. (在线, 且没有被 [close])
...
...
@@ -278,6 +276,13 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
abstract
val
network
:
BotNetworkHandler
}
/**
* 获取 [Job] 的协程 [Job]. 此 [Job] 为一个 [SupervisorJob]
*/
@
get
:
JvmSynthetic
inline
val
Bot
.
supervisorJob
:
CompletableJob
get
()
=
this
.
coroutineContext
[
Job
]
as
CompletableJob
/**
* 挂起协程直到 [Bot] 下线.
*/
...
...
@@ -305,13 +310,13 @@ suspend inline fun Bot.recall(message: MessageChain) =
* @see recall
*/
@JvmSynthetic
inline
fun
Bot
.
recallIn
(
inline
fun
CoroutineScope
.
recallIn
(
source
:
MessageSource
,
millis
:
Long
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
):
Job
=
this
.
launch
(
coroutineContext
+
CoroutineName
(
"MessageRecall"
))
{
kotlinx
.
coroutines
.
delay
(
millis
)
recall
(
source
)
delay
(
millis
)
source
.
recall
(
)
}
/**
...
...
@@ -322,13 +327,13 @@ inline fun Bot.recallIn(
* @see recall
*/
@JvmSynthetic
inline
fun
Bot
.
recallIn
(
inline
fun
CoroutineScope
.
recallIn
(
message
:
MessageChain
,
millis
:
Long
,
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
):
Job
=
this
.
launch
(
coroutineContext
+
CoroutineName
(
"MessageRecall"
))
{
kotlinx
.
coroutines
.
delay
(
millis
)
recall
(
message
)
delay
(
millis
)
message
.
recall
(
)
}
/**
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
View file @
06205cb6
...
...
@@ -91,11 +91,15 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
@Suppress
(
"unused"
)
private
val
offlineListener
:
Listener
<
BotOfflineEvent
>
=
this
@BotImpl
.
subscribeAlways
(
concurrency
=
Listener
.
ConcurrencyKind
.
LOCKED
)
{
event
->
if
(
event
.
bot
!=
this
.
bot
)
{
if
(
event
.
bot
!=
this
@BotImpl
)
{
return
@
subscribeAlways
}
if
(!
::
_network
.
isInitialized
)
{
// bot 还未登录就被 close
return
@
subscribeAlways
}
if
(
network
.
areYouOk
()
&&
event
!
is
BotOfflineEvent
.
Force
)
{
//
avoid concurrent re-login tasks
//
network 运行正常
return
@
subscribeAlways
}
when
(
event
)
{
...
...
@@ -262,14 +266,14 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
this
.
launch
{
BotOfflineEvent
.
Active
(
this
@BotImpl
,
cause
).
broadcast
()
}
logger
.
info
{
"Bot cancelled"
+
cause
?.
message
?.
let
{
": $it"
}.
orEmpty
()
}
if
(
cause
==
null
)
{
this
.
cancel
()
supervisorJob
.
cancel
()
}
else
{
this
.
cancel
(
CancellationException
(
"bot cancell
ed"
,
cause
))
supervisorJob
.
cancel
(
CancellationException
(
"Bot clos
ed"
,
cause
))
}
}
}
@RequiresOptIn
(
level
=
RequiresOptIn
.
Level
.
ERROR
)
internal
annotation
class
ThisApiMustBeUsedInWithConnectionLockBlock
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
View file @
06205cb6
...
...
@@ -51,7 +51,7 @@ class EventCancelledException : RuntimeException {
/**
* [Bot] 登录完成, 好友列表, 群组列表初始化完成
*/
data class
BotOnlineEvent
(
override
val
bot
:
Bot
)
:
BotActiveEvent
,
AbstractEvent
()
data class
BotOnlineEvent
internal
constructor
(
override
val
bot
:
Bot
)
:
BotActiveEvent
,
AbstractEvent
()
/**
* [Bot] 离线.
...
...
@@ -59,31 +59,33 @@ data class BotOnlineEvent(override val bot: Bot) : BotActiveEvent, AbstractEvent
sealed
class
BotOfflineEvent
:
BotEvent
,
AbstractEvent
()
{
/**
* 主动离线
* 主动离线
. 主动广播这个事件也可以让 [Bot] 关闭.
*/
data class
Active
(
override
val
bot
:
Bot
,
val
cause
:
Throwable
?)
:
BotOfflineEvent
(),
BotActiveEvent
/**
* 被挤下线
*/
data class
Force
(
override
val
bot
:
Bot
,
val
title
:
String
,
val
message
:
String
)
:
BotOfflineEvent
(),
Packet
,
data class
Force
internal
constructor
(
override
val
bot
:
Bot
,
val
title
:
String
,
val
message
:
String
)
:
BotOfflineEvent
(),
Packet
,
BotPassiveEvent
/**
* 被服务器断开或因网络问题而掉线
*/
data class
Dropped
(
override
val
bot
:
Bot
,
val
cause
:
Throwable
?)
:
BotOfflineEvent
(),
Packet
,
BotPassiveEvent
data class
Dropped
internal
constructor
(
override
val
bot
:
Bot
,
val
cause
:
Throwable
?)
:
BotOfflineEvent
(),
Packet
,
BotPassiveEvent
/**
* 服务器主动要求更换另一个服务器
*/
data class
RequireReconnect
(
override
val
bot
:
Bot
)
:
BotOfflineEvent
(),
Packet
,
BotPassiveEvent
data class
RequireReconnect
internal
constructor
(
override
val
bot
:
Bot
)
:
BotOfflineEvent
(),
Packet
,
BotPassiveEvent
}
/**
* [Bot] 主动或被动重新登录.
*/
data class
BotReloginEvent
(
data class
BotReloginEvent
internal
constructor
(
override
val
bot
:
Bot
,
val
cause
:
Throwable
?
)
:
BotEvent
,
BotActiveEvent
,
AbstractEvent
()
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/javaFriendly.kt
View file @
06205cb6
...
...
@@ -13,15 +13,18 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
/**
* 表明这个 API 是为了让 Java 使用者调用更方便.
*
* 一般有一定的性能损失, 且不能在 JVM/Android 以外平台使用. 不要在 Kotlin 调用它.
*/
@MiraiInternalAPI
@RequiresOptIn
(
level
=
RequiresOptIn
.
Level
.
ERROR
)
@Target
(
AnnotationTarget
.
PROPERTY
,
AnnotationTarget
.
FUNCTION
,
AnnotationTarget
.
TYPE
,
AnnotationTarget
.
CLASS
)
annotation
class
JavaFriendlyAPI
internal
annotation
class
JavaFriendlyAPI
/**
* [Bot] 中为了让 Java 使用者调用更方便的 API 列表.
*
* **注意**: 不应该把这个类作为一个类型, 只应使用其中的方法
*/
@MiraiInternalAPI
@Suppress
(
"FunctionName"
,
"INAPPLICABLE_JVM_NAME"
,
"unused"
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
View file @
06205cb6
...
...
@@ -30,6 +30,10 @@ annotation class LowLevelAPI
/**
* [Bot] 相关协议层低级 API.
*
* **注意**: 不应该把这个类作为一个类型, 只应使用其中的方法
*
* **警告**: 所有的低级 API 都可能在任意时刻不经过任何警告和迭代就被修改. 因此非常不建议在任何情况下使用这些 API.
*/
@MiraiExperimentalAPI
@Suppress
(
"FunctionName"
,
"unused"
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
View file @
06205cb6
...
...
@@ -331,19 +331,15 @@ inline fun MessageSource.isAboutFriend(): Boolean {
* 引用这条消息
* @see QuoteReply
*/
fun
MessageSource
.
quote
():
QuoteReply
{
return
QuoteReply
(
this
)
}
@JvmSynthetic
inline
fun
MessageSource
.
quote
():
QuoteReply
=
QuoteReply
(
this
)
/**
* 引用这条消息. 仅从服务器接收的消息 (即来自 [MessageEvent]) 才可以通过这个方式被引用.
* @see QuoteReply
*/
fun
MessageChain
.
quote
():
QuoteReply
{
return
QuoteReply
(
this
.
source
)
}
@JvmSynthetic
inline
fun
MessageChain
.
quote
():
QuoteReply
=
QuoteReply
(
this
.
source
)
/**
* 撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/LoginFailedException.kt
View file @
06205cb6
...
...
@@ -27,7 +27,7 @@ sealed class LoginFailedException constructor(
)
:
RuntimeException
(
message
,
cause
)
/**
* 密码输入错误
* 密码输入错误
(有时候也会是其他错误, 如 `"当前上网环境异常,请更换网络环境或在常用设备上登录或稍后再试。"`)
*/
class
WrongPasswordException
(
message
:
String
?)
:
LoginFailedException
(
true
,
message
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
View file @
06205cb6
...
...
@@ -11,6 +11,7 @@
package
net.mamoe.mirai.utils
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.SupervisorJob
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.network.BotNetworkHandler
import
kotlin.coroutines.CoroutineContext
...
...
@@ -34,7 +35,7 @@ open class BotConfiguration {
/** 设备信息覆盖. 默认使用随机的设备信息. */
var
deviceInfo
:
((
Context
)
->
DeviceInfo
)?
=
null
/** 父 [CoroutineContext]. [Bot] 创建后会覆盖其 [Job], 但会将这个 [Job] 作为父 [Job] */
/** 父 [CoroutineContext]. [Bot] 创建后会
使用 [SupervisorJob]
覆盖其 [Job], 但会将这个 [Job] 作为父 [Job] */
var
parentCoroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
/** 心跳周期. 过长会导致被服务器断开连接. */
...
...
@@ -115,21 +116,64 @@ open class BotConfiguration {
}
/**
* 使用当前协程的 [coroutineContext] 作为 [parentCoroutineContext]
* 使用当前协程的 [coroutineContext] 作为 [parentCoroutineContext].
*
* Bot 将会使用一个 [SupervisorJob] 覆盖 [coroutineContext] 当前协程的 [Job], 并使用当前协程的 [Job] 作为父 [Job]
*
* 用例:
* ```
* coroutineScope {
* val bot = Bot(...)
* val bot = Bot(...) {
* inheritCoroutineContext()
* }
* bot.login()
* } // coroutineScope 会等待 Bot 退出
* ```
*
*
* **注意**: `bot.cancel` 时将会让父 [Job] 也被 cancel.
* ```
* coroutineScope { // this: CoroutineScope
* launch {
* while(isActive) {
* delay(500)
* println("I'm alive")
* }
* }
*
* val bot = Bot(...) {
* inheritCoroutineContext() // 使用 `coroutineScope` 的 Job 作为父 Job
* }
* bot.login()
* bot.cancel() // 取消了整个 `coroutineScope`, 因此上文不断打印 `"I'm alive"` 的协程也会被取消.
* }
* ```
*
* 因此, 此函数尤为适合在 `suspend fun main()` 中使用, 它能阻止主线程退出:
* ```
* suspend fun main() {
* val bot = Bot() {
* inheritCoroutineContext()
* }
* bot.subscribe { ... }
*
* // 主线程不会退出, 直到 Bot 离线.
* }
* ```
*
* 简言之,
* - 若想让 [Bot] 作为 '守护进程' 运行, 则无需调用 [inheritCoroutineContext].
* - 若想让 [Bot] 依赖于当前协程, 让当前协程等待 [Bot] 运行, 则使用 [inheritCoroutineContext]
*
* @see parentCoroutineContext
*/
@ConfigurationDsl
suspend
inline
fun
inheritCoroutineContext
()
{
parentCoroutineContext
=
coroutineContext
}
/** 标注一个配置 DSL 函数 */
@Target
(
AnnotationTarget
.
FUNCTION
)
@DslMarker
annotation
class
ConfigurationDsl
...
...
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