mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 07:30:14 +08:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt
This commit is contained in:
commit
cd0b27e63d
50
.github/workflows/bintray.yml
vendored
Normal file
50
.github/workflows/bintray.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# This is a basic workflow to help you get started with Actions
|
||||||
|
|
||||||
|
name: Bintray Publish
|
||||||
|
|
||||||
|
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||||
|
# events but only for the master branch
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- created
|
||||||
|
|
||||||
|
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||||
|
jobs:
|
||||||
|
# This workflow contains a single job called "build"
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up JDK 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
- name: Grant execute permission for gradlew
|
||||||
|
run: chmod +x gradlew
|
||||||
|
- name: Gradle clean
|
||||||
|
run: ./gradlew clean
|
||||||
|
- name: Gradle build
|
||||||
|
run: ./gradlew build # if test's failed, don't publish
|
||||||
|
- name: Check keys
|
||||||
|
run: ./gradlew :mirai-core:ensureBintrayAvailable :mirai-core-qqandroid:ensureBintrayAvailable
|
||||||
|
- name: Gradle :mirai-core:bintrayUpload
|
||||||
|
run: ./gradlew :mirai-core:bintrayUpload -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||||
|
- name: Gradle :mirai-core-qqandroid:bintrayUpload
|
||||||
|
run: ./gradlew :mirai-core-qqandroid:bintrayUpload -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
|
||||||
|
|
||||||
|
|
||||||
|
# - name: Upload artifact
|
||||||
|
# uses: actions/upload-artifact@v1.0.0
|
||||||
|
# with:
|
||||||
|
# # Artifact name
|
||||||
|
# name: mirai-core
|
||||||
|
# # Directory containing files to upload
|
||||||
|
# path: "mirai-core/build/libs/mirai-core-*-all.jar"
|
||||||
|
# - name: Upload artifact
|
||||||
|
# uses: actions/upload-artifact@v1.0.0
|
||||||
|
# with:
|
||||||
|
# # Artifact name
|
||||||
|
# name: mirai-core-qqandroid-all
|
||||||
|
# # Directory containing files to upload
|
||||||
|
# path: "mirai-core-qqandroid/build/libs/mirai-core-qqandroid-*-all.jar"
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -44,3 +44,5 @@ keys.properties
|
|||||||
/plugins/
|
/plugins/
|
||||||
|
|
||||||
token.txt
|
token.txt
|
||||||
|
bintray.user.txt
|
||||||
|
bintray.key.txt
|
@ -29,8 +29,7 @@ object Versions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Publishing {
|
object Publishing {
|
||||||
const val bintray = "1.8.4-jetbrains-3"
|
const val bintray = "1.8.5"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
99
buildSrc/src/main/kotlin/upload/Bintray.kt
Normal file
99
buildSrc/src/main/kotlin/upload/Bintray.kt
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package upload
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.kotlin.dsl.provideDelegate
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
object Bintray {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isBintrayAvailable(project: Project): Boolean {
|
||||||
|
return kotlin.runCatching {
|
||||||
|
getUser(project)
|
||||||
|
getKey(project)
|
||||||
|
}.isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getUser(project: Project): String {
|
||||||
|
kotlin.runCatching {
|
||||||
|
@Suppress("UNUSED_VARIABLE", "LocalVariableName")
|
||||||
|
val bintray_user: String by project
|
||||||
|
return bintray_user
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin.runCatching {
|
||||||
|
@Suppress("UNUSED_VARIABLE", "LocalVariableName")
|
||||||
|
val bintray_user: String by project.rootProject
|
||||||
|
return bintray_user
|
||||||
|
}
|
||||||
|
|
||||||
|
System.getProperty("bintray_user", null)?.let {
|
||||||
|
return it.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
File(File(System.getProperty("user.dir")).parent, "/bintray.user.txt").let { local ->
|
||||||
|
if (local.exists()) {
|
||||||
|
return local.readText().trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File(File(System.getProperty("user.dir")), "/bintray.user.txt").let { local ->
|
||||||
|
if (local.exists()) {
|
||||||
|
return local.readText().trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error(
|
||||||
|
"Cannot find bintray user, " +
|
||||||
|
"please specify by creating a file bintray.user.txt in project dir, " +
|
||||||
|
"or by providing JVM parameter 'bintray_user'"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getKey(project: Project): String {
|
||||||
|
kotlin.runCatching {
|
||||||
|
@Suppress("UNUSED_VARIABLE", "LocalVariableName")
|
||||||
|
val bintray_key: String by project
|
||||||
|
return bintray_key
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin.runCatching {
|
||||||
|
@Suppress("UNUSED_VARIABLE", "LocalVariableName")
|
||||||
|
val bintray_key: String by project.rootProject
|
||||||
|
return bintray_key
|
||||||
|
}
|
||||||
|
|
||||||
|
System.getProperty("bintray_key", null)?.let {
|
||||||
|
return it.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
File(File(System.getProperty("user.dir")).parent, "/bintray.key.txt").let { local ->
|
||||||
|
if (local.exists()) {
|
||||||
|
return local.readText().trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File(File(System.getProperty("user.dir")), "/bintray.key.txt").let { local ->
|
||||||
|
if (local.exists()) {
|
||||||
|
return local.readText().trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error(
|
||||||
|
"Cannot find bintray key, " +
|
||||||
|
"please specify by creating a file bintray.key.txt in project dir, " +
|
||||||
|
"or by providing JVM parameter 'bintray_key'"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,104 +0,0 @@
|
|||||||
// 部分源码来自 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,21 @@
|
|||||||
|
import upload.Bintray
|
||||||
|
|
||||||
// 部分源码来自 kotlinx.coroutines
|
// 部分源码来自 kotlinx.coroutines
|
||||||
// Source code from kotlinx.coroutines
|
// Source code from kotlinx.coroutines
|
||||||
|
|
||||||
|
task ensureBintrayAvailable() {
|
||||||
|
doLast {
|
||||||
|
if (!Bintray.isBintrayAvailable(project)) {
|
||||||
|
throw new IllegalStateException("bintray isn't available. ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Bintray.isBintrayAvailable(project)) {
|
||||||
|
println("bintray isn't available. NO PUBLICATIONS WILL BE SET")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
def pomConfig = {
|
def pomConfig = {
|
||||||
licenses {
|
licenses {
|
||||||
license {
|
license {
|
||||||
@ -22,12 +37,8 @@ def pomConfig = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bintray {
|
bintray {
|
||||||
def keyProps = new Properties()
|
user = Bintray.getUser(project)
|
||||||
def keyFile = file("../keys.properties")
|
key = Bintray.getKey(project)
|
||||||
if (keyFile.exists()) keyFile.withInputStream { keyProps.load(it) }
|
|
||||||
|
|
||||||
user = keyProps.getProperty("bintrayUser")
|
|
||||||
key = keyProps.getProperty("bintrayKey")
|
|
||||||
|
|
||||||
pkg {
|
pkg {
|
||||||
repo = 'mirai'
|
repo = 'mirai'
|
||||||
@ -67,14 +78,14 @@ bintrayUpload.dependsOn {
|
|||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try {
|
||||||
|
|
||||||
// empty xxx-javadoc.jar
|
// empty xxx-javadoc.jar
|
||||||
task javadocJar(type: Jar) {
|
task javadocJar(type: Jar) {
|
||||||
archiveClassifier = 'javadoc'
|
archiveClassifier = 'javadoc'
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e){
|
} catch (Exception ignored) {
|
||||||
|
|
||||||
}
|
}
|
||||||
publishing {
|
publishing {
|
||||||
|
@ -86,7 +86,7 @@ internal class MessageSourceFromFriendImpl(
|
|||||||
type = 0,
|
type = 0,
|
||||||
time = msg.msgHead.msgTime,
|
time = msg.msgHead.msgTime,
|
||||||
pbReserve = SourceMsg.ResvAttr(
|
pbReserve = SourceMsg.ResvAttr(
|
||||||
origUids = id.toULong().toLong()
|
origUids = id.toLong() and 0xFFFF_FFFF
|
||||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||||
srcMsg = MsgComm.Msg(
|
srcMsg = MsgComm.Msg(
|
||||||
msgHead = MsgComm.MsgHead(
|
msgHead = MsgComm.MsgHead(
|
||||||
@ -96,7 +96,7 @@ internal class MessageSourceFromFriendImpl(
|
|||||||
c2cCmd = msg.msgHead.c2cCmd,
|
c2cCmd = msg.msgHead.c2cCmd,
|
||||||
msgSeq = msg.msgHead.msgSeq,
|
msgSeq = msg.msgHead.msgSeq,
|
||||||
msgTime = msg.msgHead.msgTime,
|
msgTime = msg.msgHead.msgTime,
|
||||||
msgUid = id.toULong().toLong(), // ok
|
msgUid = id.toLong() and 0xFFFF_FFFF, // ok
|
||||||
// groupInfo = MsgComm.GroupInfo(groupCode = msg.msgHead.groupInfo.groupCode),
|
// groupInfo = MsgComm.GroupInfo(groupCode = msg.msgHead.groupInfo.groupCode),
|
||||||
isSrcMsg = true
|
isSrcMsg = true
|
||||||
),
|
),
|
||||||
@ -151,7 +151,7 @@ internal class MessageSourceFromTempImpl(
|
|||||||
type = 0,
|
type = 0,
|
||||||
time = msg.msgHead.msgTime,
|
time = msg.msgHead.msgTime,
|
||||||
pbReserve = SourceMsg.ResvAttr(
|
pbReserve = SourceMsg.ResvAttr(
|
||||||
origUids = id.toULong().toLong()
|
origUids = id.toLong() and 0xFFFF_FFFF
|
||||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||||
srcMsg = MsgComm.Msg(
|
srcMsg = MsgComm.Msg(
|
||||||
msgHead = MsgComm.MsgHead(
|
msgHead = MsgComm.MsgHead(
|
||||||
@ -161,7 +161,7 @@ internal class MessageSourceFromTempImpl(
|
|||||||
c2cCmd = msg.msgHead.c2cCmd,
|
c2cCmd = msg.msgHead.c2cCmd,
|
||||||
msgSeq = msg.msgHead.msgSeq,
|
msgSeq = msg.msgHead.msgSeq,
|
||||||
msgTime = msg.msgHead.msgTime,
|
msgTime = msg.msgHead.msgTime,
|
||||||
msgUid = id.toULong().toLong(), // ok
|
msgUid = id.toLong() and 0xFFFF_FFFF, // ok
|
||||||
// groupInfo = MsgComm.GroupInfo(groupCode = msg.msgHead.groupInfo.groupCode),
|
// groupInfo = MsgComm.GroupInfo(groupCode = msg.msgHead.groupInfo.groupCode),
|
||||||
isSrcMsg = true
|
isSrcMsg = true
|
||||||
),
|
),
|
||||||
|
@ -20,16 +20,14 @@ import net.mamoe.mirai.message.data.*
|
|||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.HummerCommelem
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.HummerCommelem
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||||
import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
|
import net.mamoe.mirai.qqandroid.utils.*
|
||||||
import net.mamoe.mirai.qqandroid.utils.encodeToString
|
|
||||||
import net.mamoe.mirai.qqandroid.utils.hexToBytes
|
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
||||||
import net.mamoe.mirai.qqandroid.utils.read
|
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import net.mamoe.mirai.utils.debug
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
|
|
||||||
private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。")
|
private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。")
|
||||||
@ -181,7 +179,12 @@ private val PB_RESERVE_FOR_DOUTU = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".h
|
|||||||
private val PB_RESERVE_FOR_ELSE = "78 00 F8 01 00 C8 02 00".hexToBytes()
|
private val PB_RESERVE_FOR_ELSE = "78 00 F8 01 00 C8 02 00".hexToBytes()
|
||||||
|
|
||||||
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||||
internal fun MsgComm.Msg.toMessageChain(bot: Bot, groupIdOrZero: Long, onlineSource: Boolean, isTemp: Boolean = false): MessageChain {
|
internal fun MsgComm.Msg.toMessageChain(
|
||||||
|
bot: Bot,
|
||||||
|
groupIdOrZero: Long,
|
||||||
|
onlineSource: Boolean,
|
||||||
|
isTemp: Boolean = false
|
||||||
|
): MessageChain {
|
||||||
val elements = this.msgBody.richText.elems
|
val elements = this.msgBody.richText.elems
|
||||||
|
|
||||||
return buildMessageChain(elements.size + 1) {
|
return buildMessageChain(elements.size + 1) {
|
||||||
@ -251,43 +254,40 @@ internal inline fun <reified R> Iterable<*>.firstIsInstanceOrNull(): R? {
|
|||||||
@OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
|
@OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
|
||||||
internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: Bot, message: MessageChainBuilder) {
|
internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: Bot, message: MessageChainBuilder) {
|
||||||
// (this._miraiContentToString())
|
// (this._miraiContentToString())
|
||||||
this.forEach {
|
this.forEach { element ->
|
||||||
when {
|
when {
|
||||||
it.srcMsg != null -> message.add(
|
element.srcMsg != null ->
|
||||||
QuoteReply(
|
message.add(QuoteReply(OfflineMessageSourceImplBySourceMsg(element.srcMsg, bot, groupIdOrZero)))
|
||||||
OfflineMessageSourceImplBySourceMsg(
|
element.notOnlineImage != null -> message.add(OnlineFriendImageImpl(element.notOnlineImage))
|
||||||
it.srcMsg,
|
element.customFace != null -> message.add(OnlineGroupImageImpl(element.customFace))
|
||||||
bot,
|
element.face != null -> message.add(Face(element.face.index))
|
||||||
groupIdOrZero
|
element.text != null -> {
|
||||||
)
|
if (element.text.attr6Buf.isEmpty()) {
|
||||||
)
|
message.add(element.text.str.toMessage())
|
||||||
)
|
|
||||||
it.notOnlineImage != null -> message.add(OnlineFriendImageImpl(it.notOnlineImage))
|
|
||||||
it.customFace != null -> message.add(OnlineGroupImageImpl(it.customFace))
|
|
||||||
it.face != null -> message.add(Face(it.face.index))
|
|
||||||
it.text != null -> {
|
|
||||||
if (it.text.attr6Buf.isEmpty()) {
|
|
||||||
message.add(it.text.str.toMessage())
|
|
||||||
} else {
|
} else {
|
||||||
val id: Long
|
val id: Long
|
||||||
it.text.attr6Buf.read {
|
element.text.attr6Buf.read {
|
||||||
discardExact(7)
|
discardExact(7)
|
||||||
id = readUInt().toLong()
|
id = readUInt().toLong()
|
||||||
}
|
}
|
||||||
if (id == 0L) {
|
if (id == 0L) {
|
||||||
message.add(AtAll)
|
message.add(AtAll)
|
||||||
} else {
|
} else {
|
||||||
message.add(At._lowLevelConstructAtInstance(id, it.text.str))
|
message.add(At._lowLevelConstructAtInstance(id, element.text.str))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it.lightApp != null -> {
|
element.lightApp != null -> {
|
||||||
val content = MiraiPlatformUtils.unzip(it.lightApp.data, 1).encodeToString()
|
val content = runWithBugReport("解析 lightApp", { element.lightApp.data.toUHexString() }) {
|
||||||
|
MiraiPlatformUtils.unzip(element.lightApp.data, 1).encodeToString()
|
||||||
|
}
|
||||||
message.add(LightApp(content))
|
message.add(LightApp(content))
|
||||||
}
|
}
|
||||||
it.richMsg != null -> {
|
element.richMsg != null -> {
|
||||||
val content = MiraiPlatformUtils.unzip(it.richMsg.template1, 1).encodeToString()
|
val content = runWithBugReport("解析 richMsg", { element.richMsg.template1.toUHexString() }) {
|
||||||
when (it.richMsg.serviceId) {
|
MiraiPlatformUtils.unzip(element.richMsg.template1, 1).encodeToString()
|
||||||
|
}
|
||||||
|
when (element.richMsg.serviceId) {
|
||||||
1 -> message.add(JsonMessage(content))
|
1 -> message.add(JsonMessage(content))
|
||||||
60 -> message.add(XmlMessage(content))
|
60 -> message.add(XmlMessage(content))
|
||||||
35 -> {
|
35 -> {
|
||||||
@ -373,26 +373,24 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@Suppress("DEPRECATION")
|
throw contextualBugReportException("richMsg.serviceId",
|
||||||
MiraiLogger.debug {
|
"richMsg.serviceId: ${element.richMsg.serviceId}, content=${element.richMsg.template1.contentToString()}, \n" + "tryUnzip=${content}")
|
||||||
"unknown richMsg.serviceId: ${it.richMsg.serviceId}, content=${it.richMsg.template1.contentToString()}, \ntryUnzip=${content}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
element.elemFlags2 != null
|
||||||
it.elemFlags2 != null
|
|| element.extraInfo != null
|
||||||
|| it.extraInfo != null
|
|| element.generalFlags != null -> {
|
||||||
|| it.generalFlags != null -> {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
it.commonElem != null -> {
|
element.commonElem != null -> {
|
||||||
when (it.commonElem.serviceType) {
|
when (element.commonElem.serviceType) {
|
||||||
2 -> {
|
2 -> {
|
||||||
val proto = it.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype2.serializer())
|
val proto = element.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype2.serializer())
|
||||||
message.add(PokeMessage(proto.pokeType, proto.vaspokeId))
|
message.add(PokeMessage(proto.pokeType, proto.vaspokeId))
|
||||||
}
|
}
|
||||||
3 -> {
|
3 -> {
|
||||||
val proto = it.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer())
|
val proto = element.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer())
|
||||||
if (proto.flashTroopPic != null) {
|
if (proto.flashTroopPic != null) {
|
||||||
message.add(GroupFlashImage(OnlineGroupImageImpl(proto.flashTroopPic)))
|
message.add(GroupFlashImage(OnlineGroupImageImpl(proto.flashTroopPic)))
|
||||||
}
|
}
|
||||||
@ -409,3 +407,26 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun contextualBugReportException(
|
||||||
|
context: String,
|
||||||
|
forDebug: String,
|
||||||
|
e: Throwable? = null
|
||||||
|
): IllegalStateException {
|
||||||
|
return IllegalStateException("在 $context 时遇到了意料之中的问题. 请完整复制此日志提交给 mirai. 调试信息: $forDebug", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
|
internal inline fun <R> runWithBugReport(context: String, forDebug: () -> String, block: () -> R): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||||
|
callsInPlace(forDebug, InvocationKind.AT_MOST_ONCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return runCatching(block).getOrElse {
|
||||||
|
throw contextualBugReportException(context, forDebug(), it)
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ import net.mamoe.mirai.event.events.BotOnlineEvent
|
|||||||
import net.mamoe.mirai.message.FriendMessage
|
import net.mamoe.mirai.message.FriendMessage
|
||||||
import net.mamoe.mirai.message.GroupMessage
|
import net.mamoe.mirai.message.GroupMessage
|
||||||
import net.mamoe.mirai.network.BotNetworkHandler
|
import net.mamoe.mirai.network.BotNetworkHandler
|
||||||
|
import net.mamoe.mirai.network.UnsupportedSMSLoginException
|
||||||
import net.mamoe.mirai.network.WrongPasswordException
|
import net.mamoe.mirai.network.WrongPasswordException
|
||||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||||
import net.mamoe.mirai.qqandroid.contact.FriendInfoImpl
|
import net.mamoe.mirai.qqandroid.contact.FriendInfoImpl
|
||||||
@ -105,6 +106,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
}.also { heartbeatJob = it }
|
}.also { heartbeatJob = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(MiraiExperimentalAPI::class)
|
||||||
override suspend fun relogin(cause: Throwable?) {
|
override suspend fun relogin(cause: Throwable?) {
|
||||||
heartbeatJob?.cancel(CancellationException("relogin", cause))
|
heartbeatJob?.cancel(CancellationException("relogin", cause))
|
||||||
heartbeatJob?.join()
|
heartbeatJob?.join()
|
||||||
@ -163,9 +165,15 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
is WtLogin.Login.LoginPacketResponse.Success -> {
|
is WtLogin.Login.LoginPacketResponse.Success -> {
|
||||||
logger.info("Login successful")
|
logger.info { "Login successful" }
|
||||||
break@mainloop
|
break@mainloop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is WtLogin.Login.LoginPacketResponse.SMSVerifyCodeNeeded -> {
|
||||||
|
val message = "SMS required: $response, which isn't yet supported"
|
||||||
|
logger.error(message)
|
||||||
|
throw UnsupportedSMSLoginException(message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +608,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
internal val packetListeners = LockFreeLinkedList<PacketListener>()
|
internal val packetListeners = LockFreeLinkedList<PacketListener>()
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal inner class PacketListener( // callback
|
internal inner class PacketListener(
|
||||||
|
// callback
|
||||||
val commandName: String,
|
val commandName: String,
|
||||||
val sequenceId: Int
|
val sequenceId: Int
|
||||||
) : CompletableDeferred<Packet?> by CompletableDeferred(supervisor) {
|
) : CompletableDeferred<Packet?> by CompletableDeferred(supervisor) {
|
||||||
|
@ -131,6 +131,7 @@ internal open class QQAndroidClient(
|
|||||||
internal fun nextHighwayDataTransSequenceIdForApplyUp(): Int = highwayDataTransSequenceIdForApplyUp.getAndAdd(2)
|
internal fun nextHighwayDataTransSequenceIdForApplyUp(): Int = highwayDataTransSequenceIdForApplyUp.getAndAdd(2)
|
||||||
|
|
||||||
internal val onlinePushCacheList: LockFreeLinkedList<Short> = LockFreeLinkedList()
|
internal val onlinePushCacheList: LockFreeLinkedList<Short> = LockFreeLinkedList()
|
||||||
|
internal val pbPushTransMsgCacheList: LockFreeLinkedList<Int> = LockFreeLinkedList()
|
||||||
|
|
||||||
val appClientVersion: Int = 0
|
val appClientVersion: Int = 0
|
||||||
|
|
||||||
|
@ -49,7 +49,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.*
|
|||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
||||||
import net.mamoe.mirai.qqandroid.utils._miraiContentToString
|
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.decodeUniPacket
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.decodeUniPacket
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
||||||
@ -132,7 +131,7 @@ internal class MessageSvc {
|
|||||||
open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: List<Packet>) :
|
open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: List<Packet>) :
|
||||||
MultiPacketByIterable<Packet>(delegate) {
|
MultiPacketByIterable<Packet>(delegate) {
|
||||||
override fun toString(): String =
|
override fun toString(): String =
|
||||||
"MessageSvc.PbGetMsg.Response($syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))"
|
"MessageSvc.PbGetMsg.Response(syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))"
|
||||||
}
|
}
|
||||||
|
|
||||||
object EmptyResponse : GetMsgSuccess(emptyList())
|
object EmptyResponse : GetMsgSuccess(emptyList())
|
||||||
@ -370,12 +369,13 @@ internal class MessageSvc {
|
|||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
|
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
|
||||||
): OutgoingPacket {
|
): OutgoingPacket {
|
||||||
|
val rand = Random.nextInt().absoluteValue
|
||||||
val source = MessageSourceToFriendImpl(
|
val source = MessageSourceToFriendImpl(
|
||||||
id = Random.nextInt().absoluteValue,
|
id = rand,
|
||||||
sender = client.bot,
|
sender = client.bot,
|
||||||
target = qq,
|
target = qq,
|
||||||
time = currentTimeSeconds.toInt(),
|
time = currentTimeSeconds.toInt(),
|
||||||
sequenceId = client.atomicNextMessageSequenceId(),
|
sequenceId = rand,
|
||||||
originalMessage = message
|
originalMessage = message
|
||||||
)
|
)
|
||||||
sourceCallback(source)
|
sourceCallback(source)
|
||||||
@ -406,7 +406,7 @@ internal class MessageSvc {
|
|||||||
),
|
),
|
||||||
msgSeq = source.sequenceId,
|
msgSeq = source.sequenceId,
|
||||||
msgRand = source.id,
|
msgRand = source.id,
|
||||||
syncCookie = SyncCookie(time = source.time.toULong().toLong()).toByteArray(SyncCookie.serializer())
|
syncCookie = SyncCookie(time = source.time.toLong()).toByteArray(SyncCookie.serializer())
|
||||||
// msgVia = 1
|
// msgVia = 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -454,7 +454,7 @@ internal class MessageSvc {
|
|||||||
),
|
),
|
||||||
msgSeq = source.sequenceId,
|
msgSeq = source.sequenceId,
|
||||||
msgRand = source.id,
|
msgRand = source.id,
|
||||||
syncCookie = SyncCookie(time = source.time.toULong().toLong()).toByteArray(SyncCookie.serializer())
|
syncCookie = SyncCookie(time = source.time.toLong()).toByteArray(SyncCookie.serializer())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,14 @@ internal class OnlinePush {
|
|||||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? {
|
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? {
|
||||||
val content = this.readProtoBuf(OnlinePushTrans.PbMsgInfo.serializer())
|
val content = this.readProtoBuf(OnlinePushTrans.PbMsgInfo.serializer())
|
||||||
|
|
||||||
|
val cache = bot.client.pbPushTransMsgCacheList.removeUntilFirst { it == content.msgSeq }
|
||||||
|
if (cache == null) {
|
||||||
|
bot.client.pbPushTransMsgCacheList.addLast(content.msgSeq)
|
||||||
|
} else {
|
||||||
|
bot.client.pbPushTransMsgCacheList.remove(cache)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
content.msgData.read<Unit> {
|
content.msgData.read<Unit> {
|
||||||
when (content.msgType) {
|
when (content.msgType) {
|
||||||
44 -> {
|
44 -> {
|
||||||
@ -237,17 +245,6 @@ internal class OnlinePush {
|
|||||||
}.flatMap { it.vMsg.read { mapper(it) } }
|
}.flatMap { it.vMsg.read { mapper(it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun LockFreeLinkedList<Short>.removeUntilFirst(block: (Short) -> Boolean): Short? {
|
|
||||||
this.forEach {
|
|
||||||
if (!block(it)) {
|
|
||||||
this.remove(it)
|
|
||||||
} else {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun lambda732(block: ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet>):
|
private fun lambda732(block: ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet>):
|
||||||
ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet> {
|
ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet> {
|
||||||
return block
|
return block
|
||||||
@ -497,7 +494,7 @@ internal class OnlinePush {
|
|||||||
discardExact(1)
|
discardExact(1)
|
||||||
|
|
||||||
Transformers732[internalType]
|
Transformers732[internalType]
|
||||||
?.let { it(this@deco, group as GroupImpl, bot) }
|
?.let { it(this@deco, group, bot) }
|
||||||
?: kotlin.run {
|
?: kotlin.run {
|
||||||
bot.network.logger.debug {
|
bot.network.logger.debug {
|
||||||
"unknown group 732 type $internalType, data: " + readBytes().toUHexString()
|
"unknown group 732 type $internalType, data: " + readBytes().toUHexString()
|
||||||
@ -514,7 +511,7 @@ internal class OnlinePush {
|
|||||||
?.let { processor -> processor(notifyMsgBody, bot) }
|
?.let { processor -> processor(notifyMsgBody, bot) }
|
||||||
?: kotlin.run {
|
?: kotlin.run {
|
||||||
bot.network.logger.debug {
|
bot.network.logger.debug {
|
||||||
"unknown group 528 type ${notifyMsgBody.uSubMsgType}, data: " + notifyMsgBody.vProtobuf.toUHexString()
|
"unknown group 528 type 0x${notifyMsgBody.uSubMsgType.toUHexString("")}, data: " + notifyMsgBody.vProtobuf.toUHexString()
|
||||||
}
|
}
|
||||||
return@deco emptySequence()
|
return@deco emptySequence()
|
||||||
}
|
}
|
||||||
@ -573,3 +570,15 @@ internal class OnlinePush {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun <E> LockFreeLinkedList<E>.removeUntilFirst(block: (E) -> Boolean): E? {
|
||||||
|
this.forEach {
|
||||||
|
if (!block(it)) {
|
||||||
|
this.remove(it)
|
||||||
|
} else {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -300,7 +300,7 @@ internal class WtLogin {
|
|||||||
|
|
||||||
class SMSVerifyCodeNeeded(val t402: ByteArray, val t403: ByteArray) : LoginPacketResponse() {
|
class SMSVerifyCodeNeeded(val t402: ByteArray, val t403: ByteArray) : LoginPacketResponse() {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "LoginPacketResponse.SMSVerifyCodeNeeded"
|
return "LoginPacketResponse.SMSVerifyCodeNeeded(t402=${t402.toUHexString()}, t403=${t403.toUHexString()})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.utils
|
package net.mamoe.mirai.qqandroid.utils
|
||||||
|
|
||||||
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
|
import net.mamoe.mirai.utils.debug
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.reflect.KProperty1
|
import kotlin.reflect.KProperty1
|
||||||
@ -26,6 +28,12 @@ private fun <T> Sequence<T>.joinToStringPrefixed(prefix: String, transform: (T)
|
|||||||
return this.joinToString(prefix = "$prefix$indent", separator = "\n$prefix$indent", transform = transform)
|
return this.joinToString(prefix = "$prefix$indent", separator = "\n$prefix$indent", transform = transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val SoutvLogger by lazy { DefaultLogger("soutv") }
|
||||||
|
internal fun Any?.soutv(name: String) {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
SoutvLogger.debug { "$name = ${this._miraiContentToString()}" }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将内容格式化为较可读的字符串输出.
|
* 将内容格式化为较可读的字符串输出.
|
||||||
*
|
*
|
||||||
|
@ -31,7 +31,7 @@ import kotlin.jvm.JvmSynthetic
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ] 和 [群][Group].
|
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ], [群成员][Member] 和 [群][Group].
|
||||||
*
|
*
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/ // 不要删除多平台结构 !!! kotlin bug
|
*/ // 不要删除多平台结构 !!! kotlin bug
|
||||||
|
@ -61,7 +61,7 @@ expect abstract class Group() : Contact, CoroutineScope {
|
|||||||
abstract val owner: Member
|
abstract val owner: Member
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [Bot] 在群内的 [newMember] 实例
|
* [Bot] 在群内的 [Member] 实例
|
||||||
*/
|
*/
|
||||||
@MiraiExperimentalAPI
|
@MiraiExperimentalAPI
|
||||||
abstract val botAsMember: Member
|
abstract val botAsMember: Member
|
||||||
|
@ -551,7 +551,7 @@ data class NewFriendRequestEvent(
|
|||||||
*/
|
*/
|
||||||
val eventId: Long,
|
val eventId: Long,
|
||||||
/**
|
/**
|
||||||
* 入群申请消息
|
* 申请好友消息
|
||||||
*/
|
*/
|
||||||
val message: String,
|
val message: String,
|
||||||
/**
|
/**
|
||||||
@ -594,7 +594,7 @@ data class NewFriendRequestEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 机器人被邀请加入群
|
* 一个账号请求加入群事件
|
||||||
*/
|
*/
|
||||||
@SinceMirai("0.35.0")
|
@SinceMirai("0.35.0")
|
||||||
data class MemberJoinRequestEvent(
|
data class MemberJoinRequestEvent(
|
||||||
|
@ -164,6 +164,7 @@ sealed class OnlineMessageSource : MessageSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract override val target: Member
|
abstract override val target: Member
|
||||||
|
val group: Group get() = target.group
|
||||||
final override val subject: Member get() = target
|
final override val subject: Member get() = target
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,6 +212,7 @@ sealed class OnlineMessageSource : MessageSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract override val sender: Member
|
abstract override val sender: Member
|
||||||
|
val group: Group get() = sender.group
|
||||||
final override val subject: Member get() = sender
|
final override val subject: Member get() = sender
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
package net.mamoe.mirai.network
|
package net.mamoe.mirai.network
|
||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在 [登录][Bot.login] 失败时抛出, 可正常地中断登录过程.
|
* 在 [登录][Bot.login] 失败时抛出, 可正常地中断登录过程.
|
||||||
@ -28,6 +29,12 @@ sealed class LoginFailedException : RuntimeException {
|
|||||||
*/
|
*/
|
||||||
class WrongPasswordException(message: String?) : LoginFailedException(message)
|
class WrongPasswordException(message: String?) : LoginFailedException(message)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需要短信验证时抛出. mirai 目前还不支持短信验证.
|
||||||
|
*/
|
||||||
|
@MiraiExperimentalAPI
|
||||||
|
class UnsupportedSMSLoginException(message: String?) : LoginFailedException(message)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 非 mirai 实现的异常
|
* 非 mirai 实现的异常
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user