diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6c905bf0..c9b8c933a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,13 +2,15 @@
 
 开发版本. 频繁更新, 不保证高稳定性
 
-## `0.15.1` Unreleased
+## `0.15.1` 2020/2/15
 
 ### mirai-core
 - 统一异常处理: 所有群成员相关操作无权限时均抛出异常而不返回 `false`.
 
 ### mirai-core-qqandroid
 - 初始化未完成时缓存接收的所有事件包 (#46)
+- 解析群踢人事件时忽略找不到的群成员
+- 登录完成后广播事件 `BotOnlineEvent`
 
 ## `0.15.0` 2020/2/14
 
diff --git a/build.gradle b/build.gradle
index de1d115e9..e9b389fc3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,6 +4,7 @@ buildscript {
         jcenter()
         mavenCentral()
         google()
+        maven { url "https://dl.bintray.com/kotlin/kotlin-eap" }
         maven { url 'https://dl.bintray.com/kotlin/kotlin-dev/'}
     }
 
diff --git a/gradle.properties b/gradle.properties
index eb4f13c0e..2fadfe689 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,7 @@
 # style guide
 kotlin.code.style=official
 # config
-mirai_version=0.15.0
+mirai_version=0.15.1
 mirai_japt_version=1.0.0
 kotlin.incremental.multiplatform=true
 kotlin.parallel.tasks.in.project=true
diff --git a/gradle/publish-japt.gradle b/gradle/publish-japt.gradle
new file mode 100644
index 000000000..083199bfb
--- /dev/null
+++ b/gradle/publish-japt.gradle
@@ -0,0 +1,104 @@
+// 部分源码来自 kotlinx.coroutines
+
+def pomConfig = {
+    licenses {
+        license {
+            name "AGPL-V3"
+            url "https://www.gnu.org/licenses/agpl-3.0.txt"
+            distribution "repo"
+        }
+    }
+    developers {
+        developer {
+            id "mamoe"
+            name "Mamoe Technologies"
+        }
+    }
+    scm {
+        url "https://github.com/mamoe/mirai"
+    }
+}
+
+bintray {
+    def keyProps = new Properties()
+    def keyFile = file("../keys.properties")
+    if (keyFile.exists()) keyFile.withInputStream { keyProps.load(it) }
+
+    user = keyProps.getProperty("bintrayUser")
+    key = keyProps.getProperty("bintrayKey")
+
+    pkg {
+        repo = 'mirai'
+        name = "mirai-japt"
+        licenses = ['AGPL']
+        vcsUrl = 'https://github.com/mamoe/mirai'
+    }
+}
+
+afterEvaluate {
+    project.publishing.publications.forEach { publication ->
+        publication.pom.withXml {
+            def root = asNode()
+            //root.appendNode('groupId', project.group)
+            //root.appendNode('artifactId', project.name)
+            //root.appendNode('version', project.version)
+            root.appendNode('name', project.name)
+            root.appendNode('description', project.description)
+            root.appendNode('url', 'https://github.com/mamoe/mirai')
+            root.children().last() + pomConfig
+        }
+    }
+}
+
+bintrayUpload.doFirst {
+    publications = project.publishing.publications
+}
+
+bintrayUpload.dependsOn {
+    def list = new LinkedList<Task>()
+    list.add(tasks.getByName("build"))
+
+    list.addAll(tasks.findAll { task -> task.name.contains('Jar') })
+    list.addAll(tasks.findAll { task -> task.name.startsWith('generateMetadataFileFor') })
+    list.addAll(tasks.findAll { task -> task.name.startsWith('generatePomFileFor') })
+
+    list
+}
+
+
+// empty xxx-javadoc.jar
+task javadocJar(type: Jar) {
+    archiveClassifier = 'javadoc'
+}
+
+publishing {
+    publications.all {
+        // add empty javadocs (no need for MPP root publication which publishes only pom file)
+        if (it.name != 'kotlinMultiplatform') {
+            it.artifact(javadocJar)
+        }
+
+        // Rename MPP artifacts for backward compatibility
+        def type = it.name
+        switch (type) {
+            case 'kotlinMultiplatform':
+                it.artifactId = "$project.name"
+                break
+            case 'metadata':
+                it.artifactId = "$project.name-common"
+                break
+            case 'jvm':
+                it.artifactId = "$project.name"
+                break
+            case 'js':
+            case 'native':
+                it.artifactId = "$project.name-$type"
+                break
+        }
+
+        // disable metadata everywhere, but in native modules
+        if (type == 'maven' || type == 'metadata' || type == 'jvm' || type == 'js') {
+            moduleDescriptorGenerator = null
+        }
+    }
+}
\ No newline at end of file
diff --git a/gradle/publish.gradle b/gradle/publish.gradle
index 10e5f35f0..45c9c6043 100644
--- a/gradle/publish.gradle
+++ b/gradle/publish.gradle
@@ -88,7 +88,7 @@ publishing {
                 it.artifactId = "$project.name-common"
                 break
             case 'jvm':
-                it.artifactId = "$project.name"
+                it.artifactId = "${project.name.replace("-jvm", "")}"
                 break
             case 'js':
             case 'native':
diff --git a/mirai-core-qqandroid/build.gradle.kts b/mirai-core-qqandroid/build.gradle.kts
index 20e38065b..017efc5b3 100644
--- a/mirai-core-qqandroid/build.gradle.kts
+++ b/mirai-core-qqandroid/build.gradle.kts
@@ -5,7 +5,7 @@ plugins {
     id("kotlinx-atomicfu")
     id("kotlinx-serialization")
     `maven-publish`
-    id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
+    id("com.jfrog.bintray") version "1.8.4-jetbrains-3"
 }
 
 apply(from = rootProject.file("gradle/publish.gradle"))
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
index 8f061f3d8..3cd073c91 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
@@ -25,6 +25,7 @@ import net.mamoe.mirai.event.CancellableEvent
 import net.mamoe.mirai.event.Event
 import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.BotOfflineEvent
+import net.mamoe.mirai.event.events.BotOnlineEvent
 import net.mamoe.mirai.network.BotNetworkHandler
 import net.mamoe.mirai.network.WrongPasswordException
 import net.mamoe.mirai.qqandroid.FriendInfoImpl
@@ -278,6 +279,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
         }
         pendingIncomingPackets = null // release
 
+        BotOnlineEvent(bot).broadcast()
         Unit
     }
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
index 87afe2054..24710fb7c 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
@@ -131,7 +131,7 @@ internal class OnlinePush {
                             val groupUin = content.fromUin
 
                             bot.getGroupByUin(groupUin).let { group ->
-                                val member = group[target] as MemberImpl
+                                val member = group.getOrNull(target) as? MemberImpl ?: return NoPacket
                                 this.discardExact(1)
                                 return MemberLeaveEvent.Kick(member.also {
                                     group.members.delegate.remove(member)
diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts
index 61dc885ce..91dcd9de4 100644
--- a/mirai-core/build.gradle.kts
+++ b/mirai-core/build.gradle.kts
@@ -5,7 +5,7 @@ plugins {
     id("kotlinx-atomicfu")
     id("kotlinx-serialization")
     `maven-publish`
-    id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
+    id("com.jfrog.bintray") version "1.8.4-jetbrains-3"
 }
 
 apply(from = rootProject.file("gradle/publish.gradle"))
diff --git a/mirai-japt/build.gradle.kts b/mirai-japt/build.gradle.kts
index f2dcaff16..4b592fd06 100644
--- a/mirai-japt/build.gradle.kts
+++ b/mirai-japt/build.gradle.kts
@@ -1,12 +1,28 @@
+import java.util.*
+
+buildscript {
+    repositories {
+        mavenLocal()
+        jcenter()
+        mavenCentral()
+        google()
+    }
+
+    dependencies {
+        // Do try to waste your time.
+        @kotlin.Suppress("GradleDependency") // 1.8.4 不能跑
+        classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.0")
+    }
+}
+
 plugins {
     kotlin("jvm")
     java
     `maven-publish`
-    id("com.jfrog.bintray") version "1.8.4-jetbrains-3" // DO NOT CHANGE THIS VERSION UNLESS YOU WANT TO WASTE YOUR TIME
+    // maven
+    id("com.jfrog.bintray") version "1.8.0"
 }
 
-apply(from = rootProject.file("gradle/publish.gradle"))
-
 val kotlinVersion: String by rootProject.ext
 val atomicFuVersion: String by rootProject.ext
 val coroutinesVersion: String by rootProject.ext
@@ -54,4 +70,58 @@ dependencies {
 
 tasks.withType<JavaCompile>() {
     options.encoding = "UTF-8"
+}
+
+bintray {
+    val keyProps = Properties()
+    val keyFile = file("../keys.properties")
+    if (keyFile.exists()) keyFile.inputStream().use { keyProps.load(it) }
+
+    user = keyProps.getProperty("bintrayUser")
+    key = keyProps.getProperty("bintrayKey")
+    setPublications("mavenJava")
+    setConfigurations("archives")
+
+    pkg.apply {
+        repo = "mirai"
+        name = "mirai-japt"
+        setLicenses("AGPLv3")
+        publicDownloadNumbers = true
+        vcsUrl = "https://github.com/mamoe/mirai"
+    }
+}
+
+@Suppress("DEPRECATION")
+val sourcesJar by tasks.registering(Jar::class) {
+    classifier = "sources"
+    from(sourceSets.main.get().allSource)
+}
+
+publishing {
+    /*
+    repositories {
+        maven {
+            // change to point to your repo, e.g. http://my.org/repo
+            url = uri("$buildDir/repo")
+        }
+    }*/
+    publications {
+        register("mavenJava", MavenPublication::class) {
+            from(components["java"])
+
+            groupId = rootProject.group.toString()
+            artifactId = "mirai-japt"
+            version = mirai_japt_version
+
+            pom.withXml {
+                val root = asNode()
+                root.appendNode("description", description)
+                root.appendNode("name", project.name)
+                root.appendNode("url", "https://github.com/mamoe/mirai")
+                root.children().last()
+            }
+
+            artifact(sourcesJar.get())
+        }
+    }
 }
\ No newline at end of file