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/ /plugins/
token.txt token.txt
bintray.user.txt
bintray.key.txt

View File

@ -29,8 +29,7 @@ object Versions {
} }
object Publishing { 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 // 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 {

View File

@ -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
), ),

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.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)
}
}

View File

@ -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) {

View File

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

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.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())
) )
) )
} }

View File

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

View File

@ -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()})"
} }
} }

View File

@ -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()}" }
}
/** /**
* 将内容格式化为较可读的字符串输出. * 将内容格式化为较可读的字符串输出.
* *

View File

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

View File

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

View File

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

View File

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

View File

@ -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 实现的异常
*/ */