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:
ryoii 2020-04-11 21:24:35 +08:00
commit cd0b27e63d
19 changed files with 302 additions and 188 deletions

50
.github/workflows/bintray.yml vendored Normal file
View 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
View File

@ -44,3 +44,5 @@ keys.properties
/plugins/
token.txt
bintray.user.txt
bintray.key.txt

View File

@ -29,8 +29,7 @@ object Versions {
}
object Publishing {
const val bintray = "1.8.4-jetbrains-3"
const val bintray = "1.8.5"
}
}

View 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'"
)
}
}

View File

@ -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
}
}
}

View File

@ -1,6 +1,21 @@
import upload.Bintray
// 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 = {
licenses {
license {
@ -22,12 +37,8 @@ def pomConfig = {
}
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")
user = Bintray.getUser(project)
key = Bintray.getKey(project)
pkg {
repo = 'mirai'
@ -67,14 +78,14 @@ bintrayUpload.dependsOn {
list
}
try{
try {
// empty xxx-javadoc.jar
task javadocJar(type: Jar) {
archiveClassifier = 'javadoc'
}
} catch (Exception e){
} catch (Exception ignored) {
}
publishing {

View File

@ -86,7 +86,7 @@ internal class MessageSourceFromFriendImpl(
type = 0,
time = msg.msgHead.msgTime,
pbReserve = SourceMsg.ResvAttr(
origUids = id.toULong().toLong()
origUids = id.toLong() and 0xFFFF_FFFF
).toByteArray(SourceMsg.ResvAttr.serializer()),
srcMsg = MsgComm.Msg(
msgHead = MsgComm.MsgHead(
@ -96,7 +96,7 @@ internal class MessageSourceFromFriendImpl(
c2cCmd = msg.msgHead.c2cCmd,
msgSeq = msg.msgHead.msgSeq,
msgTime = msg.msgHead.msgTime,
msgUid = id.toULong().toLong(), // ok
msgUid = id.toLong() and 0xFFFF_FFFF, // ok
// groupInfo = MsgComm.GroupInfo(groupCode = msg.msgHead.groupInfo.groupCode),
isSrcMsg = true
),
@ -151,7 +151,7 @@ internal class MessageSourceFromTempImpl(
type = 0,
time = msg.msgHead.msgTime,
pbReserve = SourceMsg.ResvAttr(
origUids = id.toULong().toLong()
origUids = id.toLong() and 0xFFFF_FFFF
).toByteArray(SourceMsg.ResvAttr.serializer()),
srcMsg = MsgComm.Msg(
msgHead = MsgComm.MsgHead(
@ -161,7 +161,7 @@ internal class MessageSourceFromTempImpl(
c2cCmd = msg.msgHead.c2cCmd,
msgSeq = msg.msgHead.msgSeq,
msgTime = msg.msgHead.msgTime,
msgUid = id.toULong().toLong(), // ok
msgUid = id.toLong() and 0xFFFF_FFFF, // ok
// groupInfo = MsgComm.GroupInfo(groupCode = msg.msgHead.groupInfo.groupCode),
isSrcMsg = true
),

View File

@ -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.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import net.mamoe.mirai.qqandroid.utils.encodeToString
import net.mamoe.mirai.qqandroid.utils.hexToBytes
import net.mamoe.mirai.qqandroid.utils.*
import net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs
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.MiraiInternalAPI
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.debug
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
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()
@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
return buildMessageChain(elements.size + 1) {
@ -251,43 +254,40 @@ internal inline fun <reified R> Iterable<*>.firstIsInstanceOrNull(): R? {
@OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: Bot, message: MessageChainBuilder) {
// (this._miraiContentToString())
this.forEach {
this.forEach { element ->
when {
it.srcMsg != null -> message.add(
QuoteReply(
OfflineMessageSourceImplBySourceMsg(
it.srcMsg,
bot,
groupIdOrZero
)
)
)
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())
element.srcMsg != null ->
message.add(QuoteReply(OfflineMessageSourceImplBySourceMsg(element.srcMsg, bot, groupIdOrZero)))
element.notOnlineImage != null -> message.add(OnlineFriendImageImpl(element.notOnlineImage))
element.customFace != null -> message.add(OnlineGroupImageImpl(element.customFace))
element.face != null -> message.add(Face(element.face.index))
element.text != null -> {
if (element.text.attr6Buf.isEmpty()) {
message.add(element.text.str.toMessage())
} else {
val id: Long
it.text.attr6Buf.read {
element.text.attr6Buf.read {
discardExact(7)
id = readUInt().toLong()
}
if (id == 0L) {
message.add(AtAll)
} else {
message.add(At._lowLevelConstructAtInstance(id, it.text.str))
message.add(At._lowLevelConstructAtInstance(id, element.text.str))
}
}
}
it.lightApp != null -> {
val content = MiraiPlatformUtils.unzip(it.lightApp.data, 1).encodeToString()
element.lightApp != null -> {
val content = runWithBugReport("解析 lightApp", { element.lightApp.data.toUHexString() }) {
MiraiPlatformUtils.unzip(element.lightApp.data, 1).encodeToString()
}
message.add(LightApp(content))
}
it.richMsg != null -> {
val content = MiraiPlatformUtils.unzip(it.richMsg.template1, 1).encodeToString()
when (it.richMsg.serviceId) {
element.richMsg != null -> {
val content = runWithBugReport("解析 richMsg", { element.richMsg.template1.toUHexString() }) {
MiraiPlatformUtils.unzip(element.richMsg.template1, 1).encodeToString()
}
when (element.richMsg.serviceId) {
1 -> message.add(JsonMessage(content))
60 -> message.add(XmlMessage(content))
35 -> {
@ -373,26 +373,24 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
}
}
else -> {
@Suppress("DEPRECATION")
MiraiLogger.debug {
"unknown richMsg.serviceId: ${it.richMsg.serviceId}, content=${it.richMsg.template1.contentToString()}, \ntryUnzip=${content}"
throw contextualBugReportException("richMsg.serviceId",
"richMsg.serviceId: ${element.richMsg.serviceId}, content=${element.richMsg.template1.contentToString()}, \n" + "tryUnzip=${content}")
}
}
}
}
it.elemFlags2 != null
|| it.extraInfo != null
|| it.generalFlags != null -> {
element.elemFlags2 != null
|| element.extraInfo != null
|| element.generalFlags != null -> {
}
it.commonElem != null -> {
when (it.commonElem.serviceType) {
element.commonElem != null -> {
when (element.commonElem.serviceType) {
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))
}
3 -> {
val proto = it.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer())
val proto = element.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer())
if (proto.flashTroopPic != null) {
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)
}
}

View File

@ -23,6 +23,7 @@ import net.mamoe.mirai.event.events.BotOnlineEvent
import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.UnsupportedSMSLoginException
import net.mamoe.mirai.network.WrongPasswordException
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.contact.FriendInfoImpl
@ -105,6 +106,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}.also { heartbeatJob = it }
}
@OptIn(MiraiExperimentalAPI::class)
override suspend fun relogin(cause: Throwable?) {
heartbeatJob?.cancel(CancellationException("relogin", cause))
heartbeatJob?.join()
@ -163,9 +165,15 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
is WtLogin.Login.LoginPacketResponse.Success -> {
logger.info("Login successful")
logger.info { "Login successful" }
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>()
@PublishedApi
internal inner class PacketListener( // callback
internal inner class PacketListener(
// callback
val commandName: String,
val sequenceId: Int
) : CompletableDeferred<Packet?> by CompletableDeferred(supervisor) {

View File

@ -131,6 +131,7 @@ internal open class QQAndroidClient(
internal fun nextHighwayDataTransSequenceIdForApplyUp(): Int = highwayDataTransSequenceIdForApplyUp.getAndAdd(2)
internal val onlinePushCacheList: LockFreeLinkedList<Short> = LockFreeLinkedList()
internal val pbPushTransMsgCacheList: LockFreeLinkedList<Int> = LockFreeLinkedList()
val appClientVersion: Int = 0

View File

@ -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.NewContact
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.readProtoBuf
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>) :
MultiPacketByIterable<Packet>(delegate) {
override fun toString(): String =
"MessageSvc.PbGetMsg.Response($syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))"
"MessageSvc.PbGetMsg.Response(syncFlagFromServer=$syncFlagFromServer, messages=<Iterable>))"
}
object EmptyResponse : GetMsgSuccess(emptyList())
@ -370,12 +369,13 @@ internal class MessageSvc {
message: MessageChain,
crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
): OutgoingPacket {
val rand = Random.nextInt().absoluteValue
val source = MessageSourceToFriendImpl(
id = Random.nextInt().absoluteValue,
id = rand,
sender = client.bot,
target = qq,
time = currentTimeSeconds.toInt(),
sequenceId = client.atomicNextMessageSequenceId(),
sequenceId = rand,
originalMessage = message
)
sourceCallback(source)
@ -406,7 +406,7 @@ internal class MessageSvc {
),
msgSeq = source.sequenceId,
msgRand = source.id,
syncCookie = SyncCookie(time = source.time.toULong().toLong()).toByteArray(SyncCookie.serializer())
syncCookie = SyncCookie(time = source.time.toLong()).toByteArray(SyncCookie.serializer())
// msgVia = 1
)
)
@ -454,7 +454,7 @@ internal class MessageSvc {
),
msgSeq = source.sequenceId,
msgRand = source.id,
syncCookie = SyncCookie(time = source.time.toULong().toLong()).toByteArray(SyncCookie.serializer())
syncCookie = SyncCookie(time = source.time.toLong()).toByteArray(SyncCookie.serializer())
)
)
}

View File

@ -123,6 +123,14 @@ internal class OnlinePush {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? {
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> {
when (content.msgType) {
44 -> {
@ -237,17 +245,6 @@ internal class OnlinePush {
}.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>):
ByteReadPacket.(group: GroupImpl, bot: QQAndroidBot) -> Sequence<Packet> {
return block
@ -497,7 +494,7 @@ internal class OnlinePush {
discardExact(1)
Transformers732[internalType]
?.let { it(this@deco, group as GroupImpl, bot) }
?.let { it(this@deco, group, bot) }
?: kotlin.run {
bot.network.logger.debug {
"unknown group 732 type $internalType, data: " + readBytes().toUHexString()
@ -514,7 +511,7 @@ internal class OnlinePush {
?.let { processor -> processor(notifyMsgBody, bot) }
?: kotlin.run {
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()
}
@ -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
}

View File

@ -300,7 +300,7 @@ internal class WtLogin {
class SMSVerifyCodeNeeded(val t402: ByteArray, val t403: ByteArray) : LoginPacketResponse() {
override fun toString(): String {
return "LoginPacketResponse.SMSVerifyCodeNeeded"
return "LoginPacketResponse.SMSVerifyCodeNeeded(t402=${t402.toUHexString()}, t403=${t403.toUHexString()})"
}
}

View File

@ -11,6 +11,8 @@
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.KProperty
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)
}
private val SoutvLogger by lazy { DefaultLogger("soutv") }
internal fun Any?.soutv(name: String) {
@Suppress("DEPRECATION")
SoutvLogger.debug { "$name = ${this._miraiContentToString()}" }
}
/**
* 将内容格式化为较可读的字符串输出.
*

View File

@ -31,7 +31,7 @@ import kotlin.jvm.JvmSynthetic
/**
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ] [][Group].
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ], [群成员][Member] [][Group].
*
* @author Him188moe
*/ // 不要删除多平台结构 !!! kotlin bug

View File

@ -61,7 +61,7 @@ expect abstract class Group() : Contact, CoroutineScope {
abstract val owner: Member
/**
* [Bot] 在群内的 [newMember] 实例
* [Bot] 在群内的 [Member] 实例
*/
@MiraiExperimentalAPI
abstract val botAsMember: Member

View File

@ -551,7 +551,7 @@ data class NewFriendRequestEvent(
*/
val eventId: Long,
/**
* 入群申请消息
* 申请好友消息
*/
val message: String,
/**
@ -594,7 +594,7 @@ data class NewFriendRequestEvent(
}
/**
* 机器人被邀请加入群
* 一个账号请求加入群事件
*/
@SinceMirai("0.35.0")
data class MemberJoinRequestEvent(

View File

@ -164,6 +164,7 @@ sealed class OnlineMessageSource : MessageSource() {
}
abstract override val target: Member
val group: Group get() = target.group
final override val subject: Member get() = target
}
@ -211,6 +212,7 @@ sealed class OnlineMessageSource : MessageSource() {
}
abstract override val sender: Member
val group: Group get() = sender.group
final override val subject: Member get() = sender
}

View File

@ -12,6 +12,7 @@
package net.mamoe.mirai.network
import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.MiraiExperimentalAPI
/**
* [登录][Bot.login] 失败时抛出, 可正常地中断登录过程.
@ -28,6 +29,12 @@ sealed class LoginFailedException : RuntimeException {
*/
class WrongPasswordException(message: String?) : LoginFailedException(message)
/**
* 需要短信验证时抛出. mirai 目前还不支持短信验证.
*/
@MiraiExperimentalAPI
class UnsupportedSMSLoginException(message: String?) : LoginFailedException(message)
/**
* mirai 实现的异常
*/