Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Him188 2020-12-19 22:02:13 +08:00
commit 8077a52b97
10 changed files with 179 additions and 90 deletions

View File

@ -2,7 +2,7 @@
本文介绍如何在一个项目中使用 mirai。
mirai 使用纯 Kotlin 开发,最低要求 `JDK 1.8``Kotlin 1.4`。
mirai 使用纯 Kotlin 开发,最低要求 `JDK 1.8``Kotlin 1.4`。但注意不要使用 Oracle JDK推荐使用 OpenJDK。
如果你熟悉 Gradle只需要添加 `jcenter` 仓库和依赖 `net.mamoe:mirai-core:VERSION` 即可而不需要继续阅读。下文将详细解释其他方法。
@ -128,4 +128,4 @@ dependencies {
5. 确认并等待下载
> [回到 Mirai 文档索引](README.md)
> [回到 Mirai 文档索引](README.md)

View File

@ -14,6 +14,7 @@
package net.mamoe.mirai.event
import kotlinx.coroutines.*
import net.mamoe.mirai.utils.EventListenerLikeJava
import java.lang.reflect.Method
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@ -238,6 +239,23 @@ public fun CoroutineScope.registerEvents(
}
}
private fun Method.isKotlinFunction(): Boolean {
if (getDeclaredAnnotation(EventListenerLikeJava::class.java) != null) return false
if (declaringClass.getDeclaredAnnotation(EventListenerLikeJava::class.java) != null) return false
@Suppress("RemoveRedundantQualifierName") // for strict
return declaringClass.getDeclaredAnnotation(kotlin.Metadata::class.java) != null
}
private fun Method.invokeWithErrorReport(self: Any?, vararg args: Any?): Any? = try {
invoke(self, *args)
} catch (exception: IllegalArgumentException) {
throw IllegalArgumentException(
"Internal Error: $exception, method=${this}, this=$self, arguments=$args, please report to https://github.com/mamoe/mirai",
exception
)
}
@Suppress("UNCHECKED_CAST")
private fun Method.registerEvent(
@ -248,7 +266,7 @@ private fun Method.registerEvent(
): Listener<Event> {
this.isAccessible = true
val kotlinFunction = kotlin.runCatching { this.kotlinFunction }.getOrNull()
return if (kotlinFunction != null) {
return if (kotlinFunction != null && isKotlinFunction()) {
// kotlin functions
val param = kotlinFunction.parameters
@ -337,7 +355,7 @@ private fun Method.registerEvent(
val paramType = this.parameters[0].type
check(this.parameterCount == 1 && Event::class.java.isAssignableFrom(paramType)) {
"Illegal method parameter. Required one exact Event subclass. found $paramType"
"Illegal method parameter. Required one exact Event subclass. found ${this.parameters.contentToString()}"
}
when (this.returnType) {
Void::class.java, Void.TYPE, Nothing::class.java -> {
@ -350,11 +368,11 @@ private fun Method.registerEvent(
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this)
this@registerEvent.invokeWithErrorReport(owner, this@subscribeAlways)
}
}
} else withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this)
this@registerEvent.invokeWithErrorReport(owner, this@subscribeAlways)
}
}
}
@ -368,11 +386,11 @@ private fun Method.registerEvent(
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this) as ListeningStatus
this@registerEvent.invokeWithErrorReport(owner, this@subscribe) as ListeningStatus
}
} else ListeningStatus.LISTENING
} else withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this) as ListeningStatus
this@registerEvent.invokeWithErrorReport(owner, this@subscribe) as ListeningStatus
}
}

View File

@ -60,4 +60,12 @@ internal annotation class PlannedRemoval(val version: String)
@PlannedRemoval("2.0-M2")
internal annotation class MemberDeprecatedApi(val message: String)
/**
* 该注解仅用于测试 EventHandler
*
* 标注了此注解的意为像处理 java 方法那样处理 kotlin 方法
*/
@Retention(AnnotationRetention.RUNTIME)
internal annotation class EventListenerLikeJava

View File

@ -208,24 +208,22 @@ public open class BotConfiguration { // open for Java
}
@Suppress("ACTUAL_WITHOUT_EXPECT")
public enum class MiraiProtocol constructor(
/** 协议模块使用的 ID */
@JvmField internal val id: Long
) {
public enum class MiraiProtocol {
/**
* Android 手机.
*/
ANDROID_PHONE(537066439),
ANDROID_PHONE,
/**
* Android 平板.
*/
ANDROID_PAD(537062409),
ANDROID_PAD,
/**
* Android 手表.
* */
ANDROID_WATCH(537061176)
ANDROID_WATCH,
}
public companion object {

View File

@ -1,61 +0,0 @@
/*
* Copyright 2019-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
*/
package net.mamoe.mirai.event;
import org.junit.jupiter.api.Test;
import java.util.concurrent.atomic.AtomicInteger;
import static kotlin.test.AssertionsKt.assertEquals;
public class JvmMethodEventsTestJava extends SimpleListenerHost {
private final AtomicInteger called = new AtomicInteger(0);
@EventHandler
public void ev(TestEvent event) {
called.incrementAndGet();
}
@EventHandler
public Void ev2(TestEvent event) {
called.incrementAndGet();
return null;
}
@EventHandler
public ListeningStatus ev3(TestEvent event) {
called.incrementAndGet();
return ListeningStatus.LISTENING;
}
@EventHandler
public void ev(TestEvent event, TestEvent event2) {
called.incrementAndGet();
}
@EventHandler
public Void ev2(TestEvent event, TestEvent event2) {
called.incrementAndGet();
return null;
}
@EventHandler
public ListeningStatus ev3(TestEvent event, TestEvent event2) {
called.incrementAndGet();
return ListeningStatus.LISTENING;
}
@Test
public void test() {
Events.registerEvents(this);
EventKt.broadcast(new TestEvent());
assertEquals(6, called.get(), null);
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2019-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
*/
package net.mamoe.mirai.event
import net.mamoe.mirai.JavaFriendlyAPI
import net.mamoe.mirai.utils.EventListenerLikeJava
import org.junit.jupiter.api.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertEquals
@EventListenerLikeJava
@JavaFriendlyAPI
internal class JvmMethodEventsTestJava : SimpleListenerHost() {
private val called = AtomicInteger(0)
@EventHandler
fun ev(event: TestEvent?) {
called.incrementAndGet()
}
@EventHandler
fun ev2(event: TestEvent?): Void? {
called.incrementAndGet()
return null
}
@EventHandler
fun ev3(event: TestEvent?): ListeningStatus? {
called.incrementAndGet()
return ListeningStatus.LISTENING
}
@Test
fun test() {
this.registerEvents()
TestEvent().__broadcastJava()
assertEquals(3, called.get(), null)
}
}

View File

@ -76,9 +76,10 @@ internal open class QQAndroidClient(
val device: DeviceInfo,
bot: QQAndroidBot
) {
@Suppress("INVISIBLE_MEMBER")
val protocol = MiraiProtocolInternal[bot.configuration.protocol]
val subAppId: Long
get() = bot.configuration.protocol.id
get() = protocol.id
internal val serverList: MutableList<Pair<String, Int>> = DefaultServerList.toMutableList()
@ -118,9 +119,10 @@ internal open class QQAndroidClient(
var tgtgtKey: ByteArray = generateTgtgtKey(device.guid)
val randomKey: ByteArray = getRandomByteArray(16)
var miscBitMap: Int = 184024956 // 也可能是 150470524 ?
private var mainSigMap: Int = 16724722
var subSigMap: Int = 0x10400 //=66,560
val miscBitMap: Int get() = protocol.miscBitMap // 184024956 // 也可能是 150470524 ?
private val mainSigMap: Int = protocol.mainSigMap
var subSigMap: Int = protocol.subSigMap // 0x10400 //=66,560
private val _ssoSequenceId: AtomicInt = atomic(85600)
@ -157,9 +159,12 @@ internal open class QQAndroidClient(
var openAppId: Long = 715019303L
val apkVersionName: ByteArray get() = "8.4.18".toByteArray()
val apkVersionName: ByteArray get() = protocol.ver.toByteArray() //"8.4.18".toByteArray()
val buildVer: String get() = "8.4.18.4810" // 8.2.0.1296 // 8.4.8.4810 // 8.2.7.4410
val buildTime: Long get() = protocol.buildTime
val sdkVersion: String get() = protocol.sdkVer
private val messageSequenceId: AtomicInt = atomic(22911)
internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2)
@ -194,7 +199,7 @@ internal open class QQAndroidClient(
var networkType: NetworkType = NetworkType.WIFI
val apkSignatureMd5: ByteArray = "A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D".hexToBytes()
val apkSignatureMd5: ByteArray get() = protocol.sign.hexToBytes() // "A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D".hexToBytes()
/**
* 协议版本?, 8.2.7 的为 8001

View File

@ -120,7 +120,7 @@ internal fun BytePacketBuilder.t106(
writeByte(isSavePassword.toByte())
writeFully(passwordMd5)
writeFully(tgtgtKey)
writeInt(0)
writeInt(0) // wtf
writeByte(isGuidAvailable.toByte())
if (isGuidAvailable) {
require(guid != null) { "Guid must not be null when isGuidAvailable==true" }
@ -193,7 +193,7 @@ internal fun BytePacketBuilder.t107(
internal fun BytePacketBuilder.t108(
ksid: ByteArray
) {
require(ksid.size == 16) { "ksid should length 16" }
// require(ksid.size == 16) { "ksid should length 16" }
writeShort(0x108)
writeShortLVPacket {
writeFully(ksid)

View File

@ -128,7 +128,7 @@ internal class WtLogin {
writeSsoPacket(client, client.subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(9) // subCommand
writeShort(17) // count of TLVs, probably ignored by server?
writeShort(0x18) // count of TLVs, probably ignored by server?
//writeShort(LoginType.PASSWORD.value.toShort())
t18(appId, client.appClientVersion, client.uin)
@ -161,7 +161,8 @@ internal class WtLogin {
*/
t116(client.miscBitMap, client.subSigMap)
t100(appId, client.subAppId, client.appClientVersion, client.ssoVersion)
t107(6)
t107(0)
t108(client.device.imei.toByteArray())
// t108(byteArrayOf())
// ignored: t104()
@ -192,9 +193,11 @@ internal class WtLogin {
t145(client.device.guid)
t147(appId, client.apkVersionName, client.apkSignatureMd5)
/*
if (client.miscBitMap and 0x80 != 0) {
t166(1)
}
*/
// ignored t16a because array5 is null
@ -210,14 +213,14 @@ internal class WtLogin {
"connect.qq.com",
"qzone.qq.com",
"vip.qq.com",
"gamecenter.qq.com",
"qun.qq.com",
"game.qq.com",
"qqweb.qq.com",
"office.qq.com",
"ti.qq.com",
"mail.qq.com",
"qzone.com",
"mma.qq.com"
"mma.qq.com",
)
)
@ -243,7 +246,10 @@ internal class WtLogin {
t202(bssid, ssid)
}
t177()
t177(
buildTime = client.buildTime,
buildVersion = client.sdkVersion,
)
t516()
t521()

View File

@ -0,0 +1,69 @@
/*
* Copyright 2019-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
*/
package net.mamoe.mirai.internal.utils
import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol
import java.util.*
internal class MiraiProtocolInternal(
@JvmField internal val apkId: String,
@JvmField internal val id: Long,
@JvmField internal val ver: String,
@JvmField internal val sdkVer: String,
@JvmField internal val miscBitMap: Int,
@JvmField internal val subSigMap: Int,
@JvmField internal val mainSigMap: Int,
@JvmField internal val sign: String,
@JvmField internal val buildTime: Long,
) {
internal companion object {
internal val protocols = EnumMap<MiraiProtocol, MiraiProtocolInternal>(
MiraiProtocol::class.java
)
operator fun get(protocol: MiraiProtocol): MiraiProtocolInternal =
protocols[protocol] ?: error("Internal Error: Missing protocol $protocol")
init {
protocols[MiraiProtocol.ANDROID_PHONE] = MiraiProtocolInternal(
"com.tencent.mobileqq",
537066419,
"8.4.18",
"6.0.0.2454",
184024956,
0x10400,
34869472,
"A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D",
1604580615L,
)
protocols[MiraiProtocol.ANDROID_PAD] = MiraiProtocolInternal(
"com.tencent.mobileqq",
537062409, "8.4.18",
"6.0.0.2454",
184024956,
0x10400,
34869472,
"A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D",
1604580615L,
)
protocols[MiraiProtocol.ANDROID_WATCH] = MiraiProtocolInternal(
"com.tencent.mobileqq",
537061176,
"8.2.7",
"6.0.0.2413",
184024956,
0x10400,
34869472,
"A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D",
1571193922L
)
}
}
}