mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-24 23:20:09 +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/
|
||||
|
||||
token.txt
|
||||
bintray.user.txt
|
||||
bintray.key.txt
|
@ -29,8 +29,7 @@ object Versions {
|
||||
}
|
||||
|
||||
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
|
||||
// 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 {
|
||||
|
@ -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
|
||||
),
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()})"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()}" }
|
||||
}
|
||||
|
||||
/**
|
||||
* 将内容格式化为较可读的字符串输出.
|
||||
*
|
||||
|
@ -31,7 +31,7 @@ import kotlin.jvm.JvmSynthetic
|
||||
|
||||
|
||||
/**
|
||||
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ] 和 [群][Group].
|
||||
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ], [群成员][Member] 和 [群][Group].
|
||||
*
|
||||
* @author Him188moe
|
||||
*/ // 不要删除多平台结构 !!! kotlin bug
|
||||
|
@ -61,7 +61,7 @@ expect abstract class Group() : Contact, CoroutineScope {
|
||||
abstract val owner: Member
|
||||
|
||||
/**
|
||||
* [Bot] 在群内的 [newMember] 实例
|
||||
* [Bot] 在群内的 [Member] 实例
|
||||
*/
|
||||
@MiraiExperimentalAPI
|
||||
abstract val botAsMember: Member
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 实现的异常
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user