diff --git a/build.gradle b/build.gradle
index ce4dfd3b8..a9f99a31b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,12 +2,12 @@ buildscript {
     ext.kotlin_version = '1.3.50'
 
     repositories {
-        google()
+        mavenLocal()
+        jcenter()
+        mavenCentral()
         maven { url "https://mirrors.huaweicloud.com/repository/maven/" }
     }
 
-    apply from: rootProject.file('dependencies.gradle')
-
     dependencies {
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version"
@@ -16,14 +16,12 @@ buildscript {
 
 allprojects {
     group = "net.mamoe"
-    version = "1.0"
+    version = getProperty("mirai_version")
 
     repositories {
-        jcenter()//klock
-        google()
+        mavenLocal()
+        mavenCentral()
+        jcenter()
         maven { url "https://mirrors.huaweicloud.com/repository/maven/" }
     }
-
-    apply from: rootProject.file('dependencies.gradle')
-}
-
+}
\ No newline at end of file
diff --git a/dependencies.gradle b/dependencies.gradle
deleted file mode 100644
index fbe4285a8..000000000
--- a/dependencies.gradle
+++ /dev/null
@@ -1,44 +0,0 @@
-ext {
-    // dependencies
-
-    // kotlin
-    kotlinJvm = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
-    kotlinCommon = "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
-    kotlinNative = "org.jetbrains.kotlin:kotlin-stdlib-native:$kotlin_version"
-
-    // kotlinx.coroutine
-    coroutine_version = "1.3.0"
-    coroutine = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
-    coroutineCommon = "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutine_version"
-    coroutineNative = "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$coroutine_version"
-    coroutineAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
-    coroutineJs = "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutine_version"
-
-    coroutineIo = "org.jetbrains.kotlinx:kotlinx-coroutines-io:0.24.0"
-
-    // kotlin.reflect
-    reflect = "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
-
-    // kotlinx.atomicfu
-    atomicfu_version = "0.13.1"
-    atomicFUCommon = "org.jetbrains.kotlinx:atomicfu-common:$atomicfu_version"
-
-    // kotlinx.io
-    kotlinx_io_version = "0.1.15"
-    kotlinxIOJvm = "org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version"
-    kotlinxIOCommon = "org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version"
-    kotlinxIOJS = "org.jetbrains.kotlinx:kotlinx-io-js:$kotlinx_io_version"
-    kotlinxIONative = "org.jetbrains.kotlinx:kotlinx-io-native:$kotlinx_io_version"
-
-    // klock
-    klock = "com.soywiz.korlibs.klock:klock:1.7.0"
-
-    // ktor
-    ktor_version = "1.2.4"
-    ktorClientCore = "io.ktor:ktor-client-core:$ktor_version"
-    ktorClientCoreNative = "io.ktor:ktor-client-core-native:$ktor_version"
-    ktorClientCoreJvm = "io.ktor:ktor-client-core-jvm:$ktor_version"
-    ktorClientCio = "io.ktor:ktor-client-cio:$ktor_version"
-    ktorHttp = "io.ktor:ktor-http:$ktor_version"
-    ktorHttpCio = "io.ktor:ktor-http-cio:$ktor_version"
-}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 000000000..a6068cb82
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+# style guide
+kotlin.code.style=official
+# config
+mirai_version=1.0.0
+kotlin.incremental.multiplatform=true
+kotlin.parallel.tasks.in.project=true
+# kotlin
+kotlin_version=1.3.50
+# kotlin libraries
+serialization_version=0.13.0
+coroutines_version=1.3.2
+atomicfu_version=0.13.0
+ktor_version=1.2.4
+ktorio_version=1.3.0-beta-1
+klock_version=1.7.0
+kotlinxio_version=0.1.15
+coroutinesio_version=0.24.0
+# utility
diff --git a/mirai-console/build.gradle b/mirai-console/build.gradle
index c5a2254b4..19daa8a75 100644
--- a/mirai-console/build.gradle
+++ b/mirai-console/build.gradle
@@ -1,18 +1,6 @@
 apply plugin: "kotlin"
-apply plugin: "application"
 apply plugin: "java"
 
 dependencies {
-    compile project(':mirai-core')
-
-    compile rootProject.ext.kotlinCommon
-    compile rootProject.ext.kotlinJvm
-    compile rootProject.ext.reflect
-    compile rootProject.ext.coroutine
-}
-
-sourceCompatibility = "11"
-
-tasks.withType(JavaCompile) {
-    options.encoding = "UTF-8"
+    implementation project(':mirai-core')
 }
diff --git a/mirai-core/build.gradle b/mirai-core/build.gradle
index 8c5b56260..6e3d69b36 100644
--- a/mirai-core/build.gradle
+++ b/mirai-core/build.gradle
@@ -23,24 +23,21 @@ kotlin {
     sourceSets {
         commonMain {
             dependencies {
-                // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-reflect
-                implementation rootProject.ext.kotlinCommon
+                api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-common', version: kotlin_version
+                api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
+                api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core-common', version: coroutines_version
+                api group: 'org.jetbrains.kotlinx', name: 'atomicfu-common', version: atomicfu_version
+                api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io', version: kotlinxio_version
+                api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-io', version: coroutinesio_version
 
-                implementation rootProject.ext.reflect
+                implementation "com.soywiz.korlibs.klock:klock:$klock_version"
 
-                //implementation rootProject.ext.coroutine
-                implementation rootProject.ext.coroutineCommon
-                implementation rootProject.ext.coroutineIo
-
-                implementation rootProject.ext.atomicFUCommon
-
-                implementation rootProject.ext.kotlinxIOCommon
-                implementation rootProject.ext.klock
-
-                implementation rootProject.ext.ktorClientCore
-                implementation rootProject.ext.ktorClientCio
-                implementation rootProject.ext.ktorHttp
-                implementation rootProject.ext.ktorHttpCio
+                api group: 'io.ktor', name: 'ktor-client-core', version: ktor_version
+                //api group: 'io.ktor', name: 'ktor-client-cio', version: ktor_version
+                //api group: 'io.ktor', name: 'ktor-client', version: ktor_version
+                api group: 'io.ktor', name: 'ktor-http', version: ktor_version
+                //api group: 'io.ktor', name: 'ktor-utils', version: ktor_version
+                //api group: 'io.ktor', name: 'ktor-io', version: ktorio_version
             }
         }
 
@@ -48,22 +45,23 @@ kotlin {
             apply plugin: 'java'
 
             dependencies {
-                implementation rootProject.ext.kotlinJvm
-                implementation rootProject.ext.reflect
-                implementation rootProject.ext.coroutine
+                api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_version
+                api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
+                api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
+                api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutines_version
+                api group: 'org.jetbrains.kotlinx', name: 'atomicfu', version: atomicfu_version
+                api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io', version: kotlinxio_version
+                // api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io-jvm', version: kotlinxio_version
+                api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-io', version: coroutinesio_version
 
-                implementation rootProject.ext.kotlinxIOJvm
+                api group: 'io.ktor', name: 'ktor-http-cio', version: ktor_version
+                api group: 'io.ktor', name: 'ktor-http', version: ktor_version
+                api group: 'io.ktor', name: 'ktor-client-core-jvm', version: ktor_version
+                api group: 'io.ktor', name: 'ktor-client-cio', version: ktor_version
 
                 implementation 'org.yaml:snakeyaml:1.18'
                 implementation 'org.jsoup:jsoup:1.12.1'
                 implementation 'org.ini4j:ini4j:0.5.2'
-                implementation rootProject.ext.klock
-
-                implementation rootProject.ext.ktorClientCore
-                implementation rootProject.ext.ktorClientCoreJvm
-                implementation rootProject.ext.ktorClientCio
-                implementation rootProject.ext.ktorHttp
-                implementation rootProject.ext.ktorHttpCio
             }
         }
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
index 89efdd9f9..a83aac3dc 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
@@ -110,7 +110,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
         }
     }
 
-    fun close() {
+    suspend fun close() {
         this.network.close()
         this.contacts.groups.clear()
         this.contacts.qqs.clear()
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt
index 6a43300c1..266d3eaf4 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt
@@ -4,13 +4,14 @@ package net.mamoe.mirai.event
 
 import kotlinx.coroutines.CoroutineExceptionHandler
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.newCoroutineContext
 import kotlinx.coroutines.withContext
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.event.internal.broadcastInternal
 import net.mamoe.mirai.network.BotNetworkHandler
 import net.mamoe.mirai.utils.log
 import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.jvm.JvmOverloads
 
 /**
@@ -57,17 +58,8 @@ interface Cancellable {
  */
 @Suppress("UNCHECKED_CAST")
 @JvmOverloads
-suspend fun <E : Event> E.broadcast(context: CoroutineContext? = CoroutineExceptionHandler { _, e -> e.log() }): E {
-    var ctx = EventScope.coroutineContext
-    if (context == null) {
-        ctx += CoroutineExceptionHandler { _, e -> e.log() }
-    } else {
-        ctx += context
-        if (context[CoroutineExceptionHandler] == null) {
-            ctx += CoroutineExceptionHandler { _, e -> e.log() }
-        }
-    }
-    return withContext(ctx) { this@broadcast.broadcastInternal() }
+suspend fun <E : Event> E.broadcast(context: CoroutineContext = EmptyCoroutineContext): E {
+    return withContext(EventScope.newCoroutineContext(context)) { this@broadcast.broadcastInternal() }
 }
 
 /**
@@ -77,4 +69,6 @@ suspend fun <E : Event> E.broadcast(context: CoroutineContext? = CoroutineExcept
  * 然而, 若在事件处理过程中使用到 [Contact.sendMessage] 等会 [发送数据包][BotNetworkHandler.sendPacket] 的方法,
  * 发送过程将会通过 [withContext] 将协程切换到 [BotNetworkHandler.NetworkScope]
  */
-object EventScope : CoroutineScope by CoroutineScope(Dispatchers.Default)//todo may change
\ No newline at end of file
+object EventScope : CoroutineScope {
+    override val coroutineContext: CoroutineContext = EmptyCoroutineContext
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
index 250130ac3..ce953b997 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
@@ -21,44 +21,72 @@ enum class ListeningStatus {
 
 // region 顶层方法
 
-inline fun <reified E : Event> subscribe(noinline handler: suspend (E) -> ListeningStatus) = E::class.subscribe(handler)
+suspend inline fun <reified E : Event> subscribe(noinline handler: suspend (E) -> ListeningStatus) =
+    E::class.subscribe(handler)
 
-inline fun <reified E : Event> subscribeAlways(noinline listener: suspend (E) -> Unit) = E::class.subscribeAlways(listener)
+suspend inline fun <reified E : Event> subscribeAlways(noinline listener: suspend (E) -> Unit) =
+    E::class.subscribeAlways(listener)
 
-inline fun <reified E : Event> subscribeOnce(noinline listener: suspend (E) -> Unit) = E::class.subscribeOnce(listener)
+suspend inline fun <reified E : Event> subscribeOnce(noinline listener: suspend (E) -> Unit) =
+    E::class.subscribeOnce(listener)
 
-inline fun <reified E : Event, T> subscribeUntil(valueIfStop: T, noinline listener: suspend (E) -> T) = E::class.subscribeUntil(valueIfStop, listener)
-inline fun <reified E : Event> subscribeUntilFalse(noinline listener: suspend (E) -> Boolean) = E::class.subscribeUntilFalse(listener)
-inline fun <reified E : Event> subscribeUntilTrue(noinline listener: suspend (E) -> Boolean) = E::class.subscribeUntilTrue(listener)
-inline fun <reified E : Event> subscribeUntilNull(noinline listener: suspend (E) -> Any?) = E::class.subscribeUntilNull(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)
 
 
-inline fun <reified E : Event, T> subscribeWhile(valueIfContinue: T, noinline listener: suspend (E) -> T) = E::class.subscribeWhile(valueIfContinue, listener)
-inline fun <reified E : Event> subscribeWhileFalse(noinline listener: suspend (E) -> Boolean) = E::class.subscribeWhileFalse(listener)
-inline fun <reified E : Event> subscribeWhileTrue(noinline listener: suspend (E) -> Boolean) = E::class.subscribeWhileTrue(listener)
-inline fun <reified E : Event> subscribeWhileNull(noinline listener: suspend (E) -> Any?) = E::class.subscribeWhileNull(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)
 
 // endregion
 
 
 // region KClass 的扩展方法 (不推荐)
 
-fun <E : Event> KClass<E>.subscribe(handler: suspend (E) -> ListeningStatus) = this.subscribeInternal(Handler(handler))
+suspend fun <E : Event> KClass<E>.subscribe(handler: suspend (E) -> ListeningStatus) =
+    this.subscribeInternal(Handler(handler))
 
-fun <E : Event> KClass<E>.subscribeAlways(listener: suspend (E) -> Unit) = this.subscribeInternal(Handler { listener(it); ListeningStatus.LISTENING })
+suspend fun <E : Event> KClass<E>.subscribeAlways(listener: suspend (E) -> Unit) =
+    this.subscribeInternal(Handler { listener(it); ListeningStatus.LISTENING })
 
-fun <E : Event> KClass<E>.subscribeOnce(listener: suspend (E) -> Unit) = this.subscribeInternal(Handler { listener(it); ListeningStatus.STOPPED })
+suspend fun <E : Event> KClass<E>.subscribeOnce(listener: suspend (E) -> Unit) =
+    this.subscribeInternal(Handler { listener(it); ListeningStatus.STOPPED })
 
-fun <E : Event, T> KClass<E>.subscribeUntil(valueIfStop: T, listener: suspend (E) -> T) = subscribeInternal(Handler { if (listener(it) === valueIfStop) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
-fun <E : Event> KClass<E>.subscribeUntilFalse(listener: suspend (E) -> Boolean) = subscribeUntil(false, listener)
-fun <E : Event> KClass<E>.subscribeUntilTrue(listener: suspend (E) -> Boolean) = subscribeUntil(true, listener)
-fun <E : Event> KClass<E>.subscribeUntilNull(listener: suspend (E) -> Any?) = subscribeUntil(null, listener)
+suspend fun <E : Event, T> KClass<E>.subscribeUntil(valueIfStop: T, listener: suspend (E) -> T) =
+    subscribeInternal(Handler { if (listener(it) === valueIfStop) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
+
+suspend fun <E : Event> KClass<E>.subscribeUntilFalse(listener: suspend (E) -> Boolean) =
+    subscribeUntil(false, listener)
+
+suspend fun <E : Event> KClass<E>.subscribeUntilTrue(listener: suspend (E) -> Boolean) = subscribeUntil(true, listener)
+suspend fun <E : Event> KClass<E>.subscribeUntilNull(listener: suspend (E) -> Any?) = subscribeUntil(null, listener)
 
 
-fun <E : Event, T> KClass<E>.subscribeWhile(valueIfContinue: T, listener: suspend (E) -> T) = subscribeInternal(Handler { if (listener(it) !== valueIfContinue) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
-fun <E : Event> KClass<E>.subscribeWhileFalse(listener: suspend (E) -> Boolean) = subscribeWhile(false, listener)
-fun <E : Event> KClass<E>.subscribeWhileTrue(listener: suspend (E) -> Boolean) = subscribeWhile(true, listener)
-fun <E : Event> KClass<E>.subscribeWhileNull(listener: suspend (E) -> Any?) = subscribeWhile(null, listener)
+suspend fun <E : Event, T> KClass<E>.subscribeWhile(valueIfContinue: T, listener: suspend (E) -> T) =
+    subscribeInternal(Handler { if (listener(it) !== valueIfContinue) ListeningStatus.STOPPED else ListeningStatus.LISTENING })
+
+suspend fun <E : Event> KClass<E>.subscribeWhileFalse(listener: suspend (E) -> Boolean) =
+    subscribeWhile(false, listener)
+
+suspend fun <E : Event> KClass<E>.subscribeWhileTrue(listener: suspend (E) -> Boolean) = subscribeWhile(true, listener)
+suspend fun <E : Event> KClass<E>.subscribeWhileNull(listener: suspend (E) -> Any?) = subscribeWhile(null, listener)
 
 // endregion
 
@@ -68,15 +96,18 @@ fun <E : Event> KClass<E>.subscribeWhileNull(listener: suspend (E) -> Any?) = su
  * 监听一个事件. 可同时进行多种方式的监听
  * @see ListenerBuilder
  */
-fun <E : Event> KClass<E>.subscribeAll(listeners: ListenerBuilder<E>.() -> Unit) {
-    ListenerBuilder<E> { this.subscribeInternal(it) }.apply(listeners)
+suspend fun <E : Event> KClass<E>.subscribeAll(listeners: suspend ListenerBuilder<E>.() -> Unit) {
+    with(ListenerBuilder<E> { this.subscribeInternal(it) }) {
+        listeners()
+    }
 }
 
 /**
  * 监听一个事件. 可同时进行多种方式的监听
  * @see ListenerBuilder
  */
-inline fun <reified E : Event> subscribeAll(noinline listeners: ListenerBuilder<E>.() -> Unit) = E::class.subscribeAll(listeners)
+suspend inline fun <reified E : Event> subscribeAll(noinline listeners: suspend ListenerBuilder<E>.() -> Unit) =
+    E::class.subscribeAll(listeners)
 
 /**
  * 监听构建器. 可同时进行多种方式的监听
@@ -96,27 +127,31 @@ inline fun <reified E : Event> subscribeAll(noinline listeners: ListenerBuilder<
  */
 @Suppress("MemberVisibilityCanBePrivate", "unused")
 inline class ListenerBuilder<out E : Event>(
-        private val handlerConsumer: (Handler<in E>) -> Unit
+    private val handlerConsumer: suspend (Handler<in E>) -> Unit
 ) {
-    fun handler(listener: suspend (E) -> ListeningStatus) {
+    suspend fun handler(listener: suspend (E) -> ListeningStatus) {
         handlerConsumer(Handler(listener))
     }
 
-    fun always(listener: suspend (E) -> Unit) = handler { listener(it); ListeningStatus.LISTENING }
+    suspend fun always(listener: suspend (E) -> Unit) = handler { listener(it); ListeningStatus.LISTENING }
 
-    fun <T> until(until: T, listener: suspend (E) -> T) = handler { if (listener(it) === until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
-    fun untilFalse(listener: suspend (E) -> Boolean) = until(false, listener)
-    fun untilTrue(listener: suspend (E) -> Boolean) = until(true, listener)
-    fun untilNull(listener: suspend (E) -> Any?) = until(null, listener)
+    suspend fun <T> until(until: T, listener: suspend (E) -> T) =
+        handler { if (listener(it) === until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
+
+    suspend fun untilFalse(listener: suspend (E) -> Boolean) = until(false, listener)
+    suspend fun untilTrue(listener: suspend (E) -> Boolean) = until(true, listener)
+    suspend fun untilNull(listener: suspend (E) -> Any?) = until(null, listener)
 
 
-    fun <T> `while`(until: T, listener: suspend (E) -> T) = handler { if (listener(it) !== until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
-    fun whileFalse(listener: suspend (E) -> Boolean) = `while`(false, listener)
-    fun whileTrue(listener: suspend (E) -> Boolean) = `while`(true, listener)
-    fun whileNull(listener: suspend (E) -> Any?) = `while`(null, listener)
+    suspend fun <T> `while`(until: T, listener: suspend (E) -> T) =
+        handler { if (listener(it) !== until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
+
+    suspend fun whileFalse(listener: suspend (E) -> Boolean) = `while`(false, listener)
+    suspend fun whileTrue(listener: suspend (E) -> Boolean) = `while`(true, listener)
+    suspend fun whileNull(listener: suspend (E) -> Any?) = `while`(null, listener)
 
 
-    fun once(block: suspend (E) -> Unit) = handler { block(it); ListeningStatus.STOPPED }
+    suspend fun once(block: suspend (E) -> Unit) = handler { block(it); ListeningStatus.STOPPED }
 }
 
 // endregion
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
index 4c38ee329..da9efd545 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
@@ -1,18 +1,48 @@
 package net.mamoe.mirai.event.internal
 
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import net.mamoe.mirai.event.Event
+import net.mamoe.mirai.event.EventScope
 import net.mamoe.mirai.event.ListeningStatus
 import net.mamoe.mirai.utils.inlinedRemoveIf
 import kotlin.reflect.KClass
 
 /**
- * 监听和广播实现
+ * 监听和广播实现.
+ * 它会首先检查这个事件是否正在被广播
+ *  - 如果是, 则将新的监听者放入缓存中. 在当前广播结束后转移到主列表 (通过一个协程完成)
+ *  - 如果不是, 则直接将新的监听者放入主列表
  *
  * @author Him188moe
  */
-internal fun <E : Event> KClass<E>.subscribeInternal(listener: Listener<E>) = this.listeners.add(listener)//TODO lock or sth else
+internal suspend fun <E : Event> KClass<E>.subscribeInternal(listener: Listener<E>): Unit = with(this.listeners()) {
+    if (mainMutex.tryLock()) {//能锁则代表这个事件目前没有正在广播.
+        try {
+            add(listener)//直接修改主监听者列表
+        } finally {
+            mainMutex.unlock()
+        }
+        return
+    }
+
+    //不能锁住, 则这个事件正在广播, 那么要将新的监听者放入缓存
+    cacheMutex.withLock {
+        cache.add(listener)
+    }
+
+    EventScope.launch {
+        //启动协程并等待正在进行的广播结束, 然后将缓存转移到主监听者列表
+        //启动后的协程马上就会因为锁而被挂起
+        mainMutex.withLock {
+            if (cache.size != 0) {
+                addAll(cache)
+                cache.clear()
+            }
+        }
+    }
+}
 
 /**
  * 事件监听器
@@ -31,18 +61,37 @@ class Handler<E : Event>(val handler: suspend (E) -> ListeningStatus) : Listener
     override suspend fun onEvent(event: E): ListeningStatus = handler.invoke(event)
 }
 
-internal val <E : Event> KClass<E>.listeners: EventListeners<E> get() = EventListenerManger.get(this)
+/**
+ * 这个事件类的监听器 list
+ */
+internal suspend fun <E : Event> KClass<E>.listeners(): EventListeners<E> = EventListenerManger.get(this)
 
 internal class EventListeners<E : Event> : MutableList<Listener<E>> by mutableListOf() {
-    val lock = Mutex()
+    /**
+     * 主监听者列表.
+     * 广播事件时使用这个锁.
+     */
+    val mainMutex = Mutex()
+    /**
+     * 缓存(监听)事件时使用的锁
+     */
+    val cacheMutex = Mutex()
+    /**
+     * 等待加入到主 list 的监听者. 务必使用 [cacheMutex]
+     */
+    val cache: MutableList<Listener<E>> = mutableListOf()
 }
 
+/**
+ * 管理每个事件 class 的 [EventListeners].
+ * [EventListeners] 是 lazy 的: 它们只会在被需要的时候才创建和存储.
+ */
 internal object EventListenerManger {
     private val registries: MutableMap<KClass<out Event>, EventListeners<out Event>> = mutableMapOf()
+    private val registriesMutex = Mutex()
 
     @Suppress("UNCHECKED_CAST")
-    internal fun <E : Event> get(clazz: KClass<E>): EventListeners<E> {
-        //synchronized(clazz) {
+    internal suspend fun <E : Event> get(clazz: KClass<E>): EventListeners<E> = registriesMutex.withLock {
         if (registries.containsKey(clazz)) {
             return registries[clazz] as EventListeners<E>
         } else {
@@ -51,21 +100,24 @@ internal object EventListenerManger {
                 return it
             }
         }
-        //}
     }
 }
 
 @Suppress("UNCHECKED_CAST")
 internal suspend fun <E : Event> E.broadcastInternal(): E {
-    suspend fun callListeners(listeners: EventListeners<in E>) = listeners.lock.withLock {
-        //fixme 这个锁会导致在事件处理时再监听这个事件死锁
+    //FIXME 若一个 listener 阻塞, 则这个事件全部阻塞.
+    suspend fun callListeners(listeners: EventListeners<in E>) = listeners.mainMutex.withLock {
         listeners.inlinedRemoveIf { it.onEvent(this) == ListeningStatus.STOPPED }
     }
 
-    callListeners(this::class.listeners as EventListeners<in E>)
+    callListeners(this::class.listeners() as EventListeners<in E>)
 
+    //FIXME 这可能不支持所有的平台. 可能需要修改.
     loopAllListeners(this::class) { callListeners(it as EventListeners<in E>) }
     return this
 }
 
-internal expect inline fun <E : Event> loopAllListeners(clazz: KClass<E>, consumer: (EventListeners<in E>) -> Unit)
\ No newline at end of file
+internal expect suspend inline fun <E : Event> loopAllListeners(
+    clazz: KClass<E>,
+    consumer: (EventListeners<in E>) -> Unit
+)
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt
index 44cb454fb..6a0fea7e6 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt
@@ -28,27 +28,25 @@ import kotlin.coroutines.ContinuationInterceptor
  * - [EventPacketHandler] 处理消息相关(群消息/好友消息)([ServerEventPacket])
  * - [ActionPacketHandler] 处理动作相关(踢人/加入群/好友列表等)
  *
+ *
+ * NetworkHandler 实现接口 [CoroutineScope]
+ * 即 [BotNetworkHandler] 自己就是作用域.
+ * 所有 [BotNetworkHandler] 的协程均启动在此作用域下.
+ *
+ * [BotNetworkHandler] 的协程包含:
+ * - UDP 包接收: [PlatformDatagramChannel.read]
+ * - 心跳 Job [HeartbeatPacket]
+ * - SKey 刷新 [ReuestSKeyPcket]
+ * - 所有数据包处理和发送
+ *
+ * [BotNetworkHandler.close] 时将会 [取消][kotlin.coroutines.CoroutineContext.cancelChildren] 所有此作用域下的协程
+ *
  * A BotNetworkHandler is used to connect with Tencent servers.
  */
 @Suppress("PropertyName")
-interface BotNetworkHandler<Socket : DataPacketSocketAdapter> {
-    /**
-     * [BotNetworkHandler] 的协程作用域.
-     * 所有 [BotNetworkHandler] 的协程均启动在此作用域下.
-     *
-     * [BotNetworkHandler] 的协程包含:
-     * - UDP 包接收: [PlatformDatagramChannel.read]
-     * - 心跳 Job [HeartbeatPacket]
-     * - SKey 刷新 [RefreshSKeyRequestPacket]
-     * - 所有数据包处理和发送
-     *
-     * [BotNetworkHandler.close] 时将会 [取消][kotlin.coroutines.CoroutineContext.cancelChildren] 所有此作用域下的协程
-     */
-    val NetworkScope: CoroutineScope
-
+interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : CoroutineScope {
     val socket: Socket
 
-
     /**
      * 得到 [PacketHandler].
      * `get(EventPacketHandler)` 返回 [EventPacketHandler]
@@ -76,8 +74,13 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> {
      */
     suspend fun sendPacket(packet: OutgoingPacket)
 
-    fun close(cause: Throwable? = null) {
+    /**
+     * 等待直到与服务器断开连接. 若未连接则立即返回
+     */
+    suspend fun awaitDisconnection()
+
+    suspend fun close(cause: Throwable? = null) {
         //todo check??
-        NetworkScope.coroutineContext[ContinuationInterceptor]!!.cancelChildren(CancellationException("handler closed", cause))
+        coroutineContext[ContinuationInterceptor]!!.cancelChildren(CancellationException("handler closed", cause))
     }
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt
index 928aab574..bed32c4ed 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt
@@ -6,6 +6,7 @@ import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
 import net.mamoe.mirai.network.protocol.tim.handler.ActionPacketHandler
 import net.mamoe.mirai.network.protocol.tim.handler.DataPacketSocketAdapter
 import net.mamoe.mirai.network.protocol.tim.handler.TemporaryPacketHandler
@@ -14,6 +15,12 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
 import net.mamoe.mirai.utils.getGTK
 import kotlin.coroutines.coroutineContext
 
+internal fun TIMBotNetworkHandler.BotSession(
+    bot: Bot,
+    sessionKey: ByteArray,
+    socket: DataPacketSocketAdapter
+) = BotSession(bot, sessionKey, socket, this)
+
 /**
  * 登录会话. 当登录完成后, 客户端会拿到 sessionKey.
  * 此时建立 session, 然后开始处理事务.
@@ -21,10 +28,10 @@ import kotlin.coroutines.coroutineContext
  * @author Him188moe
  */
 class BotSession(
-        val bot: Bot,
-        val sessionKey: ByteArray,//TODO 协议抽象? 可能并不是所有协议均需要 sessionKey
-        val socket: DataPacketSocketAdapter,
-        val NetworkScope: CoroutineScope
+    val bot: Bot,
+    val sessionKey: ByteArray,//TODO 协议抽象? 可能并不是所有协议均需要 sessionKey
+    val socket: DataPacketSocketAdapter,
+    val NetworkScope: CoroutineScope
 ) {
 
     /**
@@ -66,7 +73,8 @@ class BotSession(
      * @param handler 处理期待的包
      */
     suspend inline fun <reified P : ServerPacket, R> OutgoingPacket.sendAndExpect(noinline handler: suspend (P) -> R): CompletableDeferred<R> {
-        val deferred: CompletableDeferred<R> = coroutineContext[Job].takeIf { it != null }?.let { CompletableDeferred<R>(it) } ?: CompletableDeferred()
+        val deferred: CompletableDeferred<R> =
+            coroutineContext[Job].takeIf { it != null }?.let { CompletableDeferred<R>(it) } ?: CompletableDeferred()
         bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this@BotSession).also {
             it.toSend(this)
             it.onExpect(handler)
@@ -74,7 +82,8 @@ class BotSession(
         return deferred
     }
 
-    suspend inline fun <reified P : ServerPacket> OutgoingPacket.sendAndExpect(): CompletableDeferred<Unit> = sendAndExpect<P, Unit> {}
+    suspend inline fun <reified P : ServerPacket> OutgoingPacket.sendAndExpect(): CompletableDeferred<Unit> =
+        sendAndExpect<P, Unit> {}
 
     suspend inline fun OutgoingPacket.send() = socket.sendPacket(this)
 }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
index ec43a46c4..cc77d1131 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
@@ -5,7 +5,6 @@ import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import kotlinx.io.core.*
 import net.mamoe.mirai.*
-import net.mamoe.mirai.event.EventScope
 import net.mamoe.mirai.event.ListeningStatus
 import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.BeforePacketSendEvent
@@ -26,14 +25,18 @@ import net.mamoe.mirai.network.session
 import net.mamoe.mirai.utils.*
 import net.mamoe.mirai.utils.io.parseServerPacket
 import net.mamoe.mirai.utils.io.toUHexString
+import kotlin.coroutines.CoroutineContext
 
 /**
  * [BotNetworkHandler] 的 TIM PC 协议实现
  *
  * @see BotNetworkHandler
  */
-internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() {
-    override val NetworkScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
+internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
+    BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() {
+
+    override val coroutineContext: CoroutineContext =
+        Dispatchers.Default + CoroutineExceptionHandler { _, e -> bot.logger.log(e) }
 
     override lateinit var socket: BotSocketAdapter
         private set
@@ -76,7 +79,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
     //private | internal
     private fun onLoggedIn(sessionKey: ByteArray) {
         require(size == 0) { "Already logged in" }
-        val session = BotSession(bot, sessionKey, socket, NetworkScope)
+        val session = BotSession(bot, sessionKey, socket)
 
         add(EventPacketHandler(session).asNode(EventPacketHandler))
         add(ActionPacketHandler(session).asNode(ActionPacketHandler))
@@ -85,14 +88,20 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
     private lateinit var sessionKey: ByteArray
 
-    override fun close(cause: Throwable?) {
+    override suspend fun awaitDisconnection() {
+        heartbeatJob?.join()
+    }
+
+    override suspend fun close(cause: Throwable?) {
         super.close(cause)
 
         this.heartbeatJob?.cancel(CancellationException("handler closed"))
+        this.heartbeatJob?.join()//等待 cancel 完成
         this.heartbeatJob = null
 
         if (!this.loginResult.isCompleted && !this.loginResult.isCancelled) {
             this.loginResult.cancel(CancellationException("socket closed"))
+            this.loginResult.join()
         }
 
         this.forEach {
@@ -104,7 +113,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
     override suspend fun sendPacket(packet: OutgoingPacket) = socket.sendPacket(packet)
 
-    internal inner class BotSocketAdapter(override val serverIp: String, val configuration: BotNetworkConfiguration) : DataPacketSocketAdapter {
+    internal inner class BotSocketAdapter(override val serverIp: String, val configuration: BotNetworkConfiguration) :
+        DataPacketSocketAdapter {
         override val channel: PlatformDatagramChannel = PlatformDatagramChannel(serverIp, 8000)
 
         override val isOpen: Boolean get() = channel.isOpen
@@ -130,14 +140,10 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                     continue
                 }
 
-                NetworkScope.launch {
-                    try {
-                        //`.use`: Ensure that the packet is consumed totally so that all the buffers are released
-                        ByteReadPacket(buffer, IoBuffer.Pool).use {
-                            distributePacket(it.parseServerPacket(buffer.readRemaining))
-                        }
-                    } catch (e: Throwable) {
-                        e.log()
+                launch {
+                    //`.use`: Ensure that the packet is consumed totally so that all the buffers are released
+                    ByteReadPacket(buffer, IoBuffer.Pool).use {
+                        distributePacket(it.parseServerPacket(buffer.readRemaining))
                     }
                 }
             }
@@ -150,8 +156,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
 
             val expect = expectPacket<ServerTouchResponsePacket>()
-            NetworkScope.launch { processReceive() }
-            NetworkScope.launch {
+            launch { processReceive() }
+            launch {
                 if (withTimeoutOrNull(configuration.touchTimeout.millisecondsLong) { expect.join() } == null) {
                     loginResult.complete(LoginResult.TIMEOUT)
                 }
@@ -161,7 +167,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
             return loginResult.await()
         }
 
-        private inline fun <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> {
+        private suspend inline fun <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> {
             val receiving = CompletableDeferred<P>()
             subscribe<ServerPacketReceivedEvent> {
                 if (it.packet is P && it.bot === bot) {
@@ -173,7 +179,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
             return receiving
         }
 
-        override suspend fun distributePacket(packet: ServerPacket) {
+        override suspend fun distributePacket(packet: ServerPacket) = coroutineScope {
             try {
                 packet.decode()
             } catch (e: Exception) {
@@ -197,13 +203,13 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
                 if (packet is ServerEventPacket) {
                     //no need to sync acknowledgement packets
-                    NetworkScope.launch {
+                    launch {
                         sendPacket(packet.ResponsePacket(bot.qqAccount, sessionKey))
                     }
                 }
 
                 if (ServerPacketReceivedEvent(bot, packet).broadcast().cancelled) {
-                    return
+                    return@coroutineScope
                 }
 
                 loginHandler.onPacketReceived(packet)
@@ -231,11 +237,11 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
             }
         }*/
 
-        override suspend fun sendPacket(packet: OutgoingPacket) = withContext(NetworkScope.coroutineContext) {
+        override suspend fun sendPacket(packet: OutgoingPacket): Unit = coroutineScope {
             check(channel.isOpen) { "channel is not open" }
 
             if (BeforePacketSendEvent(bot, packet).broadcast().cancelled) {
-                return@withContext
+                return@coroutineScope
             }
 
             packet.packet.use { build ->
@@ -246,7 +252,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                 } catch (e: SendPacketInternalException) {
                     bot.logger.logError("Caught SendPacketInternalException: ${e.cause?.message}")
                     bot.reinitializeNetworkHandler(configuration, e)
-                    return@withContext
+                    return@coroutineScope
                 } finally {
                     buffer.release(IoBuffer.Pool)
                 }
@@ -254,7 +260,9 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
             bot.logGreen("Packet sent:     $packet")
 
-            EventScope.launch { PacketSentEvent(bot, packet).broadcast() }
+            PacketSentEvent(bot, packet).broadcast()
+
+            Unit
         }
 
         override val owner: Bot get() = this@TIMBotNetworkHandler.bot
@@ -307,7 +315,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                         this.loginTime = packet.loginTime
                         this.token0825 = packet.token0825
 
-                        socket.sendPacket(OutgoingPasswordSubmissionPacket(
+                        socket.sendPacket(
+                            OutgoingPasswordSubmissionPacket(
                                 bot = bot.qqAccount,
                                 password = bot.account.password,
                                 loginTime = loginTime,
@@ -316,7 +325,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                                 token0825 = token0825,
                                 token00BA = null,
                                 randomDeviceName = socket.configuration.randomDeviceName
-                        ))
+                            )
+                        )
                     }
                 }
 
@@ -329,7 +339,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                     this.privateKey = getRandomByteArray(16)//似乎是必须的
                     this.token00BA = packet.token00BA
 
-                    socket.sendPacket(OutgoingPasswordSubmissionPacket(
+                    socket.sendPacket(
+                        OutgoingPasswordSubmissionPacket(
                             bot = bot.qqAccount,
                             password = bot.account.password,
                             loginTime = loginTime,
@@ -338,7 +349,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                             token0825 = token0825,
                             token00BA = packet.token00BA,
                             randomDeviceName = socket.configuration.randomDeviceName
-                    ))
+                        )
+                    )
                 }
 
                 is ServerLoginResponseCaptchaInitPacket -> {
@@ -348,7 +360,14 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
                     if (packet.unknownBoolean) {
                         this.captchaSectionId = 1
-                        socket.sendPacket(OutgoingCaptchaTransmissionRequestPacket(bot.qqAccount, this.token0825, this.captchaSectionId++, packet.token00BA))
+                        socket.sendPacket(
+                            OutgoingCaptchaTransmissionRequestPacket(
+                                bot.qqAccount,
+                                this.token0825,
+                                this.captchaSectionId++,
+                                packet.token00BA
+                            )
+                        )
                     }
                 }
 
@@ -372,23 +391,46 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                             socket.sendPacket(OutgoingCaptchaRefreshPacket(bot.qqAccount, token0825))
                         } else {
                             this.captchaSectionId = 0//意味着已经提交验证码
-                            socket.sendPacket(OutgoingCaptchaSubmitPacket(bot.qqAccount, token0825, code, packet.verificationToken))
+                            socket.sendPacket(
+                                OutgoingCaptchaSubmitPacket(
+                                    bot.qqAccount,
+                                    token0825,
+                                    code,
+                                    packet.verificationToken
+                                )
+                            )
                         }
                     } else {
-                        socket.sendPacket(OutgoingCaptchaTransmissionRequestPacket(bot.qqAccount, token0825, captchaSectionId++, packet.token00BA))
+                        socket.sendPacket(
+                            OutgoingCaptchaTransmissionRequestPacket(
+                                bot.qqAccount,
+                                token0825,
+                                captchaSectionId++,
+                                packet.token00BA
+                            )
+                        )
                     }
                 }
 
                 is ServerLoginResponseSuccessPacket -> {
                     this.sessionResponseDecryptionKey = packet.sessionResponseDecryptionKey
-                    socket.sendPacket(OutgoingSessionRequestPacket(bot.qqAccount, socket.serverIp, packet.token38, packet.token88, packet.encryptionKey))
+                    socket.sendPacket(
+                        OutgoingSessionRequestPacket(
+                            bot.qqAccount,
+                            socket.serverIp,
+                            packet.token38,
+                            packet.token88,
+                            packet.encryptionKey
+                        )
+                    )
                 }
 
                 //是ClientPasswordSubmissionPacket之后服务器回复的可能之一
                 is ServerLoginResponseKeyExchangePacket -> {
                     this.privateKey = packet.privateKeyUpdate
 
-                    socket.sendPacket(OutgoingPasswordSubmissionPacket(
+                    socket.sendPacket(
+                        OutgoingPasswordSubmissionPacket(
                             bot = bot.qqAccount,
                             password = bot.account.password,
                             loginTime = loginTime,
@@ -398,22 +440,26 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                             token00BA = packet.tokenUnknown ?: token00BA,
                             randomDeviceName = socket.configuration.randomDeviceName,
                             tlv0006 = packet.tlv0006
-                    ))
+                        )
+                    )
                 }
 
                 is ServerSessionKeyResponsePacket -> {
                     sessionKey = packet.sessionKey
                     bot.logger.logPurple("sessionKey = ${sessionKey.toUHexString()}")
 
-                    heartbeatJob = NetworkScope.launch {
+                    heartbeatJob = launch {
                         while (socket.isOpen) {
                             delay(configuration.heartbeatPeriod.millisecondsLong)
                             with(session) {
                                 class HeartbeatTimeoutException : CancellationException("heartbeat timeout")
 
                                 if (withTimeoutOrNull(configuration.heartbeatTimeout.millisecondsLong) {
-                                            HeartbeatPacket(bot.qqAccount, sessionKey).sendAndExpect<HeartbeatPacket.Response>().join()
-                                        } == null) {
+                                        HeartbeatPacket(
+                                            bot.qqAccount,
+                                            sessionKey
+                                        ).sendAndExpect<HeartbeatPacket.Response>().join()
+                                    } == null) {
                                     bot.logPurple("Heartbeat timed out")
                                     bot.reinitializeNetworkHandler(configuration, HeartbeatTimeoutException())
                                     return@launch
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadFriendImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadFriendImage.kt
index 91e0d999e..4189bd4cc 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadFriendImage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadFriendImage.kt
@@ -2,6 +2,8 @@
 
 package net.mamoe.mirai.network.protocol.tim.packet
 
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
 import kotlinx.io.core.*
 import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.message.ImageId
@@ -21,11 +23,10 @@ suspend fun QQ.uploadImage(image: BufferedImage): ImageId = with(bot.network.ses
     return FriendImageIdRequestPacket(this.qqAccount, sessionKey, this.qqAccount, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> {
         if (it.uKey != null)
             require(httpPostFriendImage(
+                botAccount = bot.qqAccount,
                     uKeyHex = it.uKey!!.toUHexString(""),
-                    botNumber = bot.qqAccount,
-                    imageData = image.data,
-                    fileSize = image.fileSize,
-                    qq = this.qqAccount
+                imageInput = image.input,
+                inputSize = image.inputSize
             ))
         it.imageId!!
     }.await()
@@ -217,8 +218,10 @@ class FriendImageIdRequestPacket(
                 writeFully(image.md5)
 
                 writeUByte(0x28u)
-                writeUVarInt(image.fileSize.toUInt())
+                writeUVarInt(image.inputSize.toUInt())
 
+
+                GlobalScope.launch { }
                 writeUByte(0x32u)
                 //长度应为1A
                 writeUVarintLVPacket {
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadGroupImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadGroupImage.kt
index dea0f47d5..3b38c6a40 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadGroupImage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadGroupImage.kt
@@ -18,10 +18,10 @@ suspend fun Group.uploadImage(
             .sendAndExpect<GroupImageIdRequestPacket.Response, Unit> {
                 if (it.uKey != null) {
                     httpPostGroupImage(
-                            bot = bot.qqAccount,
+                        botAccount = bot.qqAccount,
                             groupNumber = groupId,
-                            imageData = image.data,
-                            fileSize = image.fileSize,
+                        imageInput = image.input,
+                        inputSize = image.inputSize,
                             uKeyHex = it.uKey!!.toUHexString("")
                     )
                 }
@@ -154,7 +154,7 @@ class GroupImageIdRequestPacket(
                     writeUByte(0x10u)
                     writeFully(image.md5)
 
-                    writeTUVarint(0x28u, image.fileSize.toUInt())
+                    writeTUVarint(0x28u, image.inputSize.toUInt())
                     writeUVarintLVPacket(tag = 0x32u) {
                         writeTV(0x5B_00u)
                         writeTV(0x40_00u)
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotNetworkConfiguration.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotNetworkConfiguration.kt
index bed1e0c5a..de91f555e 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotNetworkConfiguration.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotNetworkConfiguration.kt
@@ -3,6 +3,7 @@ package net.mamoe.mirai.utils
 import com.soywiz.klock.TimeSpan
 import com.soywiz.klock.seconds
 import net.mamoe.mirai.network.protocol.tim.packet.login.ServerTouchResponsePacket
+import kotlin.jvm.JvmField
 
 /**
  * 网络配置
@@ -32,6 +33,7 @@ class BotNetworkConfiguration {
     var heartbeatTimeout: TimeSpan = 2.seconds
 
     companion object {
+        @JvmField
         val Default = BotNetworkConfiguration()
     }
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BufferedImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BufferedImage.kt
index 6cba3bd19..5bca83c88 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BufferedImage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BufferedImage.kt
@@ -1,16 +1,27 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE")
+
 package net.mamoe.mirai.utils
 
 import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Input
 import net.mamoe.mirai.message.ImageId
 
+fun BufferedImage(
+    width: Int,
+    height: Int,
+    md5: ByteArray,
+    format: String,
+    data: ByteReadPacket
+) = BufferedImage(width, height, md5, format, data, data.remaining)
+
 class BufferedImage(
-        val width: Int,
-        val height: Int,
-        val md5: ByteArray,
-        val format: String,
-        val data: ByteReadPacket
+    val width: Int,
+    val height: Int,
+    val md5: ByteArray,
+    val format: String,
+    val input: Input,
+    val inputSize: Long
 ) {
-    val fileSize: Long = data.remaining
 
     /**
      * 用于发送消息的 [ImageId]
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
index 3aa4dbd94..329dd0a94 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
@@ -3,8 +3,13 @@
 package net.mamoe.mirai.utils
 
 import com.soywiz.klock.DateTime
-import kotlinx.io.core.ByteReadPacket
-import net.mamoe.mirai.utils.io.printStringFromHex
+import io.ktor.client.HttpClient
+import io.ktor.client.request.HttpRequestBuilder
+import io.ktor.client.request.post
+import io.ktor.http.HttpStatusCode
+import io.ktor.http.URLProtocol
+import io.ktor.http.userAgent
+import kotlinx.io.core.Input
 
 /**
  * 时间戳
@@ -39,28 +44,67 @@ expect fun solveIpAddress(hostname: String): String
  */
 expect fun localIpAddress(): String
 
+/**
+ * Provided by Ktor Http
+ */
+internal expect val httpClient: HttpClient
+
 /**
  * 上传好友图片
  */
-expect suspend fun httpPostFriendImage(
-        uKeyHex: String,
-        fileSize: Long,
-        botNumber: UInt,
-        qq: UInt,
-        imageData: ByteReadPacket
-): Boolean
+@Suppress("DuplicatedCode")
+suspend fun httpPostFriendImage(
+    botAccount: UInt,
+    uKeyHex: String,
+    imageInput: Input,
+    inputSize: Long
+): Boolean = (httpClient.postImage(imageInput, inputSize, uKeyHex) {
+    url {
+        parameters["htcmd"] = "0x6ff0070"
+        parameters["uin"] = botAccount.toLong().toString()
+    }
+
+} as HttpStatusCode).value.also { println(it) } == 200
 
 /**
  * 上传群图片
  */
-expect suspend fun httpPostGroupImage(
-        bot: UInt,
-        groupNumber: UInt,
-        uKeyHex: String,
-        fileSize: Long,
-        imageData: ByteReadPacket
-): Boolean
+@Suppress("DuplicatedCode")
+suspend fun httpPostGroupImage(
+    botAccount: UInt,
+    groupNumber: UInt,
+    uKeyHex: String,
+    imageInput: Input,
+    inputSize: Long
+): Boolean = (httpClient.postImage(imageInput, inputSize, uKeyHex) {
+    url {
+        parameters["htcmd"] = "0x6ff0071"
+        parameters["uin"] = botAccount.toLong().toString()
+        parameters["groupcode"] = groupNumber.toLong().toString()
+    }
+} as HttpStatusCode).value.also { println(it) } == 200
 
-fun main() {
-    "46 52 25 46 60 30 59 4F 4A 5A 51".printStringFromHex()
-}
\ No newline at end of file
+
+private suspend inline fun <reified T> HttpClient.postImage(
+    imageInput: Input,
+    inputSize: Long,
+    uKeyHex: String,
+    block: HttpRequestBuilder.() -> Unit = {}
+): T = post {
+    url {
+        protocol = URLProtocol.HTTP
+        host = "htdata2.qq.com"
+        path("cgi-bin/httpconn")
+
+        parameters["ver"] = "5603"
+        parameters["filezise"] = inputSize.toString()
+        parameters["range"] = 0.toString()
+        parameters["ukey"] = uKeyHex
+
+        userAgent("QQClient")
+    }
+    block()
+    configureBody(inputSize, imageInput)
+}
+
+internal expect fun HttpRequestBuilder.configureBody(inputSize: Long, input: Input)
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJvm.kt
index 0a37596e6..7eff59a9f 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJvm.kt
@@ -2,10 +2,7 @@
 
 package net.mamoe.mirai.contact
 
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.message.Message
 import net.mamoe.mirai.message.MessageChain
 import net.mamoe.mirai.network.protocol.tim.handler.EventPacketHandler
 import net.mamoe.mirai.utils.ContactList
@@ -26,36 +23,36 @@ actual sealed class Contact actual constructor(bot: Bot, number: UInt) : Platfor
 
     abstract override suspend fun sendXMLMessage(message: String)
 
-
-    /**
-     * 阻塞发送一个消息. 仅应在 Java 使用
-     */
-    fun blockingSendMessage(chain: MessageChain) = runBlocking { sendMessage(chain) }
-
-    /**
-     * 阻塞发送一个消息. 仅应在 Java 使用
-     */
-    fun blockingSendMessage(message: Message) = runBlocking { sendMessage(message) }
-
-    /**
-     * 阻塞发送一个消息. 仅应在 Java 使用
-     */
-    fun blockingSendMessage(plain: String) = runBlocking { sendMessage(plain) }
-
-    /**
-     * 异步发送一个消息. 仅应在 Java 使用
-     */
-    fun asyncSendMessage(chain: MessageChain) = bot.network.NetworkScope.launch { sendMessage(chain) }
-
-    /**
-     * 异步发送一个消息. 仅应在 Java 使用
-     */
-    fun asyncSendMessage(message: Message) = bot.network.NetworkScope.launch { sendMessage(message) }
-
-    /**
-     * 异步发送一个消息. 仅应在 Java 使用
-     */
-    fun asyncSendMessage(plain: String) = bot.network.NetworkScope.launch { sendMessage(plain) }
+//
+//    /**
+//     * 阻塞发送一个消息. 仅应在 Java 使用
+//     */
+//    fun blockingSendMessage(chain: MessageChain) = runBlocking { sendMessage(chain) }
+//
+//    /**
+//     * 阻塞发送一个消息. 仅应在 Java 使用
+//     */
+//    fun blockingSendMessage(message: Message) = runBlocking { sendMessage(message) }
+//
+//    /**
+//     * 阻塞发送一个消息. 仅应在 Java 使用
+//     */
+//    fun blockingSendMessage(plain: String) = runBlocking { sendMessage(plain) }
+//
+//    /**
+//     * 异步发送一个消息. 仅应在 Java 使用
+//     */
+//    fun asyncSendMessage(chain: MessageChain) = bot.network.launch { sendMessage(chain) }
+//
+//    /**
+//     * 异步发送一个消息. 仅应在 Java 使用
+//     */
+//    fun asyncSendMessage(message: Message) = bot.network.launch { sendMessage(message) }
+//
+//    /**
+//     * 异步发送一个消息. 仅应在 Java 使用
+//     */
+//    fun asyncSendMessage(plain: String) = bot.network.launch { sendMessage(plain) }
 }
 
 /**
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/SubscribersJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/SubscribersJvm.kt
new file mode 100644
index 000000000..e778dac9c
--- /dev/null
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/SubscribersJvm.kt
@@ -0,0 +1,14 @@
+package net.mamoe.mirai.event
+
+import kotlinx.coroutines.runBlocking
+
+
+// TODO 添加更多
+/**
+ * Jvm 调用实现(阻塞)
+ */
+object Events {
+    @JvmStatic
+    fun <E : Event> subscribe(type: Class<E>, handler: suspend (E) -> ListeningStatus) =
+        runBlocking { type.kotlin.subscribe(handler) }
+}
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventListeneresInternalJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventListeneresInternalJvm.kt
index b5e5ca8ed..676400ce6 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventListeneresInternalJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/internal/EventListeneresInternalJvm.kt
@@ -6,10 +6,13 @@ import kotlin.reflect.full.allSuperclasses
 import kotlin.reflect.full.isSuperclassOf
 
 @Suppress("UNCHECKED_CAST")
-internal actual inline fun <E : Event> loopAllListeners(clazz: KClass<E>, consumer: (EventListeners<in E>) -> Unit) {
+internal actual suspend inline fun <E : Event> loopAllListeners(
+    clazz: KClass<E>,
+    consumer: (EventListeners<in E>) -> Unit
+) {
     clazz.allSuperclasses.forEach {
         if (Event::class.isSuperclassOf(it)) {
-            consumer((it as KClass<out Event>).listeners as EventListeners<in E>)
+            consumer((it as KClass<out Event>).listeners() as EventListeners<in E>)
         }
     }
 }
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BufferedImageJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BufferedImageJvm.kt
index df3c9933b..fb30b763c 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BufferedImageJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BufferedImageJvm.kt
@@ -2,8 +2,11 @@
 
 package net.mamoe.mirai.utils
 
+import kotlinx.io.core.IoBuffer
 import kotlinx.io.core.buildPacket
-import kotlinx.io.streams.writePacket
+import kotlinx.io.streams.asInput
+import java.io.File
+import java.io.FileInputStream
 import java.io.InputStream
 import java.io.OutputStream
 import java.security.MessageDigest
@@ -29,14 +32,33 @@ fun JavaBufferedImage.toMiraiImage(formatName: String = "gif"): BufferedImage {
 }
 
 fun BufferedImage.toJavaImage(): JavaBufferedImage = ImageIO.read(object : InputStream() {
-    override fun read(): Int = with(this@toJavaImage.data) {
-        if (remaining != 0L)
+    override fun read(): Int = with(this@toJavaImage.input) {
+        if (!endOfInput)
             readByte().toInt()
         else -1
     }
 })
 
-/**
- * 将缓存的图片写入流. 注意, 写入后缓存将会被清空.
- */
-fun OutputStream.writeImage(image: BufferedImage) = this.writePacket(image.data)
\ No newline at end of file
+fun File.toMiraiImage(): BufferedImage {
+    val image = ImageIO.getImageReaders(this.inputStream()).asSequence().first()
+
+    val digest = MessageDigest.getInstance("md5")
+    digest.reset()
+    FileInputStream(this).transferTo(object : OutputStream() {
+        override fun write(b: Int) {
+            b.toByte().let {
+                digest.update(it)
+            }
+        }
+    })
+
+    val dimension = image.defaultReadParam.sourceRenderSize
+    return BufferedImage(
+        width = dimension.width,
+        height = dimension.height,
+        md5 = digest.digest(),
+        format = image.formatName,
+        input = this.inputStream().asInput(IoBuffer.Pool),
+        inputSize = this.length()
+    )
+}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
index fe05c7be6..a2b067e4b 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
@@ -4,15 +4,17 @@ package net.mamoe.mirai.utils
 
 import io.ktor.client.HttpClient
 import io.ktor.client.engine.cio.CIO
-import io.ktor.client.request.post
-import io.ktor.content.ByteArrayContent
+import io.ktor.client.request.HttpRequestBuilder
 import io.ktor.http.ContentType
-import io.ktor.http.HttpStatusCode
-import io.ktor.http.URLProtocol
+import io.ktor.http.content.OutgoingContent
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.io.ByteWriteChannel
+import kotlinx.coroutines.io.writeFully
 import kotlinx.coroutines.withContext
 import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Input
 import kotlinx.io.core.readBytes
+import kotlinx.io.core.readFully
 import org.jsoup.Connection
 import org.jsoup.Jsoup
 import java.net.InetAddress
@@ -22,7 +24,7 @@ import java.util.zip.CRC32
 actual val deviceName: String = InetAddress.getLocalHost().hostName
 
 /*
- * TODO we may use libraries that provide these functions
+ * TODO: we may use libraries that provide these functions
  */
 
 actual fun crc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
@@ -33,56 +35,18 @@ actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(host
 
 actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
 
-/**
- * Provided by Ktor Http
- */
-private val httpClient: HttpClient = HttpClient {
-    engine { CIO }
-}
-
-actual suspend fun httpPostFriendImage(
+suspend fun httpPostFriendImageOld(
         uKeyHex: String,
-        fileSize: Long,
         botNumber: UInt,
-        qq: UInt,
-        imageData: ByteReadPacket
-): Boolean = (httpClient.post {
-    url {
-        protocol = URLProtocol.HTTP
-        host = "htdata2.qq.com"
-        path("cgi-bin/httpconn")
-        parameters["htcmd"] = "0x6ff0070"
-        parameters["ver"] = "5603"
-        parameters["ukey"] = uKeyHex
-        parameters["filezise"] = imageData.remaining.toString()
-        parameters["range"] = 0.toString()
-        parameters["uin"] = qq.toString()
-    }
-
-    body = ByteArrayContent(imageData.readBytes(), ContentType.Image.Any)
-} as HttpStatusCode).value == 200
-
-
-//.postImage(imageData)
-
-/**
- * 上传群图片
- */
-actual suspend fun httpPostGroupImage(
-        bot: UInt,
-        groupNumber: UInt,
-        uKeyHex: String,
-        fileSize: Long,
         imageData: ByteReadPacket
 ): Boolean = Jsoup.connect("http://htdata2.qq.com/cgi-bin/httpconn" +
-        "?htcmd=0x6ff0071" +
-        "&term=pc" +
+        "?htcmd=0x6ff0070" +
         "&ver=5603" +
-        "&filesize=${imageData.remaining}" +
-        "&uin=$bot" +
-        "&groupcode=$groupNumber" +
+        "&ukey=${uKeyHex}" +
+        "&filezise=${imageData.remaining}" +
         "&range=0" +
-        "&ukey=" + uKeyHex)
+        "&uin=$botNumber"
+)
         .postImage(imageData)
 
 
@@ -99,4 +63,24 @@ private suspend fun Connection.postImage(image: ByteReadPacket): Boolean = this
 
 private suspend fun Connection.suspendExecute(): Connection.Response = withContext(Dispatchers.IO) {
     execute()
+}
+
+internal actual val httpClient: HttpClient = HttpClient(CIO)
+
+internal actual fun HttpRequestBuilder.configureBody(
+        inputSize: Long,
+        input: Input
+) {
+        body = object : OutgoingContent.WriteChannelContent() {
+                override val contentType: ContentType = ContentType.Image.GIF
+                override val contentLength: Long = inputSize
+
+                override suspend fun writeTo(channel: ByteWriteChannel) {
+                        val buffer = byteArrayOf(1)
+                        while (!input.endOfInput) {
+                                input.readFully(buffer)
+                                channel.writeFully(buffer)
+                        }
+                }
+        }
 }
\ No newline at end of file
diff --git a/mirai-debug/build.gradle b/mirai-debug/build.gradle
index b1a47636b..5b3bdc6d5 100644
--- a/mirai-debug/build.gradle
+++ b/mirai-debug/build.gradle
@@ -5,11 +5,10 @@ dependencies {
     implementation project(':mirai-core')
     compile files('./lib/jpcap.jar')
 
-    implementation rootProject.ext.coroutineCommon
-    implementation rootProject.ext.kotlinJvm
-    implementation rootProject.ext.kotlinxIOJvm
-    compile "org.jetbrains.kotlin:kotlin-reflect:1.3.50"
-    implementation 'org.jsoup:jsoup:1.12.1'
+    api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
+    api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io', version: kotlinxio_version
+
+    api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
 }
 
 tasks.withType(JavaCompile) {
diff --git a/mirai-debug/src/main/java/DownloadImgTest.kt b/mirai-debug/src/main/java/DownloadImgTest.kt
deleted file mode 100644
index e78377cbd..000000000
--- a/mirai-debug/src/main/java/DownloadImgTest.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-import net.mamoe.mirai.utils.io.toUHexString
-import org.jsoup.Connection
-import org.jsoup.Jsoup
-
-fun main() {
-    println(Jsoup.connect("http://61.183.164.34/gchatpic_new/B814D8D6A55D6DB423469E38D2A17AAA23836D74E7A656A4A8288C6078950A33B4A49E854E59B6D2314EFC47D6475902EDE8CADAFAF7F2A7670CAC05EA8314A1241128102F0A3AAF9C07284B1AE35E52F6D0A265235AFA6B/0?vuin=1040400290&term=255&srvver=26933&rf=n")
-            .cookie("ST", "00015DAA9C030040F3B82971FCBC718AA35573100B9CDBA2CB0DE38AF8710CA22E2986246345FC96B82BA0C211FDA700C397DF99FCC0989D67FD75F00B2FFB9CE0D032C3DCAC5A77")
-            .method(Connection.Method.GET)
-            .ignoreContentType(true)
-            .execute()
-            .bodyAsBytes().toUHexString())
-}
\ No newline at end of file
diff --git a/mirai-demos/mirai-demo-1/build.gradle b/mirai-demos/mirai-demo-1/build.gradle
index 820eb9690..e0a26eb75 100644
--- a/mirai-demos/mirai-demo-1/build.gradle
+++ b/mirai-demos/mirai-demo-1/build.gradle
@@ -3,6 +3,6 @@ apply plugin: "java"
 
 dependencies {
     compile project(":mirai-core")
-    compile rootProject.ext.coroutine
-    compile rootProject.ext.kotlinJvm
+    api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
+    api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutines_version
 }
diff --git a/mirai-demos/mirai-demo-1/build.gradle.kts b/mirai-demos/mirai-demo-1/build.gradle.kts
new file mode 100644
index 000000000..e69de29bb
diff --git a/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt b/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt
index f23b44d10..f3fc82eb8 100644
--- a/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt
+++ b/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt
@@ -2,10 +2,7 @@
 
 package demo1
 
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.withContext
-import kotlinx.coroutines.withTimeoutOrNull
+import kotlinx.coroutines.*
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.BotAccount
 import net.mamoe.mirai.contact.Group
@@ -44,14 +41,14 @@ private fun readTestAccount(): BotAccount? {
 }
 
 @Suppress("UNUSED_VARIABLE")
-suspend fun main() {
+suspend fun main() = coroutineScope {
     val bot = Bot(readTestAccount() ?: BotAccount(//填写你的账号
             account = 1994701121u,
             password = "123456"
     ), PlatformLogger())
 
     bot.login {
-        randomDeviceName = true
+        randomDeviceName = false
     }.let {
         if (it != LoginResult.SUCCESS) {
             MiraiLogger.logError("Login failed: " + it.name)
@@ -143,8 +140,7 @@ suspend fun main() {
 
     demo2()
 
-    //由于使用的是协程, main函数执行完后就会结束程序.
-    delay(Long.MAX_VALUE)//永远等待, 以测试事件
+    bot.network.awaitDisconnection()//等到直到断开连接
 }
 
 
@@ -153,7 +149,7 @@ suspend fun main() {
  * 对机器人说 "记笔记", 机器人记录之后的所有消息.
  * 对机器人说 "停止", 机器人停止
  */
-fun demo2() {
+suspend fun demo2() {
     subscribeAlways<FriendMessageEvent> { event ->
         if (event.message eq "记笔记") {
             subscribeUntilFalse<FriendMessageEvent> {