mirror of
https://github.com/mamoe/mirai.git
synced 2024-12-27 09:00:15 +08:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
8077a52b97
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user