Commit 71d5dda2 authored by Him188's avatar Him188

ByteArrayPool

parent b9129576
...@@ -34,4 +34,7 @@ mirai.iml ...@@ -34,4 +34,7 @@ mirai.iml
/test /test
.gradle/ .gradle/
\ No newline at end of file
local.properties
\ No newline at end of file
...@@ -4,11 +4,12 @@ buildscript { ...@@ -4,11 +4,12 @@ buildscript {
repositories { repositories {
mavenLocal() mavenLocal()
jcenter() jcenter()
mavenCentral() google()
maven { url "https://mirrors.huaweicloud.com/repository/maven/" } maven { url "https://mirrors.huaweicloud.com/repository/maven/" }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version" classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version"
} }
...@@ -20,8 +21,8 @@ allprojects { ...@@ -20,8 +21,8 @@ allprojects {
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral()
jcenter() jcenter()
google()
maven { url "https://mirrors.huaweicloud.com/repository/maven/" } maven { url "https://mirrors.huaweicloud.com/repository/maven/" }
} }
} }
\ No newline at end of file
apply plugin: 'kotlinx-atomicfu'
apply plugin: "kotlin-multiplatform"
kotlin { kotlin {
targets { targets {
fromPreset(presets.jvm, "jvm") fromPreset(presets.jvm, "jvm")
//fromPreset(presets.jvm, "android")
//fromPreset(presets.mingwX64, "mingwX64") //fromPreset(presets.mingwX64, "mingwX64")
} }
jvm{ jvm{
...@@ -35,6 +33,7 @@ kotlin { ...@@ -35,6 +33,7 @@ kotlin {
implementation "com.soywiz.korlibs.klock:klock:$klock_version" implementation "com.soywiz.korlibs.klock:klock:$klock_version"
api group: 'io.ktor', name: 'ktor-client-core', version: ktor_version api group: 'io.ktor', name: 'ktor-client-core', version: ktor_version
api group: 'io.ktor', name: 'ktor-network', version: ktor_version
//api group: 'io.ktor', name: 'ktor-client-cio', version: ktor_version //api group: 'io.ktor', name: 'ktor-client-cio', version: ktor_version
//api group: 'io.ktor', name: 'ktor-client', version: ktor_version //api group: 'io.ktor', name: 'ktor-client', version: ktor_version
api group: 'io.ktor', name: 'ktor-http', version: ktor_version api group: 'io.ktor', name: 'ktor-http', version: ktor_version
...@@ -87,6 +86,28 @@ kotlin { ...@@ -87,6 +86,28 @@ kotlin {
apply plugin: 'java' apply plugin: 'java'
} }
androidMain{
dependencies{
api 'com.google.android:android:4.1.1.4'
api 'com.android.support:support-annotations:26.1.0'
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_version
api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
api group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutines_version
api group: 'org.jetbrains.kotlinx', name: 'atomicfu', version: atomicfu_version
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io', version: kotlinxio_version
// api group: 'org.jetbrains.kotlinx', name: 'kotlinx-io-jvm', version: kotlinxio_version
api group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-io', version: coroutinesio_version
api group: 'io.ktor', name: 'ktor-http-cio', version: ktor_version
api group: 'io.ktor', name: 'ktor-http', version: ktor_version
api group: 'io.ktor', name: 'ktor-client-core-jvm', version: ktor_version
api group: 'io.ktor', name: 'ktor-client-cio', version: ktor_version
}
}
all { all {
languageSettings.enableLanguageFeature("InlineClasses") languageSettings.enableLanguageFeature("InlineClasses")
} }
......
plugins {
id("kotlinx-atomicfu")
kotlin("multiplatform")
//id("com.android.library")
//id("kotlin-android-extensions")
}
val kotlinVersion = rootProject.ext["kotlin_version"].toString()
val atomicFuVersion = rootProject.ext["atomicfu_version"].toString()
val coroutinesVersion = rootProject.ext["coroutines_version"].toString()
val kotlinXIoVersion = rootProject.ext["kotlinxio_version"].toString()
val coroutinesIoVersion = rootProject.ext["coroutinesio_version"].toString()
val klockVersion = rootProject.ext["klock_version"].toString()
val ktorVersion = rootProject.ext["ktor_version"].toString()
/*
//apply()
android {
compileSdkVersion(29)
buildToolsVersion("29.0.2")
defaultConfig {
applicationId = "com.youngfeng.kotlindsl"
minSdkVersion(15)
targetSdkVersion(27)
versionCode = 1
versionName = "1.0"
// testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
//proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
}
sourceSets.forEach {
println(it)
// it.languageSettings.enableLanguageFeature("InlineClasses")
}
}
*/
kotlin {
// android("android")
jvm("jvm")
sourceSets["commonMain"].apply {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
implementation("com.soywiz.korlibs.klock:klock:$klockVersion")
api("io.ktor:ktor-client-core:$ktorVersion")
api("io.ktor:ktor-network:$ktorVersion")
api("io.ktor:ktor-http:$ktorVersion")
}
}
/*
sourceSets["androidMain"].apply {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
implementation("io.ktor:ktor-http-cio:$ktorVersion")
implementation("io.ktor:ktor-http:$ktorVersion")
implementation("io.ktor:ktor-client-core-jvm:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
}
languageSettings.enableLanguageFeature("InlineClasses")
}*/
sourceSets["jvmMain"].apply {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
implementation("io.ktor:ktor-http-cio:$ktorVersion")
implementation("io.ktor:ktor-http:$ktorVersion")
implementation("io.ktor:ktor-client-core-jvm:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
}
}
sourceSets.forEach {
it.languageSettings.enableLanguageFeature("InlineClasses")
it.dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
implementation("org.jetbrains.kotlinx:kotlinx-io:$kotlinXIoVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-io:$coroutinesIoVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
}
}
}
...@@ -183,7 +183,6 @@ class MessageSubscribersBuilder<T : SenderAndMessage<*>>( ...@@ -183,7 +183,6 @@ class MessageSubscribersBuilder<T : SenderAndMessage<*>>(
/** /**
* 如果消息的前缀是 [prefix], 就执行 [onEvent] * 如果消息的前缀是 [prefix], 就执行 [onEvent]
* @param
*/ */
suspend fun startsWith(prefix: String, removePrefix: Boolean = false, onEvent: @MessageDsl suspend T.(String) -> Unit) = suspend fun startsWith(prefix: String, removePrefix: Boolean = false, onEvent: @MessageDsl suspend T.(String) -> Unit) =
content({ it.startsWith(prefix) }) { content({ it.startsWith(prefix) }) {
...@@ -258,4 +257,12 @@ class MessageSubscribersBuilder<T : SenderAndMessage<*>>( ...@@ -258,4 +257,12 @@ class MessageSubscribersBuilder<T : SenderAndMessage<*>>(
*/ */
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPE) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPE)
@DslMarker @DslMarker
internal annotation class MessageDsl internal annotation class MessageDsl
\ No newline at end of file
fun main() {
println('B')
println("\u7154225")
println('B' - 'b')
}
\ No newline at end of file
...@@ -5,7 +5,6 @@ package net.mamoe.mirai.message.internal ...@@ -5,7 +5,6 @@ package net.mamoe.mirai.message.internal
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.message.* import net.mamoe.mirai.message.*
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.toUHexString
internal fun IoBuffer.parseMessageFace(): Face { internal fun IoBuffer.parseMessageFace(): Face {
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0 //00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0
...@@ -105,7 +104,7 @@ internal fun ByteReadPacket.readMessage(): Message? { ...@@ -105,7 +104,7 @@ internal fun ByteReadPacket.readMessage(): Message? {
//后面似乎还有一节? //后面似乎还有一节?
//discardExact(7)//02 00 04 00 00 00 23 //discardExact(7)//02 00 04 00 00 00 23
return PlainText(value.toUHexString()) //return PlainText(value.toUHexString())
} }
0x0E -> { 0x0E -> {
......
...@@ -23,8 +23,11 @@ import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket ...@@ -23,8 +23,11 @@ import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.* import net.mamoe.mirai.network.protocol.tim.packet.login.*
import net.mamoe.mirai.network.session import net.mamoe.mirai.network.session
import net.mamoe.mirai.qqAccount import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.BotNetworkConfiguration
import net.mamoe.mirai.utils.OnlineStatus
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.log
import net.mamoe.mirai.utils.solveCaptcha
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
/** /**
...@@ -35,7 +38,9 @@ import kotlin.coroutines.CoroutineContext ...@@ -35,7 +38,9 @@ import kotlin.coroutines.CoroutineContext
internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() { internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() {
override val coroutineContext: CoroutineContext = override val coroutineContext: CoroutineContext =
Dispatchers.Default + CoroutineExceptionHandler { _, e -> bot.logger.log(e) } + SupervisorJob() Dispatchers.Default + CoroutineExceptionHandler { _, e ->
bot.logger.log(e)
} + SupervisorJob()
override lateinit var socket: BotSocketAdapter override lateinit var socket: BotSocketAdapter
...@@ -56,13 +61,14 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -56,13 +61,14 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
override suspend fun login(configuration: BotNetworkConfiguration): LoginResult = withContext(this.coroutineContext) { override suspend fun login(configuration: BotNetworkConfiguration): LoginResult = withContext(this.coroutineContext) {
TIMProtocol.SERVER_IP.forEach { ip -> TIMProtocol.SERVER_IP.forEach { ip ->
bot.logger.logInfo("Connecting server $ip") bot.logger.logPurple("Connecting server $ip")
socket = BotSocketAdapter(ip, configuration) socket = BotSocketAdapter(ip, configuration)
loginResult = CompletableDeferred() loginResult = CompletableDeferred()
socket.resendTouch().takeIf { it != LoginResult.TIMEOUT }?.let { return@withContext it } socket.resendTouch().takeIf { it != LoginResult.TIMEOUT }?.let { return@withContext it }
println()
bot.logger.logPurple("Timeout. Retrying next server") bot.logger.logPurple("Timeout. Retrying next server")
socket.close() socket.close()
...@@ -124,6 +130,9 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -124,6 +130,9 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
try { try {
channel.read(buffer)// JVM: withContext(IO) channel.read(buffer)// JVM: withContext(IO)
} catch (e: ClosedChannelException) {
close()
return
} catch (e: ReadPacketInternalException) { } catch (e: ReadPacketInternalException) {
bot.logger.logError("Socket channel read failed: ${e.message}") bot.logger.logError("Socket channel read failed: ${e.message}")
continue continue
...@@ -133,6 +142,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -133,6 +142,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
continue continue
} finally { } finally {
if (!buffer.canRead() || buffer.readRemaining == 0) {//size==0 if (!buffer.canRead() || buffer.readRemaining == 0) {//size==0
//bot.logger.logDebug("processReceive: Buffer cannot be read")
buffer.release(IoBuffer.Pool) buffer.release(IoBuffer.Pool)
continue continue
}// sometimes exceptions are thrown without this `if` clause }// sometimes exceptions are thrown without this `if` clause
...@@ -153,7 +163,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -153,7 +163,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
} }
} }
internal suspend fun resendTouch(): LoginResult = coroutineScope { internal suspend fun resendTouch(): LoginResult /* = coroutineScope */ {
if (::loginHandler.isInitialized) loginHandler.close() if (::loginHandler.isInitialized) loginHandler.close()
loginHandler = LoginHandler(configuration) loginHandler = LoginHandler(configuration)
...@@ -168,7 +178,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -168,7 +178,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
} }
sendPacket(TouchPacket(bot.qqAccount, serverIp)) sendPacket(TouchPacket(bot.qqAccount, serverIp))
return@coroutineScope loginResult.await() return loginResult.await()
} }
private suspend inline fun <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> { private suspend inline fun <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> {
...@@ -183,7 +193,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -183,7 +193,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
return receiving return receiving
} }
override suspend fun distributePacket(packet: ServerPacket) = coroutineScope { override suspend fun distributePacket(packet: ServerPacket) {
try { try {
packet.decode() packet.decode()
} catch (e: Exception) { } catch (e: Exception) {
...@@ -213,7 +223,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -213,7 +223,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
} }
if (ServerPacketReceivedEvent(bot, packet).broadcast().cancelled) { if (ServerPacketReceivedEvent(bot, packet).broadcast().cancelled) {
return@coroutineScope return
} }
// They should be called in sequence because packet is lock-free // They should be called in sequence because packet is lock-free
...@@ -267,11 +277,12 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : ...@@ -267,11 +277,12 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
return@withContext return@withContext
} }
packet.packet.use { build -> packet.buildAndUse { build ->
val buffer = IoBuffer.Pool.borrow() val buffer = IoBuffer.Pool.borrow()
try { try {
build.readAvailable(buffer) build.readAvailable(buffer)
channel.send(buffer)//JVM: withContext(IO) val shouldBeSent = buffer.readRemaining
check(channel.send(buffer) == shouldBeSent) { "Buffer is not entirely sent. Required sent length=$shouldBeSent, but after channel.send, buffer remains ${buffer.readBytes().toUHexString()}" }//JVM: withContext(IO)
} catch (e: SendPacketInternalException) { } catch (e: SendPacketInternalException) {
bot.logger.logError("Caught SendPacketInternalException: ${e.cause?.message}") bot.logger.logError("Caught SendPacketInternalException: ${e.cause?.message}")
bot.reinitializeNetworkHandler(configuration, e) bot.reinitializeNetworkHandler(configuration, e)
......
...@@ -30,33 +30,22 @@ abstract class OutgoingPacket : Packet(), Closeable { ...@@ -30,33 +30,22 @@ abstract class OutgoingPacket : Packet(), Closeable {
internal fun atomicNextSequenceId() = sequenceIdInternal.getAndIncrement().toUShort() internal fun atomicNextSequenceId() = sequenceIdInternal.getAndIncrement().toUShort()
} }
/** inline fun buildAndUse(block: (ByteReadPacket) -> Unit) {
* 务必 [ByteReadPacket.close] 或 [close] 或使用 [Closeable.use] buildPacket().use(block)
*/ }
var packet: ByteReadPacket = Uninitialized
get() {
if (field === Uninitialized) build()
return field
}
private set
private fun build(): ByteReadPacket { @PublishedApi
packet = buildPacket { internal fun buildPacket(): ByteReadPacket = buildPacket {
writeHex(TIMProtocol.head) writeHex(TIMProtocol.head)
writeHex(TIMProtocol.ver) writeHex(TIMProtocol.ver)
writePacketId() writePacketId()
encode(this) encode(this)
writeHex(TIMProtocol.tail) writeHex(TIMProtocol.tail)
}
return packet
} }
override fun toString(): String = packetToString() override fun toString(): String = packetToString()
override fun close() { override fun close() {
if (this.packet !== Uninitialized) {
this.packet.close()
}
} }
private fun BytePacketBuilder.writePacketId() { private fun BytePacketBuilder.writePacketId() {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
package net.mamoe.mirai.network.protocol.tim.packet package net.mamoe.mirai.network.protocol.tim.packet
import net.mamoe.mirai.utils.toUHexString import net.mamoe.mirai.utils.io.toUHexString
/** /**
* 数据包. * 数据包.
......
...@@ -6,11 +6,11 @@ import kotlinx.io.core.ByteReadPacket ...@@ -6,11 +6,11 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Closeable import kotlinx.io.core.Closeable
import kotlinx.io.core.IoBuffer import kotlinx.io.core.IoBuffer
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
import net.mamoe.mirai.utils.TEA import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.decryptBy
import net.mamoe.mirai.utils.io.cutTail import net.mamoe.mirai.utils.io.ByteArrayPool
import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.parseServerPacket import net.mamoe.mirai.utils.io.parseServerPacket
import net.mamoe.mirai.utils.io.readRemainingBytes
import net.mamoe.mirai.utils.io.toReadPacket import net.mamoe.mirai.utils.io.toReadPacket
import kotlin.properties.Delegates import kotlin.properties.Delegates
...@@ -41,16 +41,32 @@ fun <S : ServerPacket> S.applySequence(sequenceId: UShort): S { ...@@ -41,16 +41,32 @@ fun <S : ServerPacket> S.applySequence(sequenceId: UShort): S {
return this return this
} }
fun ServerPacket.decryptBy(key: ByteArray): ByteReadPacket = ByteReadPacket(decryptAsByteArray(key)) fun ServerPacket.decryptBy(key: ByteArray): ByteReadPacket = decryptAsByteArray(key) { data -> ByteReadPacket(data, 0) }
fun ServerPacket.decryptBy(key: IoBuffer): ByteReadPacket = ByteReadPacket(decryptAsByteArray(key)) fun ServerPacket.decryptBy(key: IoBuffer): ByteReadPacket = decryptAsByteArray(key) { data -> ByteReadPacket(data, 0) }
fun ServerPacket.decryptBy(keyHex: String): ByteReadPacket = this.decryptBy(keyHex.hexToBytes()) fun ServerPacket.decryptBy(keyHex: String): ByteReadPacket = this.decryptBy(keyHex.hexToBytes())
fun ServerPacket.decryptBy(key1: ByteArray, key2: ByteArray): ByteReadPacket = TEA.decrypt(this.decryptAsByteArray(key1), key2).toReadPacket() fun ServerPacket.decryptBy(key1: ByteArray, key2: ByteArray): ByteReadPacket =
this.decryptAsByteArray(key1) { data ->
data.decryptBy(key2).toReadPacket()
}
fun ServerPacket.decryptBy(key1: String, key2: ByteArray): ByteReadPacket = this.decryptBy(key1.hexToBytes(), key2) fun ServerPacket.decryptBy(key1: String, key2: ByteArray): ByteReadPacket = this.decryptBy(key1.hexToBytes(), key2)
fun ServerPacket.decryptBy(key1: String, key2: IoBuffer): ByteReadPacket = this.decryptBy(key1.hexToBytes(), key2.readBytes()) fun ServerPacket.decryptBy(key1: String, key2: IoBuffer): ByteReadPacket = this.decryptBy(key1.hexToBytes(), key2.readBytes())
fun ServerPacket.decryptBy(key1: ByteArray, key2: String): ByteReadPacket = this.decryptBy(key1, key2.hexToBytes()) fun ServerPacket.decryptBy(key1: ByteArray, key2: String): ByteReadPacket = this.decryptBy(key1, key2.hexToBytes())
fun ServerPacket.decryptBy(keyHex1: String, keyHex2: String): ByteReadPacket = this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes()) fun ServerPacket.decryptBy(keyHex1: String, keyHex2: String): ByteReadPacket = this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes())
fun ServerPacket.decryptAsByteArray(key: ByteArray): ByteArray = TEA.decrypt(input.readRemainingBytes().cutTail(1), key) inline fun <R> ServerPacket.decryptAsByteArray(key: ByteArray, consumer: (ByteArray) -> R): R =
fun ServerPacket.decryptAsByteArray(keyHex: String): ByteArray = this.decryptAsByteArray(keyHex.hexToBytes()) ByteArrayPool.useInstance {
fun ServerPacket.decryptAsByteArray(buffer: IoBuffer): ByteArray = this.decryptAsByteArray(buffer.readBytes()) val length = input.remaining.toInt() - 1
\ No newline at end of file input.readFully(it, 0, length)
consumer(it.decryptBy(key, length))
}.also { input.close() }
inline fun <R> ServerPacket.decryptAsByteArray(keyHex: String, consumer: (ByteArray) -> R): R = this.decryptAsByteArray(keyHex.hexToBytes(), consumer)
inline fun <R> ServerPacket.decryptAsByteArray(key: IoBuffer, consumer: (ByteArray) -> R): R =
ByteArrayPool.useInstance {
val length = input.remaining.toInt() - 1
input.readFully(it, 0, length)
consumer(it.decryptBy(key, length))
}.also { input.close() }
...@@ -10,7 +10,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.applySequence ...@@ -10,7 +10,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.applySequence
import net.mamoe.mirai.network.protocol.tim.packet.decryptBy import net.mamoe.mirai.network.protocol.tim.packet.decryptBy
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.toByteArray
/** /**
* 事件的识别 ID. 在 [事件确认包][ServerEventPacket.ResponsePacket] 中被使用. * 事件的识别 ID. 在 [事件确认包][ServerEventPacket.ResponsePacket] 中被使用.
......
...@@ -84,8 +84,8 @@ class SubmitCaptchaPacket( ...@@ -84,8 +84,8 @@ class SubmitCaptchaPacket(
*/ */
@PacketId(0x00_BAu) @PacketId(0x00_BAu)
class OutgoingCaptchaRefreshPacket( class OutgoingCaptchaRefreshPacket(
private val qq: UInt, private val qq: UInt,
private val token0825: ByteArray private val token0825: ByteArray
) : OutgoingPacket() { ) : OutgoingPacket() {
override fun encode(builder: BytePacketBuilder) = with(builder) { override fun encode(builder: BytePacketBuilder) = with(builder) {
this.writeQQ(qq) this.writeQQ(qq)
...@@ -164,14 +164,14 @@ abstract class ServerCaptchaPacket(input: ByteReadPacket) : ServerPacket(input) ...@@ -164,14 +164,14 @@ abstract class ServerCaptchaPacket(input: ByteReadPacket) : ServerPacket(input)
@PacketId(0x00_BAu) @PacketId(0x00_BAu)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(): ServerCaptchaPacket { fun decrypt(): ServerCaptchaPacket {
val data = this.decryptAsByteArray(TIMProtocol.key00BA) return this.decryptAsByteArray(TIMProtocol.key00BA) { data ->
when (data.size) {
return when (data.size) { 66,
66, 95 -> CaptchaCorrectPacket(data.toReadPacket())
95 -> CaptchaCorrectPacket(data.toReadPacket()) //66 -> ServerCaptchaUnknownPacket(data.toReadPacket())
//66 -> ServerCaptchaUnknownPacket(data.toReadPacket()) else -> CaptchaTransmissionResponsePacket(data.toReadPacket())
else -> CaptchaTransmissionResponsePacket(data.toReadPacket()) }.applySequence(sequenceId)
}.applySequence(sequenceId) }
} }
} }
} }
...@@ -8,8 +8,7 @@ import kotlinx.io.core.writeFully ...@@ -8,8 +8,7 @@ import kotlinx.io.core.writeFully
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.PacketId import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.utils.TEA import net.mamoe.mirai.utils.encryptBy
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.writeCRC32 import net.mamoe.mirai.utils.writeCRC32
...@@ -90,7 +89,7 @@ private fun BytePacketBuilder.writePart1( ...@@ -90,7 +89,7 @@ private fun BytePacketBuilder.writePart1(
this.writeHex(TIMProtocol.passwordSubmissionTLV2) this.writeHex(TIMProtocol.passwordSubmissionTLV2)
this.writeHex("00 1A")//tag this.writeHex("00 1A")//tag
this.writeHex("00 40")//length this.writeHex("00 40")//length
this.writeFully(TEA.encrypt(TIMProtocol.passwordSubmissionTLV2.hexToBytes(), privateKey)) this.writeFully(TIMProtocol.passwordSubmissionTLV2.hexToBytes().encryptBy(privateKey))
this.writeHex(TIMProtocol.constantData1) this.writeHex(TIMProtocol.constantData1)
this.writeHex(TIMProtocol.constantData2) this.writeHex(TIMProtocol.constantData2)
this.writeQQ(qq) this.writeQQ(qq)
......
...@@ -4,12 +4,14 @@ package net.mamoe.mirai.network.protocol.tim.packet.login ...@@ -4,12 +4,14 @@ package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.applySequence
import net.mamoe.mirai.network.protocol.tim.packet.decryptBy
import net.mamoe.mirai.utils.Tested import net.mamoe.mirai.utils.Tested
import net.mamoe.mirai.utils.io.readBoolean import net.mamoe.mirai.utils.io.readBoolean
import net.mamoe.mirai.utils.io.readIoBuffer import net.mamoe.mirai.utils.io.readIoBuffer
import net.mamoe.mirai.utils.io.readString import net.mamoe.mirai.utils.io.readString
import net.mamoe.mirai.utils.io.toReadPacket
import kotlin.properties.Delegates import kotlin.properties.Delegates
@PacketId(0x08_36u) @PacketId(0x08_36u)
...@@ -82,18 +84,20 @@ class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePac ...@@ -82,18 +84,20 @@ class LoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePac
discardExact(60)//00 20 01 60 C5 A1 39 7A 12 8E BC 34 C3 56 70 E3 1A ED 20 67 ED A9 DB 06 C1 70 81 3C 01 69 0D FF 63 DA 00 00 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6 discardExact(60)//00 20 01 60 C5 A1 39 7A 12 8E BC 34 C3 56 70 E3 1A ED 20 67 ED A9 DB 06 C1 70 81 3C 01 69 0D FF 63 DA 00 00 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6
discardExact(when (readUByte().toUInt()) { discardExact(
0x00u -> when (readUByte().toUInt()) { when (readUByte().toUInt()) {
0x33u -> 28 0x00u -> when (readUByte().toUInt()) {
0x33u -> 28
else -> null
}
0x01u -> when (readUByte().toUInt()) {
0x07u -> 0
0x10u -> 64
else -> null
}
else -> null else -> null
} } ?: error("Unknown length flag")
0x01u -> when (readUByte().toUInt()) { )
0x07u -> 0
0x10u -> 64
else -> null
}
else -> null
} ?: error("Unknown length flag"))
discardExact(23 + 3)//01 D3 00 01 00 16 00 00 00 01 00 00 00 64 00 00 0D DE 00 09 3A 80 00 discardExact(23 + 3)//01 D3 00 01 00 16 00 00 00 01 00 00 00 64 00 00 0D DE 00 09 3A 80 00
...@@ -156,6 +160,6 @@ class LoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginRespons ...@@ -156,6 +160,6 @@ class LoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginRespons
@PacketId(0x08_36u) @PacketId(0x08_36u)
class Encrypted(input: ByteReadPacket) : ServerPacket(input) { class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
fun decrypt(): LoginResponseCaptchaInitPacket = LoginResponseCaptchaInitPacket(decryptAsByteArray(TIMProtocol.shareKey).toReadPacket()).applySequence(sequenceId) fun decrypt(): LoginResponseCaptchaInitPacket = LoginResponseCaptchaInitPacket(decryptBy(TIMProtocol.shareKey)).applySequence(sequenceId)
} }
} }
\ No newline at end of file
...@@ -8,9 +8,7 @@ import kotlinx.io.core.discardExact ...@@ -8,9 +8,7 @@ import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.toUHexString
/** /**
* The packet received when logging in, used to redirect server address * The packet received when logging in, used to redirect server address
......
...@@ -3,6 +3,7 @@ package net.mamoe.mirai.utils ...@@ -3,6 +3,7 @@ package net.mamoe.mirai.utils
import kotlinx.io.core.BytePacketBuilder import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.toByteArray import kotlinx.io.core.toByteArray
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import net.mamoe.mirai.utils.io.getRandomByteArray
private const val GTK_BASE_VALUE: Int = 5381 private const val GTK_BASE_VALUE: Int = 5381
......
...@@ -12,6 +12,7 @@ import net.mamoe.mirai.message.ImageId ...@@ -12,6 +12,7 @@ import net.mamoe.mirai.message.ImageId
import net.mamoe.mirai.message.image import net.mamoe.mirai.message.image
import net.mamoe.mirai.message.sendTo import net.mamoe.mirai.message.sendTo
import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
import net.mamoe.mirai.utils.io.toUHexString
@Suppress("FunctionName") @Suppress("FunctionName")
fun ExternalImage( fun ExternalImage(
......
...@@ -17,7 +17,7 @@ interface MiraiLogger { ...@@ -17,7 +17,7 @@ interface MiraiLogger {
*/ */
companion object : MiraiLogger by DefaultLogger("TOP Level") companion object : MiraiLogger by DefaultLogger("TOP Level")
var identity: String? val identity: String?
/** /**
* 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用. * 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用.
...@@ -74,7 +74,8 @@ interface MiraiLogger { ...@@ -74,7 +74,8 @@ interface MiraiLogger {
/** /**
* 平台基类. * 平台基类.
* 实现了 [follower] 的调用传递 * 实现了 [follower] 的调用传递.
* 若要自行实现日志记录, 请优先考虑继承 [PlatformLogger]
*/ */
abstract class MiraiLoggerPlatformBase : MiraiLogger { abstract class MiraiLoggerPlatformBase : MiraiLogger {
final override var follower: MiraiLogger? = null final override var follower: MiraiLogger? = null
...@@ -145,7 +146,7 @@ abstract class MiraiLoggerPlatformBase : MiraiLogger { ...@@ -145,7 +146,7 @@ abstract class MiraiLoggerPlatformBase : MiraiLogger {
/** /**
* 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, 如 [Bot], 都会通过这个函数构造日志记录器 * 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, 如 [Bot], 都会通过这个函数构造日志记录器
*/ */
var DefaultLogger: (identity: String?) -> PlatformLogger = { PlatformLogger() } var DefaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger() }
/** /**
* 当前平台的默认的日志记录器. * 当前平台的默认的日志记录器.
...@@ -153,7 +154,39 @@ var DefaultLogger: (identity: String?) -> PlatformLogger = { PlatformLogger() } ...@@ -153,7 +154,39 @@ var DefaultLogger: (identity: String?) -> PlatformLogger = { PlatformLogger() }
* *
* 不应该直接构造这个类的实例. 需使用 [DefaultLogger] * 不应该直接构造这个类的实例. 需使用 [DefaultLogger]
*/ */
expect class PlatformLogger @JvmOverloads internal constructor(identity: String? = null) : MiraiLoggerPlatformBase expect open class PlatformLogger @JvmOverloads internal constructor(identity: String? = null) : MiraiLoggerPlatformBase
/**
* 不作任何事情的 logger
*/
@Suppress("unused")
object NoLogger : PlatformLogger() {
override val identity: String? = null
override fun log0(e: Throwable) {
}
override fun log0(any: Any?) {
}
override fun logError0(any: Any?) {
}
override fun logDebug0(any: Any?) {
}
override fun logCyan0(any: Any?) {
}
override fun logPurple0(any: Any?) {
}
override fun logGreen0(any: Any?) {
}
override fun logBlue0(any: Any?) {
}
}
/** /**
* 在顶层日志记录这个异常 * 在顶层日志记录这个异常
......
...@@ -22,6 +22,8 @@ enum class OnlineStatus( ...@@ -22,6 +22,8 @@ enum class OnlineStatus(
BUSY(0x32u); BUSY(0x32u);
// TODO: 2019/10/29 what is 0x20u
companion object { companion object {
fun ofId(id: UByte): OnlineStatus? = values().firstOrNull { it.id == id } fun ofId(id: UByte): OnlineStatus? = values().firstOrNull { it.id == id }
} }
......
...@@ -12,6 +12,16 @@ internal fun Long.coerceAtLeastOrFail(value: Long): Long { ...@@ -12,6 +12,16 @@ internal fun Long.coerceAtLeastOrFail(value: Long): Long {
return this return this
} }
@PublishedApi
internal fun Int.coerceAtMostOrFail(maximumValue: Int): Int =
if (this > maximumValue) error("value is greater than its expected maximum value $maximumValue")
else this
@PublishedApi
internal fun Long.coerceAtMostOrFail(maximumValue: Long): Long =
if (this > maximumValue) error("value is greater than its expected maximum value $maximumValue")
else this
/** /**
* 表示这个参数必须为正数 * 表示这个参数必须为正数
*/ */
......
package net.mamoe.mirai.utils package net.mamoe.mirai.utils.io
import kotlinx.io.pool.DefaultPool import kotlinx.io.pool.DefaultPool
import kotlinx.io.pool.ObjectPool import kotlinx.io.pool.ObjectPool
internal const val DEFAULT_BUFFER_SIZE = 4098 internal const val DEFAULT_BYTE_ARRAY_POOL_SIZE = 128
internal const val DEFAULT_BYTE_ARRAY_POOL_SIZE = 2048 internal const val DEFAULT_BYTE_ARRAY_SIZE = 2048
/** val ByteArrayPool: ObjectPool<ByteArray> = ByteArrayPoolImpl
* The default ktor byte buffer pool
*/
val ByteArrayPool: ObjectPool<ByteArray> = ByteBufferPool()
class ByteBufferPool : DefaultPool<ByteArray>(DEFAULT_BYTE_ARRAY_POOL_SIZE) { private object ByteArrayPoolImpl : DefaultPool<ByteArray>(DEFAULT_BYTE_ARRAY_POOL_SIZE) {
override fun produceInstance(): ByteArray = ByteArray(DEFAULT_BUFFER_SIZE) override fun produceInstance(): ByteArray = ByteArray(DEFAULT_BYTE_ARRAY_SIZE)
override fun clearInstance(instance: ByteArray): ByteArray = instance.apply { map { 0 } } override fun clearInstance(instance: ByteArray): ByteArray = instance
} }
...@@ -33,7 +33,7 @@ fun UByteArray.toUHexString(separator: String = " "): String = this.joinToString ...@@ -33,7 +33,7 @@ fun UByteArray.toUHexString(separator: String = " "): String = this.joinToString
return@joinToString ret return@joinToString ret
} }
fun ByteArray.toReadPacket() = ByteReadPacket(this) fun ByteArray.toReadPacket(offset: Int = 0, length: Int = this.size) = ByteReadPacket(this, offset = offset, length = length)
fun <R> ByteArray.read(t: ByteReadPacket.() -> R): R = this.toReadPacket().use(t) fun <R> ByteArray.read(t: ByteReadPacket.() -> R): R = this.toReadPacket().use(t)
......
...@@ -3,8 +3,6 @@ package net.mamoe.mirai.utils.io ...@@ -3,8 +3,6 @@ package net.mamoe.mirai.utils.io
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toIoBuffer
internal object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug") internal object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug")
...@@ -78,5 +76,8 @@ internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstCons ...@@ -78,5 +76,8 @@ internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstCons
println() println()
} }
/**
* TODO 这两个方法不应该 MPP
*/
expect fun printCompareHex(hex1s: String, hex2s: String): String expect fun printCompareHex(hex1s: String, hex2s: String): String
expect fun String.printColorize(ignoreUntilFirstConst: Boolean = false): String expect fun String.printColorize(ignoreUntilFirstConst: Boolean = false): String
...@@ -84,7 +84,7 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket { ...@@ -84,7 +84,7 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
0x00_58u -> ResponsePacket.Encrypted<HeartbeatPacket.Response>(this) 0x00_58u -> ResponsePacket.Encrypted<HeartbeatPacket.Response>(this)
0x03_88u -> ResponsePacket.Encrypted<GroupImageIdRequestPacket.Response>(this) 0x03_88u -> ResponsePacket.Encrypted<GroupImageIdRequestPacket.Response>(this)
0x03_52u -> ResponsePacket.Encrypted<FriendImageIdRequestPacket.Response>(this) 0x03_52u -> ResponsePacket.Encrypted<FriendImageIdRequestPacket.Response>(this)
0x01_BDu -> ResponsePacket.Encrypted<SubmitImageFilenamePacket.Response>(this) // 0x01_BDu -> ResponsePacket.Encrypted<SubmitImageFilenamePacket.Response>(this)
else -> UnknownServerPacket.Encrypted(this, id, sequenceId) else -> UnknownServerPacket.Encrypted(this, id, sequenceId)
}.applySequence(sequenceId) }.applySequence(sequenceId)
......
...@@ -3,14 +3,15 @@ ...@@ -3,14 +3,15 @@
package net.mamoe.mirai.utils.io package net.mamoe.mirai.utils.io
import kotlinx.io.core.* import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.contact.GroupId import net.mamoe.mirai.contact.GroupId
import net.mamoe.mirai.contact.GroupInternalId import net.mamoe.mirai.contact.GroupInternalId
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.internal.coerceAtMostOrFail
import kotlin.random.Random import kotlin.random.Random
import kotlin.random.nextInt import kotlin.random.nextInt
fun BytePacketBuilder.writeZero(count: Int) = repeat(count) { this.writeByte(0) } fun BytePacketBuilder.writeZero(count: Int) = repeat(count) { this.writeByte(0) }
fun BytePacketBuilder.writeRandom(length: Int) = repeat(length) { this.writeByte(Random.Default.nextInt(255).toByte()) } fun BytePacketBuilder.writeRandom(length: Int) = repeat(length) { this.writeByte(Random.Default.nextInt(255).toByte()) }
...@@ -26,28 +27,25 @@ fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray) { ...@@ -26,28 +27,25 @@ fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray) {
} }
// will box, but it doesn't matter fun BytePacketBuilder.writeShortLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long)? = null, builder: BytePacketBuilder.() -> Unit) =
private fun <N : Comparable<N>> N.coerceAtMostOrFail(maximumValue: N): N = with(BytePacketBuilder().apply(builder).build()) {
if (this > maximumValue) error("value is greater than its expected maximum value $maximumValue") if (tag != null) {
else this writeUByte(tag)
}
fun BytePacketBuilder.writeShortLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long)? = null, builder: BytePacketBuilder.() -> Unit) = with(BytePacketBuilder().apply(builder).build()) { writeUShort((lengthOffset?.invoke(remaining) ?: remaining).coerceAtMostOrFail(0xFFFFL).toUShort())
if (tag != null) { writePacket(this)
writeUByte(tag) this.release()
} }
writeUShort((lengthOffset?.invoke(remaining) ?: remaining).coerceAtMostOrFail(0xFFFFL).toUShort())
writePacket(this)
this.release()
}
fun BytePacketBuilder.writeUVarintLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long)? = null, builder: BytePacketBuilder.() -> Unit) = with(BytePacketBuilder().apply(builder).build()) { fun BytePacketBuilder.writeUVarintLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long)? = null, builder: BytePacketBuilder.() -> Unit) =
if (tag != null) { with(BytePacketBuilder().apply(builder).build()) {
writeUByte(tag) if (tag != null) {
writeUByte(tag)
}
writeUVarInt((lengthOffset?.invoke(remaining) ?: remaining).coerceAtMostOrFail(0xFFFFL))
writePacket(this)
this.release()
} }
writeUVarInt((lengthOffset?.invoke(remaining) ?: remaining).coerceAtMostOrFail(0xFFFFL))
writePacket(this)
this.release()
}
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
fun BytePacketBuilder.writeShortLVString(str: String) = this.writeShortLVByteArray(str.toByteArray()) fun BytePacketBuilder.writeShortLVString(str: String) = this.writeShortLVByteArray(str.toByteArray())
...@@ -100,10 +98,17 @@ fun BytePacketBuilder.writeTByteArray(tag: UByte, value: UByteArray) { ...@@ -100,10 +98,17 @@ fun BytePacketBuilder.writeTByteArray(tag: UByte, value: UByteArray) {
this.writeFully(value) this.writeFully(value)
} }
fun BytePacketBuilder.encryptAndWrite(key: IoBuffer, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(key.readBytes(), encoder) /**
fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) = writeFully(TEA.encrypt(BytePacketBuilder().apply(encoder).use { * 会使用 [ByteArrayPool] 缓存
it.build().readBytes() */
}, key)) fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) =
BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) }
fun BytePacketBuilder.encryptAndWrite(key: IoBuffer, encoder: BytePacketBuilder.() -> Unit) = ByteArrayPool.useInstance {
key.readFully(it, 0, key.readRemaining)
encryptAndWrite(it, encoder)
}
fun BytePacketBuilder.encryptAndWrite(keyHex: String, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(keyHex.hexToBytes(), encoder) fun BytePacketBuilder.encryptAndWrite(keyHex: String, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(keyHex.hexToBytes(), encoder)
fun BytePacketBuilder.writeTLV0006(qq: UInt, password: String, loginTime: Int, loginIP: String, privateKey: ByteArray) { fun BytePacketBuilder.writeTLV0006(qq: UInt, password: String, loginTime: Int, loginIP: String, privateKey: ByteArray) {
......
...@@ -9,8 +9,6 @@ import kotlinx.io.errors.IOException ...@@ -9,8 +9,6 @@ import kotlinx.io.errors.IOException
*/ */
expect class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Closeable { expect class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Closeable {
// TODO: 2019/10/27 使用 Ktor 的 socket
suspend fun read(buffer: IoBuffer): Int suspend fun read(buffer: IoBuffer): Int
suspend fun send(buffer: IoBuffer): Int suspend fun send(buffer: IoBuffer): Int
...@@ -25,9 +23,9 @@ expect class ClosedChannelException : IOException ...@@ -25,9 +23,9 @@ expect class ClosedChannelException : IOException
/** /**
* 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误. * 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误.
*/ */
expect class SendPacketInternalException(cause: Throwable?) : IOException class SendPacketInternalException(cause: Throwable?) : Exception(cause)
/** /**
* 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误. * 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误.
*/ */
expect class ReadPacketInternalException(cause: Throwable?) : IOException class ReadPacketInternalException(cause: Throwable?) : Exception(cause)
\ No newline at end of file \ No newline at end of file
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE") @file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE", "unused")
package net.mamoe.mirai.utils package net.mamoe.mirai.utils.io
import kotlinx.io.core.IoBuffer import kotlinx.io.core.IoBuffer
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import net.mamoe.mirai.utils.io.toUHexString import kotlinx.io.pool.ObjectPool
import kotlin.jvm.Synchronized import kotlin.jvm.Synchronized
import kotlin.random.Random import kotlin.random.Random
import kotlin.random.nextInt import kotlin.random.nextInt
/*
* 类型转换 Utils.
* 这些函数为内部函数, 可能会改变
*/
/** /**
* 255 -> 00 00 00 FF * 255 -> 00 00 00 FF
*/ */
fun Int.toByteArray(): ByteArray = byteArrayOf( fun Int.toByteArray(): ByteArray = byteArrayOf(
(ushr(24) and 0xFF).toByte(), (shr(24) and 0xFF).toByte(),
(ushr(16) and 0xFF).toByte(), (shr(16) and 0xFF).toByte(),
(ushr(8) and 0xFF).toByte(), (shr(8) and 0xFF).toByte(),
(ushr(0) and 0xFF).toByte() (shr(0) and 0xFF).toByte()
) )
/** /**
...@@ -24,8 +30,8 @@ fun Int.toByteArray(): ByteArray = byteArrayOf( ...@@ -24,8 +30,8 @@ fun Int.toByteArray(): ByteArray = byteArrayOf(
*/ */
fun UShort.toByteArray(): ByteArray = with(toUInt()) { fun UShort.toByteArray(): ByteArray = with(toUInt()) {
byteArrayOf( byteArrayOf(
(shr(8) and 255u).toByte(), (shr(8) and 255u).toByte(),
(shr(0) and 255u).toByte() (shr(0) and 255u).toByte()
) )
} }
...@@ -33,32 +39,80 @@ fun UShort.toByteArray(): ByteArray = with(toUInt()) { ...@@ -33,32 +39,80 @@ fun UShort.toByteArray(): ByteArray = with(toUInt()) {
* 255u -> 00 00 00 FF * 255u -> 00 00 00 FF
*/ */
fun UInt.toByteArray(): ByteArray = byteArrayOf( fun UInt.toByteArray(): ByteArray = byteArrayOf(
(shr(24) and 255u).toByte(), (shr(24) and 255u).toByte(),
(shr(16) and 255u).toByte(), (shr(16) and 255u).toByte(),
(shr(8) and 255u).toByte(), (shr(8) and 255u).toByte(),
(shr(0) and 255u).toByte() (shr(0) and 255u).toByte()
) )
/**
* 转 [ByteArray] 后再转 hex
*/
fun Int.toUHexString(separator: String = " "): String = this.toByteArray().toUHexString(separator) fun Int.toUHexString(separator: String = " "): String = this.toByteArray().toUHexString(separator)
/**
* 转无符号十六进制表示, 并补充首位 `0`.
* 转换结果示例: `FF`, `0E`
*/
fun Byte.toUHexString(): String = this.toUByte().toString(16).toUpperCase().let { fun Byte.toUHexString(): String = this.toUByte().toString(16).toUpperCase().let {
if (it.length == 1) "0$it" if (it.length == 1) "0$it"
else it else it
} }
fun String.hexToBytes(): ByteArray = HexCache.hexToBytes(this) /**
* 将无符号 Hex 转为 [ByteArray], 有根据 hex 的 [hashCode] 建立的缓存.
*/
fun String.hexToBytes(withCache: Boolean = true): ByteArray =
if (withCache) HexCache.getCacheOrConvert(this)
else this.split(" ")
.filterNot { it.isEmpty() }
.map { s -> s.toUByte(16).toByte() }
.toByteArray()
/** /**
* 将 Hex 转为 UByteArray, 有根据 hex 的 [hashCode] 建立的缓存. * 将无符号 Hex 转为 [UByteArray], 有根据 hex 的 [hashCode] 建立的缓存.
*/ */
fun String.hexToUBytes(): UByteArray = HexCache.hexToUBytes(this) fun String.hexToUBytes(withCache: Boolean = true): UByteArray =
if (withCache) HexCache.getUCacheOrConvert(this)
else this.split(" ")
.filterNot { it.isEmpty() }
.map { s -> s.toUByte(16) }
.toUByteArray()
fun String.hexToInt(): Int = hexToBytes().toUInt().toInt() /**
* 生成长度为 [length], 元素为随机 `0..255` 的 [ByteArray]
*/
fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Random.nextInt(0..255).toByte() } fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Random.nextInt(0..255).toByte() }
/**
* 随机生成长度为 [length] 的 [String].
*/
fun getRandomString(length: Int): String = getRandomString(length, 'a'..'z', 'A'..'Z', '0'..'9') fun getRandomString(length: Int): String = getRandomString(length, 'a'..'z', 'A'..'Z', '0'..'9')
/**
* 根据所给 [charRange] 随机生成长度为 [length] 的 [String].
*/
fun getRandomString(length: Int, charRange: CharRange): String = String(CharArray(length) { charRange.random() }) fun getRandomString(length: Int, charRange: CharRange): String = String(CharArray(length) { charRange.random() })
/**
* 根据所给 [charRanges] 随机生成长度为 [length] 的 [String].
*/
fun getRandomString(length: Int, vararg charRanges: CharRange): String = String(CharArray(length) { charRanges[Random.Default.nextInt(0..charRanges.lastIndex)].random() }) fun getRandomString(length: Int, vararg charRanges: CharRange): String = String(CharArray(length) { charRanges[Random.Default.nextInt(0..charRanges.lastIndex)].random() })
/**
* 将 [this] 前 4 个 [Byte] 的 bits 合并为一个 [Int]
*
* 详细解释:
* 一个 [Byte] 有 8 bits
* 一个 [Int] 有 32 bits
* 本函数将 4 个 [Byte] 的 bits 连接得到 [Int]
*/
fun ByteArray.toUInt(): UInt = this[0].toUInt().and(255u).shl(24) + this[1].toUInt().and(255u).shl(16) + this[2].toUInt().and(255u).shl(8) + this[3].toUInt().and(255u).shl(0) fun ByteArray.toUInt(): UInt = this[0].toUInt().and(255u).shl(24) + this[1].toUInt().and(255u).shl(16) + this[2].toUInt().and(255u).shl(8) + this[3].toUInt().and(255u).shl(0)
/**
* 从 [IoBuffer.Pool] [borrow][ObjectPool.borrow] 一个 [IoBuffer] 然后将 [this] 写入.
* 注意回收 ([ObjectPool.recycle])
*/
fun ByteArray.toIoBuffer(): IoBuffer = IoBuffer.Pool.borrow().let { it.writeFully(this); it } fun ByteArray.toIoBuffer(): IoBuffer = IoBuffer.Pool.borrow().let { it.writeFully(this); it }
/** /**
...@@ -69,20 +123,28 @@ internal object HexCache { ...@@ -69,20 +123,28 @@ internal object HexCache {
private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf() private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf()
@Synchronized @Synchronized
internal fun hexToBytes(hex: String): ByteArray = hex.hashCode().let { id -> internal fun getCacheOrConvert(hex: String): ByteArray = hex.hashCode().let { id ->
if (hexToByteArrayCacheMap.containsKey(id)) { if (hexToByteArrayCacheMap.containsKey(id)) {
return hexToByteArrayCacheMap[id]!!.copyOf() return hexToByteArrayCacheMap[id]!!
} else { } else {
hexToUBytes(hex).toByteArray().let { hex.hexToBytes(withCache = false).let {
hexToByteArrayCacheMap[id] = it.copyOf() hexToByteArrayCacheMap[id] = it
return it return it
} }
} }
} }
internal fun hexToUBytes(hex: String): UByteArray = private val hexToUByteArrayCacheMap: MutableMap<Int, UByteArray> = mutableMapOf()
hex.split(" ")
.filterNot { it.isEmpty() } @Synchronized
.map { s -> s.toUByte(16) } internal fun getUCacheOrConvert(hex: String): UByteArray = hex.hashCode().let { id ->
.toUByteArray() if (hexToUByteArrayCacheMap.containsKey(id)) {
return hexToUByteArrayCacheMap[id]!!
} else {
hex.hexToUBytes(withCache = false).let {
hexToUByteArrayCacheMap[id] = it
return it
}
}
}
} }
\ No newline at end of file
...@@ -55,6 +55,9 @@ private object IgnoreIdListInclude : List<String> by listOf( ...@@ -55,6 +55,9 @@ private object IgnoreIdListInclude : List<String> by listOf(
"RefVolatile" "RefVolatile"
) )
/**
* 这个方法会翻倍内存占用, 考虑修改.
*/
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjustName(this::class.simpleName + "(${this.idHexString})") + this::class.java.allDeclaredFields internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjustName(this::class.simpleName + "(${this.idHexString})") + this::class.java.allDeclaredFields
.filterNot { field -> .filterNot { field ->
......
...@@ -9,7 +9,7 @@ actual typealias PlatformLogger = Console ...@@ -9,7 +9,7 @@ actual typealias PlatformLogger = Console
* JVM 控制台日志实现 * JVM 控制台日志实现
*/ */
open class Console @JvmOverloads internal constructor( open class Console @JvmOverloads internal constructor(
override var identity: String? = null override val identity: String? = null
) : MiraiLoggerPlatformBase() { ) : MiraiLoggerPlatformBase() {
override fun logGreen0(any: Any?) = println(any.toString(), LoggerTextFormat.GREEN) override fun logGreen0(any: Any?) = println(any.toString(), LoggerTextFormat.GREEN)
override fun logPurple0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_PURPLE) override fun logPurple0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_PURPLE)
...@@ -17,7 +17,7 @@ open class Console @JvmOverloads internal constructor( ...@@ -17,7 +17,7 @@ open class Console @JvmOverloads internal constructor(
override fun logCyan0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_CYAN) override fun logCyan0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_CYAN)
override fun logError0(any: Any?) = println(any.toString(), LoggerTextFormat.RED) override fun logError0(any: Any?) = println(any.toString(), LoggerTextFormat.RED)
override fun log0(e: Throwable) = e.printStackTrace() override fun log0(e: Throwable) = e.printStackTrace()
override fun log0(any: Any?) = println(any.toString())//kotlin println override fun log0(any: Any?) = println(any.toString(), LoggerTextFormat.WHITE)
override fun logDebug0(any: Any?) { override fun logDebug0(any: Any?) {
if (DEBUGGING) { if (DEBUGGING) {
println(any.toString(), LoggerTextFormat.YELLOW) println(any.toString(), LoggerTextFormat.YELLOW)
......
package net.mamoe.mirai.utils
import java.nio.ByteBuffer
import java.util.*
import kotlin.experimental.and
import kotlin.experimental.xor
/**
* TEA 加密
*
* @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java
*/
internal actual object TEA {
private const val UINT32_MASK = 0xffffffffL
internal actual fun doOption(data: ByteArray, key: ByteArray, encrypt: Boolean): ByteArray {
val mRandom = Random()
lateinit var mOutput: ByteArray
lateinit var mInBlock: ByteArray
var mIndexPos: Int
lateinit var mIV: ByteArray
var mOutPos = 0
var mPreOutPos = 0
var isFirstBlock = true
val mKey = LongArray(4)
for (i in 0..3) {
mKey[i] = pack(key, i * 4, 4)
}
fun rand(): Int {
return mRandom.nextInt()
}
fun encode(bytes: ByteArray): ByteArray {
var v0 = pack(bytes, 0, 4)
var v1 = pack(bytes, 4, 4)
var sum: Long = 0
val delta = 0x9e3779b9L
for (i in 0..15) {
sum = sum + delta and UINT32_MASK
v0 += (v1 shl 4) + mKey[0] xor v1 + sum xor v1.ushr(5) + mKey[1]
v0 = v0 and UINT32_MASK
v1 += (v0 shl 4) + mKey[2] xor v0 + sum xor v0.ushr(5) + mKey[3]
v1 = v1 and UINT32_MASK
}
return ByteBuffer.allocate(8).putInt(v0.toInt()).putInt(v1.toInt()).array()
}
fun decode(bytes: ByteArray, offset: Int): ByteArray {
var v0 = pack(bytes, offset, 4)
var v1 = pack(bytes, offset + 4, 4)
val delta = 0x9e3779b9L
var sum = delta shl 4 and UINT32_MASK
for (i in 0..15) {
v1 -= (v0 shl 4) + mKey[2] xor v0 + sum xor v0.ushr(5) + mKey[3]
v1 = v1 and UINT32_MASK
v0 -= (v1 shl 4) + mKey[0] xor v1 + sum xor v1.ushr(5) + mKey[1]
v0 = v0 and UINT32_MASK
sum = sum - delta and UINT32_MASK
}
return ByteBuffer.allocate(8).putInt(v0.toInt()).putInt(v1.toInt()).array()
}
fun encodeOneBlock() {
mIndexPos = 0
while (mIndexPos < 8) {
mInBlock[mIndexPos] = if (isFirstBlock)
mInBlock[mIndexPos]
else
(mInBlock[mIndexPos] xor mOutput[mPreOutPos + mIndexPos])
mIndexPos++
}
System.arraycopy(encode(mInBlock), 0, mOutput, mOutPos, 8)
mIndexPos = 0
while (mIndexPos < 8) {
val outPos = mOutPos + mIndexPos
mOutput[outPos] = (mOutput[outPos] xor mIV[mIndexPos])
mIndexPos++
}
System.arraycopy(mInBlock, 0, mIV, 0, 8)
mPreOutPos = mOutPos
mOutPos += 8
mIndexPos = 0
isFirstBlock = false
}
fun decodeOneBlock(ciphertext: ByteArray, offset: Int, len: Int): Boolean {
mIndexPos = 0
while (mIndexPos < 8) {
if (mOutPos + mIndexPos < len) {
mIV[mIndexPos] = (mIV[mIndexPos] xor ciphertext[mOutPos + offset + mIndexPos])
mIndexPos++
continue
}
return true
}
mIV = decode(mIV, 0)
mOutPos += 8
mIndexPos = 0
return true
}
@Suppress("NAME_SHADOWING")
fun encrypt(plaintext: ByteArray, offset: Int, len: Int): ByteArray {
var len = len
var offset = offset
mInBlock = ByteArray(8)
mIV = ByteArray(8)
mOutPos = 0
mPreOutPos = 0
isFirstBlock = true
mIndexPos = (len + 10) % 8
if (mIndexPos != 0) {
mIndexPos = 8 - mIndexPos
}
mOutput = ByteArray(mIndexPos + len + 10)
mInBlock[0] = (rand() and 0xf8 or mIndexPos).toByte()
for (i in 1..mIndexPos) {
mInBlock[i] = (rand() and 0xff).toByte()
}
++mIndexPos
for (i in 0..7) {
mIV[i] = 0
}
var g = 0
while (g < 2) {
if (mIndexPos < 8) {
mInBlock[mIndexPos++] = (rand() and 0xff).toByte()
++g
}
if (mIndexPos == 8) {
encodeOneBlock()
}
}
while (len > 0) {
if (mIndexPos < 8) {
mInBlock[mIndexPos++] = plaintext[offset++]
}
if (mIndexPos == 8) {
encodeOneBlock()
}
len--
}
g = 0
while (g < 7) {
if (mIndexPos < 8) {
mInBlock[mIndexPos++] = 0.toByte()
}
if (mIndexPos == 8) {
encodeOneBlock()
}
g++
}
return mOutput
}
fun decrypt(cipherText: ByteArray, offset: Int, len: Int): ByteArray {
require(!(len % 8 != 0 || len < 16)) { "data must len % 8 == 0 && len >= 16 but given $len" }
mIV = decode(cipherText, offset)
mIndexPos = (mIV[0] and 7).toInt()
var plen = len - mIndexPos - 10
isFirstBlock = true
if (plen < 0) {
fail()
}
mOutput = ByteArray(plen)
mPreOutPos = 0
mOutPos = 8
++mIndexPos
var g = 0
while (g < 2) {
if (mIndexPos < 8) {
++mIndexPos
++g
}
if (mIndexPos == 8) {
isFirstBlock = false
if (!decodeOneBlock(cipherText, offset, len)) {
fail()
}
}
}
var outpos = 0
while (plen != 0) {
if (mIndexPos < 8) {
mOutput[outpos++] = if (isFirstBlock)
mIV[mIndexPos]
else
(cipherText[mPreOutPos + offset + mIndexPos] xor mIV[mIndexPos])
++mIndexPos
}
if (mIndexPos == 8) {
mPreOutPos = mOutPos - 8
isFirstBlock = false
if (!decodeOneBlock(cipherText, offset, len)) {
fail()
}
}
plen--
}
g = 0
while (g < 7) {
if (mIndexPos < 8) {
if (cipherText[mPreOutPos + offset + mIndexPos].xor(mIV[mIndexPos]).toInt() != 0) {
fail()
} else {
++mIndexPos
}
}
if (mIndexPos == 8) {
mPreOutPos = mOutPos
if (!decodeOneBlock(cipherText, offset, len)) {
fail()
}
}
g++
}
return mOutput
}
return if (encrypt) {
encrypt(data, 0, data.size)
} else {
decrypt(data, 0, data.size)
}
}
private fun fail(): Nothing = throw DecryptionFailedException()
@JvmStatic
actual fun encrypt(source: ByteArray, key: ByteArray): ByteArray {
return doOption(source, key, true)
}
@JvmStatic
actual fun decrypt(source: ByteArray, key: ByteArray): ByteArray {
return doOption(source, key, false)
}
@Suppress("SameParameterValue")
private fun pack(bytes: ByteArray, offset: Int, len: Int): Long {
var result: Long = 0
val maxOffset = if (len > 8) offset + 8 else offset + len
for (index in offset until maxOffset) {
result = result shl 8 or (bytes[index].toLong() and 0xffL)
}
return result shr 32 or (result and UINT32_MASK)
}
}
class DecryptionFailedException : Exception()
\ No newline at end of file
package net.mamoe.mirai.utils.config
import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.Yaml
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.nio.charset.Charset
import java.util.*
/**
* YAML-TYPE CONFIG
* Thread SAFE
*
* @author NaturalHG
*/
class MiraiConfig(private val root: File) : MiraiConfigSection<Any>(parse(Objects.requireNonNull(root))) {
@Synchronized
fun save() {
val dumperOptions = DumperOptions()
dumperOptions.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
val yaml = Yaml(dumperOptions)
val content = yaml.dump(this)
try {
ByteArrayInputStream(content.toByteArray()).transferTo(FileOutputStream(this.root))
} catch (e: IOException) {
e.printStackTrace()
}
}
companion object {
@Suppress("UNCHECKED_CAST")
private fun parse(file: File): MutableMap<String, Any>? {
/*
if (!file.toURI().getPath().contains(MiraiServer.getInstance().getParentFolder().getPath())) {
file = new File(MiraiServer.getInstance().getParentFolder().getPath(), file.getName());
}*/
if (!file.exists()) {
try {
if (!file.createNewFile()) {
return linkedMapOf()
}
} catch (e: IOException) {
e.printStackTrace()
return linkedMapOf()
}
}
val dumperOptions = DumperOptions()
dumperOptions.defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
val yaml = Yaml(dumperOptions)
return yaml.loadAs<LinkedHashMap<*, *>>(file.readLines(Charset.defaultCharset()).joinToString("\n"), LinkedHashMap::class.java) as MutableMap<String, Any>?
}
}
}
package net.mamoe.mirai.utils.config;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
/**
* @author NaturalHG
*/
public class MiraiConfigSection<T> extends LinkedHashMap<String, T> {
public MiraiConfigSection(){
super();
}
public MiraiConfigSection(Map<String, T> copyOfMap) {
super(new LinkedHashMap<>(copyOfMap));
}
public int getInt(String key){
return Integer.parseInt(this.get(key).toString());
}
public int getIntOrDefault(String key, int defaultV){
Object result = this.getOrDefault(key, null);
try {
return result == null ? defaultV : Integer.parseInt(result.toString());
}catch (NumberFormatException ignored){
return defaultV;
}
}
public int getIntOrThrow(String key, Callable<Throwable> throwableCallable) throws Throwable {
Object result = this.getOrDefault(key, null);
if(result == null){
throw throwableCallable.call();
}
try {
return Integer.parseInt(result.toString());
}catch (NumberFormatException ignored){
throw throwableCallable.call();
}
}
public double getDouble(String key){
return Double.parseDouble(this.get(key).toString());
}
public double getDoubleOrDefault(String key, double defaultV){
Object result = this.getOrDefault(key, null);
try {
return result == null ? defaultV : Double.parseDouble(result.toString());
}catch (NumberFormatException ignored){
return defaultV;
}
}
public double getDoubleOrThrow(String key, Callable<Throwable> throwableCallable) throws Throwable {
Object result = this.getOrDefault(key, null);
if(result == null){
throw throwableCallable.call();
}
try {
return Double.parseDouble(result.toString());
}catch (NumberFormatException ignored){
throw throwableCallable.call();
}
}
public float getFloat(String key){
return Float.parseFloat(this.get(key).toString());
}
public float getFloatOrDefault(String key, float defaultV){
Object result = this.getOrDefault(key, null);
try {
return result == null ? defaultV : Float.parseFloat(result.toString());
}catch (NumberFormatException ignored){
return defaultV;
}
}
public float getFloatOrThrow(String key, Callable<Throwable> throwableCallable) throws Throwable {
Object result = this.getOrDefault(key, null);
if(result == null){
throw throwableCallable.call();
}
try {
return Float.parseFloat(result.toString());
}catch (NumberFormatException ignored){
throw throwableCallable.call();
}
}
public long getLong(String key){
return Long.parseLong(this.get(key).toString());
}
public long getLongOrDefault(String key, long defaultV){
Object result = this.getOrDefault(key, null);
try {
return result == null ? defaultV : Long.parseLong(result.toString());
}catch (NumberFormatException ignored){
return defaultV;
}
}
public long getLongOrThrow(String key, Callable<Throwable> throwableCallable) throws Throwable {
Object result = this.getOrDefault(key, null);
if(result == null){
throw throwableCallable.call();
}
try {
return Long.parseLong(result.toString());
}catch (NumberFormatException ignored){
throw throwableCallable.call();
}
}
public String getString(String key){
return String.valueOf(this.get(key));
}
public String getStringOrDefault(String key, String defaultV){
Object result = this.getOrDefault(key, null);
return result==null?defaultV:result.toString();
}
public String getStringOrThrow(String key, Supplier<Throwable> exceptionSupplier) throws Throwable {
Object result = this.getOrDefault(key, null);
if(result == null){
throw exceptionSupplier.get();
}
return result.toString();
}
@Override
public T put(String key, T value) {
return super.put(key, value);
}
@SuppressWarnings("unchecked")
public <D> MiraiConfigSection<D> getTypedSection(String key){
var content = this.get(key);
if(content instanceof MiraiConfigSection){
return (MiraiConfigSection<D>) content;
}
if(content instanceof Map){
return new MiraiConfigSection<>(
(LinkedHashMap<String, D>) content
);
}
return null;
}
public MiraiConfigSection<Object> getSection(String key){
return this.getTypedSection(key);
}
@SuppressWarnings("unchecked")
public <D extends T> D getAsOrDefault(String key, D defaultV){
return (D)this.getOrDefault(key,defaultV);
}
@SuppressWarnings("unchecked")
public <D extends T> D getAsOrDefault(String key, Supplier<D> supplier) {
D d = (D) this.get(key);
if (d != null) {
return d;
}
return supplier.get();
}
@SuppressWarnings("unchecked")
public <D extends T> D getAs(String key) {
return (D) this.get(key);
}
}
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
package net.mamoe.mirai.utils.io package net.mamoe.mirai.utils.io
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.utils.toUHexString
import java.lang.reflect.Field import java.lang.reflect.Field
import java.util.* import java.util.*
import kotlin.math.max import kotlin.math.max
......
...@@ -4,7 +4,6 @@ import kotlinx.coroutines.Dispatchers ...@@ -4,7 +4,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.io.core.Closeable import kotlinx.io.core.Closeable
import kotlinx.io.core.IoBuffer import kotlinx.io.core.IoBuffer
import kotlinx.io.errors.IOException
import kotlinx.io.nio.read import kotlinx.io.nio.read
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.nio.channels.DatagramChannel import java.nio.channels.DatagramChannel
...@@ -43,6 +42,56 @@ actual class PlatformDatagramChannel actual constructor(serverHost: String, serv ...@@ -43,6 +42,56 @@ actual class PlatformDatagramChannel actual constructor(serverHost: String, serv
actual typealias ClosedChannelException = java.nio.channels.ClosedChannelException actual typealias ClosedChannelException = java.nio.channels.ClosedChannelException
actual class SendPacketInternalException actual constructor(cause: Throwable?) : IOException(cause)
actual class ReadPacketInternalException actual constructor(cause: Throwable?) : IOException(cause) /*
\ No newline at end of file
actual class PlatformDatagramChannel actual constructor(serverHost: String, serverPort: Short) : Closeable {
private val serverAddress: InetSocketAddress = InetSocketAddress(serverHost, serverPort.toInt())
@KtorExperimentalAPI
val socket = runBlocking { aSocket(ActorSelectorManager(Dispatchers.IO)).tcp()
.connect(remoteAddress = serverAddress) }
@KtorExperimentalAPI
val readChannel = socket.openReadChannel()
@KtorExperimentalAPI
val writeChannel = socket.openWriteChannel(true)
@KtorExperimentalAPI
@Throws(ReadPacketInternalException::class)
actual suspend fun read(buffer: IoBuffer) =
try {
readChannel.readAvailable(buffer)
} catch (e: ClosedChannelException) {
throw e
} catch (e: Throwable) {
throw ReadPacketInternalException(e)
}
@KtorExperimentalAPI
@Throws(SendPacketInternalException::class)
actual suspend fun send(buffer: IoBuffer) =
buffer.readDirect {
try {
writeChannel.writeFully(it)
} catch (e: ClosedChannelException) {
throw e
} catch (e: Throwable) {
throw SendPacketInternalException(e)
}
}
@KtorExperimentalAPI
@Throws(IOException::class)
override fun close() {
socket.close()
}
@KtorExperimentalAPI
actual val isOpen: Boolean
get() = socket.isClosed.not()
}
*/
\ No newline at end of file
package net.mamoe.mirai.utils.setting
import org.ini4j.Profile
import java.io.IOException
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantLock
/**
* @author NaturalHG
*/
class MiraiSettingListSection : Vector<Any>(), MiraiSettingSection {
private val lock = ReentrantLock()
@Suppress("UNCHECKED_CAST")
fun <T> getAs(index: Int): T {
return super.get(index) as T
}
fun getInt(index: Int): Int {
return this.getAs(index)
}
fun getDouble(index: Int): Int {
return this.getAs(index)
}
fun getString(index: Int): Int {
return this.getAs(index)
}
fun getFloat(index: Int): Int {
return this.getAs(index)
}
@Synchronized
override fun saveAsSection(section: Profile.Section) {
section.clear()
val integer = AtomicInteger(0)
this.forEach { a -> section.put(integer.getAndAdd(1).toString(), a) }
}
@Throws(IOException::class)
override fun close() {
}
}
package net.mamoe.mirai.utils.setting
import org.ini4j.Profile
import java.io.IOException
import java.util.concurrent.ConcurrentHashMap
import kotlin.streams.toList
/**
* @author NaturalHG
*/
class MiraiSettingMapSection : ConcurrentHashMap<String, Any>(), MiraiSettingSection {
operator fun <T> get(key: String?, defaultValue: T): T {
if (key == null || key.isEmpty()) {
return defaultValue
}
return if (super.containsKey(key)) {
@Suppress("UNCHECKED_CAST")
super.get(key) as T
} else defaultValue
}
operator fun set(key: String, value: Any) {
this[key] = value
}
fun getInt(key: String): Int {
return this.getInt(key, 0)
}
fun getInt(key: String, defaultValue: Int): Int {
return Integer.parseInt(this[key, defaultValue].toString())
}
fun getDouble(key: String): Double {
return this.getDouble(key, 0.0)
}
fun getDouble(key: String, defaultValue: Double): Double {
return java.lang.Double.parseDouble(this[key, defaultValue].toString())
}
fun getFloat(key: String): Float {
return this.getFloat(key, 0f)
}
fun getFloat(key: String, defaultValue: Float): Float {
return java.lang.Float.parseFloat(this[key, defaultValue].toString())
}
fun getString(key: String): String {
return this.getString(key, "")
}
fun getString(key: String, defaultValue: String): String {
return this[key, defaultValue].toString()
}
fun getObject(key: String): Any? {
return this[key]
}
@Suppress("UNCHECKED_CAST")
fun <T> asList(): List<T> {
return this.values.stream().map { a -> a as T }.toList()
}
@Synchronized
override fun saveAsSection(section: Profile.Section) {
section.clear()
this.forEach{ key, value -> section.put(key, value) }
}
@Throws(IOException::class)
override fun close() {
}
}
package net.mamoe.mirai.utils.setting
import org.ini4j.Profile
import java.io.Closeable
/**
* @author NaturalHG
*/
interface MiraiSettingSection : Closeable {
fun saveAsSection(section: Profile.Section)
}
\ No newline at end of file
package net.mamoe.mirai.utils.setting
import org.ini4j.Config
import org.ini4j.Ini
import java.io.File
import java.io.IOException
import java.util.*
import java.util.concurrent.ConcurrentHashMap
/**
* Thread-safe Mirai Config <br></br>
* Only supports `INI` format <br></br>
* Supports [Map] and [List]
*
* @author NaturalHG
*/
class MiraiSettings(file: File)
/*
public MiraiSettings(MiraiPluginBase pluginBase, String filename) {
// TODO: 2019/9/6 每个插件独立文件夹存放
this(new File(filename));
}*/ {
private val file: File
private var ini: Ini
private val cacheSection = ConcurrentHashMap<String, MiraiSettingSection>()
init {
val f = file.takeIf { it.name.contains(".") } ?: File(file.path + ".ini")
this.file = f
if (!f.exists() && !f.createNewFile()) {
throw RuntimeException("cannot create config file $f")
}
val config = Config()
config.isMultiSection = true
ini = Ini()
ini.config = config
ini.load(this.file.toURI().toURL())
}
@Synchronized
fun setSection(key: String, section: MiraiSettingSection) {
cacheSection[key] = section
}
@Synchronized
fun getMapSection(key: String): MiraiSettingMapSection {
if (!cacheSection.containsKey(key)) {
val section = MiraiSettingMapSection()
if (ini.containsKey(key)) {
section.putAll(ini[key]!!)
}
cacheSection[key] = section
}
return cacheSection[key] as MiraiSettingMapSection
}
@Synchronized
fun getListSection(key: String): MiraiSettingListSection {
if (!cacheSection.containsKey(key)) {
val section = MiraiSettingListSection()
if (ini.containsKey(key)) {
section.addAll(ini[key]!!.values)
}
cacheSection[key] = section
}
return cacheSection[key] as MiraiSettingListSection
}
@Synchronized
fun save() {
cacheSection.forEach { (k, a) ->
if (!ini.containsKey(k)) {
ini.put(k, "", HashMap<Any, Any>())
}
a.saveAsSection(ini[k]!!)
}
this.clearCache()
try {
ini.store(file)
} catch (e: IOException) {
e.printStackTrace()
}
}
@Synchronized
fun clearCache() {
cacheSection.clear()
}
}
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
import net.mamoe.mirai.contact.groupId
import net.mamoe.mirai.contact.toInternalId
import net.mamoe.mirai.network.protocol.tim.packet.action.GroupImageIdRequestPacket
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.io.readRemainingBytes
import net.mamoe.mirai.utils.io.toUHexString
import net.mamoe.mirai.utils.toExternalImage
import java.io.File
import javax.imageio.ImageIO
val sessionKey: ByteArray = "F1 ED F2 BC 55 17 7B FE CC CC F3 08 D1 8D A7 0E".hexToBytes()
fun main() = println({
val image = ImageIO.read(File("C:\\Users\\Him18\\Desktop\\test2.gif").readBytes().inputStream()).toExternalImage("png")
// File("C:\\Users\\Him18\\Desktop\\test2.jpg").writeBytes(image.fileData.readBytes())
GroupImageIdRequestPacket(
1994701021u,
580266363u.groupId().toInternalId(),
image,
sessionKey
).packet.readRemainingBytes().toUHexString()
}())
@file:Suppress("UNUSED_VARIABLE") @file:Suppress("UNUSED_VARIABLE")
import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.stringOfWitch import net.mamoe.mirai.utils.io.stringOfWitch
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
......
...@@ -7,7 +7,7 @@ import javafx.scene.layout.Region ...@@ -7,7 +7,7 @@ import javafx.scene.layout.Region
import javafx.scene.paint.Color import javafx.scene.paint.Color
import javafx.scene.text.FontWeight import javafx.scene.text.FontWeight
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.read
import net.mamoe.mirai.utils.io.stringOfWitch import net.mamoe.mirai.utils.io.stringOfWitch
import net.mamoe.mirai.utils.readUnsignedVarInt import net.mamoe.mirai.utils.readUnsignedVarInt
......
...@@ -15,9 +15,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket ...@@ -15,9 +15,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.event.UnknownServerEventPacket import net.mamoe.mirai.network.protocol.tim.packet.event.UnknownServerEventPacket
import net.mamoe.mirai.utils.DecryptionFailedException import net.mamoe.mirai.utils.DecryptionFailedException
import net.mamoe.mirai.utils.decryptBy import net.mamoe.mirai.utils.decryptBy
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.toUHexString
import org.pcap4j.core.BpfProgram.BpfCompileMode import org.pcap4j.core.BpfProgram.BpfCompileMode
import org.pcap4j.core.PacketListener import org.pcap4j.core.PacketListener
import org.pcap4j.core.PcapNetworkInterface import org.pcap4j.core.PcapNetworkInterface
...@@ -135,7 +133,6 @@ object Main { ...@@ -135,7 +133,6 @@ object Main {
println("密文body=" + remaining.toUHexString()) println("密文body=" + remaining.toUHexString())
println("解密body=解密失败") println("解密body=解密失败")
} }
} }
} }
......
...@@ -16,9 +16,9 @@ import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage ...@@ -16,9 +16,9 @@ import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
import net.mamoe.mirai.network.session import net.mamoe.mirai.network.session
import net.mamoe.mirai.qqAccount import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.toByteArray
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
import net.mamoe.mirai.utils.toByteArray
import net.mamoe.mirai.utils.toExternalImage import net.mamoe.mirai.utils.toExternalImage
import java.io.File import java.io.File
...@@ -36,6 +36,37 @@ private fun readTestAccount(): BotAccount? { ...@@ -36,6 +36,37 @@ private fun readTestAccount(): BotAccount? {
} }
} }
@Suppress("UNUSED_VARIABLE")
suspend fun main() {
val bot = Bot(
readTestAccount() ?: BotAccount(//填写你的账号
id = 1994701121u,
password = "123456"
)
)
// 覆盖默认的配置
bot.login {
randomDeviceName = false
}.requireSuccess()
bot.messageDSL()
directlySubscribe(bot)
//DSL 监听
subscribeAll<FriendMessageEvent> {
always {
//获取第一个纯文本消息
val firstText = it.message.firstOrNull<PlainText>()
}
}
demo2()
bot.network.awaitDisconnection()//等到直到断开连接
}
/** /**
* 使用 dsl 监听消息事件 * 使用 dsl 监听消息事件
* *
...@@ -237,38 +268,6 @@ suspend fun directlySubscribe(bot: Bot) { ...@@ -237,38 +268,6 @@ suspend fun directlySubscribe(bot: Bot) {
} }
} }
@Suppress("UNUSED_VARIABLE")
suspend fun main() {
val bot = Bot(
readTestAccount() ?: BotAccount(//填写你的账号
id = 1994701121u,
password = "123456"
)
)
// 覆盖默认的配置
bot.login {
randomDeviceName = false
}.requireSuccess()
bot.messageDSL()
directlySubscribe(bot)
//DSL 监听
subscribeAll<FriendMessageEvent> {
always {
//获取第一个纯文本消息
val firstText = it.message.firstOrNull<PlainText>()
}
}
demo2()
bot.network.awaitDisconnection()//等到直到断开连接
}
/** /**
* 实现功能: * 实现功能:
* 对机器人说 "记笔记", 机器人记录之后的所有消息. * 对机器人说 "记笔记", 机器人记录之后的所有消息.
......
rootProject.name = 'mirai' rootProject.name = 'mirai'
include(':mirai-core') include(':mirai-core')
include(':mirai-console') include(':mirai-console')
include(':mirai-api') include(':mirai-api')
include(':mirai-demos:mirai-demo-1') include(':mirai-demos:mirai-demo-1')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment