Commit 71225648 authored by ryoii's avatar ryoii

Merge remote-tracking branch 'origin/master'

parents 02f6d3d0 0126e4ce
---
name: 特性申请
about: 申请 mirai 添加新的特性
title: ''
labels: feature
assignees: ''
---
以下相关功能将会被直接拒绝:
- 资金相关: 红包, 转账
- 主动加好友, 主动加群, 主动邀请加入群
可以提交的内容:
- 有较高使用频率的协议 (低频功能不接受提议)
- 架构 / 功能上的建议 (欢迎)
请删除以上内容后再描述问题
---
name: Bug 报告
about: 提交一个 bug
title: ''
labels: " bug "
assignees: ''
---
提交前请先确认:
- 已经在现有 issue 列表中检查并未发现重复问题
- 确认问题属于 `mirai-core`, 而不是 `mirai-console` 或其他
若有报错, 请尽可能附加完整控制台日志或截图
若无报错, 请尽可能描述如何复现这个问题
请删除以上内容后再描述问题
# This is a basic workflow to help you get started with Actions
name: Shadow
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
release:
types:
- created
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle and shadowJar
run: ./gradlew :mirai-core:shadowJar :mirai-core-qqandroid:shadowJar
- name: Upload artifact
uses: actions/upload-artifact@v1.0.0
with:
# Artifact name
name: mirai-core-all
# Directory containing files to upload
path: "mirai-core/build/libs/mirai-core-*-all.jar"
- name: Upload artifact
uses: actions/upload-artifact@v1.0.0
with:
# Artifact name
name: mirai-core-qqandroid-all
# Directory containing files to upload
path: "mirai-core-qqandroid/build/libs/mirai-core-qqandroid-*-all.jar"
......@@ -2,12 +2,29 @@
开发版本. 频繁更新, 不保证高稳定性
## `0.25.0` 还未发布
## `0.27.0` 2020/3/8
- 支持 `XML`, `Json`, `LightApp``RichMessage`
## `0.26.2` 2020/3/8
- 新增 `MessageChain.repeat``MessageChain.times`
- JVM 平台下 `PlatformLogger` 可重定向输出
- 修复 `NullMessageChain.equals` 判断不正确的问题
- 新增 `PlainText.of` 以应对一些特殊情况
## `0.26.1` 2020/3/8
- 重写 Jce 序列化, 提升反序列性能
- 更新 `Kotlin` 版本到 1.3.70
- 更新 `kotlinx.coroutines`, `atomicfu`, `kotlinx.coroutines` 依赖版本
## `0.26.0` 2020/3/7
- 使用 `kotlinx.io` 而不是 `ktor.io`
- 修复 #111, #108, #116, #112
## `0.25.0` 2020/3/6
- 适配 8.2.7 版本(2020 年 3 月)协议
- 全面的 `Image` 类型: Online/Offline Image, Friend/Group Image
- 修复查询图片链接时好友图片链接错误的问题
- Kotlin 1.3.70
- 修复事件处理错误 (#107)
- 修复 bugs: #105, #106, #107
## `0.24.1` 2020/3/3
- 修复 `Member` 的委托 `QQ` 弱引用被释放的问题
......
......@@ -61,7 +61,7 @@ Demos: [mirai-demos](https://github.com/mamoe/mirai-demos)
### 使用者
- [mirai-console](https://github.com/mamoe/mirai/tree/master/mirai-console) 支持插件 **本模块正在完善**
- [mirai-console](https://github.com/mamoe/mirai-console) 支持插件 **本模块正在完善**
### 我是其他平台的使用者
......
......@@ -4,12 +4,11 @@ import java.util.*
buildscript {
repositories {
mavenLocal()
maven { setUrl("https://mirrors.huaweicloud.com/repository/maven") }
maven(url = "https://mirrors.huaweicloud.com/repository/maven")
jcenter()
mavenCentral()
// mavenCentral()
google()
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-dev") }
// maven (url="https://dl.bintray.com/kotlin/kotlin-eap")
}
dependencies {
......@@ -41,11 +40,10 @@ allprojects {
repositories {
mavenLocal()
maven { setUrl("https://mirrors.huaweicloud.com/repository/maven") }
maven(url = "https://mirrors.huaweicloud.com/repository/maven")
jcenter()
mavenCentral()
// mavenCentral()
google()
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-dev") }
// maven (url="https://dl.bintray.com/kotlin/kotlin-eap")
}
}
\ No newline at end of file
# style guide
kotlin.code.style=official
# config
miraiVersion=0.24.1
miraiVersion=0.27.0
kotlin.incremental.multiplatform=true
kotlin.parallel.tasks.in.project=true
# kotlin
kotlinVersion=1.3.61
kotlinVersion=1.3.70
# kotlin libraries
serializationVersion=0.14.0
coroutinesVersion=1.3.3
serializationVersion=0.20.0
coroutinesVersion=1.3.4
atomicFuVersion=0.14.1
kotlinXIoVersion=0.1.16
coroutinesIoVersion=0.1.16
# utility
ktorVersion=1.3.1
\ No newline at end of file
......@@ -8,9 +8,12 @@ plugins {
id("com.jfrog.bintray") version "1.8.4-jetbrains-3"
}
apply(plugin = "com.github.johnrengelman.shadow")
val kotlinVersion: String by rootProject.ext
val atomicFuVersion: String by rootProject.ext
val coroutinesVersion: String by rootProject.ext
val kotlinXIoVersion: String by rootProject.ext
val coroutinesIoVersion: String by rootProject.ext
......@@ -66,6 +69,7 @@ kotlin {
api(kotlin("stdlib", kotlinVersion))
api("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
api(kotlinx("io", kotlinXIoVersion))
api(kotlinx("coroutines-io", coroutinesIoVersion))
api(kotlinx("coroutines-core", coroutinesVersion))
}
......@@ -73,6 +77,7 @@ kotlin {
commonMain {
dependencies {
api(kotlinx("serialization-runtime-common", serializationVersion))
api(kotlinx("serialization-protobuf-common", serializationVersion))
}
}
commonTest {
......@@ -86,6 +91,7 @@ kotlin {
if (isAndroidSDKAvailable) {
val androidMain by getting {
dependencies {
api(kotlinx("serialization-protobuf", serializationVersion))
}
}
......
......@@ -21,7 +21,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
*/
@Suppress("INAPPLICABLE_JVM_NAME")
actual object QQAndroid : BotFactory {
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class)
@JvmName("newBot")
actual override fun Bot(context: Context, qq: Long, password: String, configuration: BotConfiguration): Bot {
return QQAndroidBot(context, BotAccount(qq, password), configuration)
......@@ -30,7 +30,7 @@ actual object QQAndroid : BotFactory {
/**
* 使用指定的 [配置][configuration] 构造 [Bot] 实例
*/
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class)
@JvmName("newBot")
actual override fun Bot(
context: Context,
......
......@@ -9,15 +9,188 @@
package net.mamoe.mirai.qqandroid
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.consumeEachBufferRange
import io.ktor.utils.io.core.Input
import io.ktor.utils.io.core.readBytes
import kotlinx.coroutines.io.*
import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.Context
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.ByteArrayPool
import net.mamoe.mirai.utils.io.toReadPacket
import java.nio.ByteBuffer
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class)
internal actual class QQAndroidBot
actual constructor(
context: Context,
account: BotAccount,
configuration: BotConfiguration
) : QQAndroidBotBase(context, account, configuration)
\ No newline at end of file
) : QQAndroidBotBase(context, account, configuration)
@OptIn(MiraiInternalAPI::class)
@Suppress("DEPRECATION")
internal actual fun ByteReadChannel.toKotlinByteReadChannel(): kotlinx.coroutines.io.ByteReadChannel {
return object : kotlinx.coroutines.io.ByteReadChannel {
override val availableForRead: Int
get() = this@toKotlinByteReadChannel.availableForRead
override val isClosedForRead: Boolean
get() = this@toKotlinByteReadChannel.isClosedForRead
override val isClosedForWrite: Boolean
get() = this@toKotlinByteReadChannel.isClosedForWrite
@Suppress("DEPRECATION_ERROR", "OverridingDeprecatedMember")
override var readByteOrder: ByteOrder
get() = when (this@toKotlinByteReadChannel.readByteOrder) {
io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN -> ByteOrder.BIG_ENDIAN
io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN -> ByteOrder.LITTLE_ENDIAN
}
set(value) {
this@toKotlinByteReadChannel.readByteOrder = when (value) {
ByteOrder.BIG_ENDIAN -> io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN
ByteOrder.LITTLE_ENDIAN -> io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN
}
}
@Suppress("DEPRECATION_ERROR", "DEPRECATION", "OverridingDeprecatedMember")
override val totalBytesRead: Long
get() = this@toKotlinByteReadChannel.totalBytesRead
override fun cancel(cause: Throwable?): Boolean = this@toKotlinByteReadChannel.cancel(cause)
override suspend fun consumeEachBufferRange(visitor: ConsumeEachBufferVisitor) =
this@toKotlinByteReadChannel.consumeEachBufferRange(visitor)
override suspend fun discard(max: Long): Long = this@toKotlinByteReadChannel.discard(max)
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
@ExperimentalIoApi
override fun <R> lookAhead(visitor: LookAheadSession.() -> R): R {
return this@toKotlinByteReadChannel.lookAhead l@{
visitor(object : LookAheadSession {
override fun consumed(n: Int) {
return this@l.consumed(n)
}
override fun request(skip: Int, atLeast: Int): ByteBuffer? {
return this@l.request(skip, atLeast)
}
})
}
}
@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
@ExperimentalIoApi
override suspend fun <R> lookAheadSuspend(visitor: suspend LookAheadSuspendSession.() -> R): R =
this@toKotlinByteReadChannel.lookAheadSuspend l@{
visitor(object : LookAheadSuspendSession {
override suspend fun awaitAtLeast(n: Int): Boolean {
return this@l.awaitAtLeast(n)
}
override fun consumed(n: Int) {
return this@l.consumed(n)
}
override fun request(skip: Int, atLeast: Int): ByteBuffer? {
return this@l.request(skip, atLeast)
}
})
}
override suspend fun read(min: Int, consumer: (ByteBuffer) -> Unit) =
this@toKotlinByteReadChannel.read(min, consumer)
override suspend fun readAvailable(dst: ByteBuffer): Int = this@toKotlinByteReadChannel.readAvailable(dst)
override suspend fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int =
this@toKotlinByteReadChannel.readAvailable(dst, offset, length)
override suspend fun readAvailable(dst: IoBuffer): Int {
ByteArrayPool.useInstance {
val read = this@toKotlinByteReadChannel.readAvailable(it, 0, it.size)
dst.writeFully(it, 0, read)
return read
}
}
override suspend fun readBoolean(): Boolean = this@toKotlinByteReadChannel.readBoolean()
override suspend fun readByte(): Byte = this@toKotlinByteReadChannel.readByte()
override suspend fun readDouble(): Double = this@toKotlinByteReadChannel.readDouble()
override suspend fun readFloat(): Float = this@toKotlinByteReadChannel.readFloat()
override suspend fun readFully(dst: ByteBuffer): Int {
TODO("not implemented")
}
override suspend fun readFully(dst: ByteArray, offset: Int, length: Int) =
this@toKotlinByteReadChannel.readFully(dst, offset, length)
override suspend fun readFully(dst: IoBuffer, n: Int) {
ByteArrayPool.useInstance {
dst.writeFully(it, 0, this.readAvailable(it, 0, it.size))
}
}
override suspend fun readInt(): Int = this@toKotlinByteReadChannel.readInt()
override suspend fun readLong(): Long = this@toKotlinByteReadChannel.readLong()
override suspend fun readPacket(size: Int, headerSizeHint: Int): ByteReadPacket {
return this@toKotlinByteReadChannel.readPacket(size, headerSizeHint).readBytes().toReadPacket()
}
override suspend fun readRemaining(limit: Long, headerSizeHint: Int): ByteReadPacket {
return this@toKotlinByteReadChannel.readRemaining(limit, headerSizeHint).readBytes().toReadPacket()
}
@OptIn(ExperimentalIoApi::class)
@ExperimentalIoApi
override fun readSession(consumer: ReadSession.() -> Unit) {
@Suppress("DEPRECATION")
this@toKotlinByteReadChannel.readSession lambda@{
consumer(object : ReadSession {
override val availableForRead: Int
get() = this@lambda.availableForRead
override fun discard(n: Int): Int = this@lambda.discard(n)
override fun request(atLeast: Int): IoBuffer? {
val ioBuffer: io.ktor.utils.io.core.IoBuffer = this@lambda.request(atLeast) ?: return null
val buffer = IoBuffer.Pool.borrow()
val bytes = (ioBuffer as Input).readBytes()
buffer.writeFully(bytes)
return buffer
}
})
}
}
override suspend fun readShort(): Short = this@toKotlinByteReadChannel.readShort()
@Suppress("EXPERIMENTAL_OVERRIDE", "EXPERIMENTAL_API_USAGE")
@ExperimentalIoApi
override suspend fun readSuspendableSession(consumer: suspend SuspendableReadSession.() -> Unit) =
this@toKotlinByteReadChannel.readSuspendableSession l@{
consumer(object : SuspendableReadSession {
override val availableForRead: Int
get() = this@l.availableForRead
override suspend fun await(atLeast: Int): Boolean = this@l.await(atLeast)
override fun discard(n: Int): Int = this@l.discard(n)
override fun request(atLeast: Int): IoBuffer? {
@Suppress("DuplicatedCode") val ioBuffer: io.ktor.utils.io.core.IoBuffer =
this@l.request(atLeast) ?: return null
val buffer = IoBuffer.Pool.borrow()
val bytes = (ioBuffer as Input).readBytes()
buffer.writeFully(bytes)
return buffer
}
})
}
override suspend fun readUTF8Line(limit: Int): String? = this@toKotlinByteReadChannel.readUTF8Line(limit)
override suspend fun <A : Appendable> readUTF8LineTo(out: A, limit: Int): Boolean =
this@toKotlinByteReadChannel.readUTF8LineTo(out, limit)
}
}
\ No newline at end of file
......@@ -9,9 +9,9 @@
package net.mamoe.mirai.qqandroid
import io.ktor.utils.io.core.Closeable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.io.core.Closeable
import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.*
......@@ -78,6 +78,7 @@ internal class QQImpl(
return MessageReceipt(source, this, null)
}
@OptIn(MiraiInternalAPI::class)
override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = try {
if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
......@@ -111,7 +112,7 @@ internal class QQImpl(
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
}
is LongConn.OffPicUp.Response.RequireUpload -> {
Http.postImage(
MiraiPlatformUtils.Http.postImage(
"0x6ff0070",
bot.uin,
null,
......@@ -358,7 +359,7 @@ internal class MemberInfoImpl(
override val muteTimestamp: Int = jceInfo.dwShutupTimestap?.toInt() ?: 0
}
@UseExperimental(ExperimentalContracts::class)
@OptIn(ExperimentalContracts::class)
internal fun GroupImpl.Companion.checkIsInstance(expression: Boolean) {
contract {
returns() implies expression
......@@ -367,13 +368,14 @@ internal fun GroupImpl.Companion.checkIsInstance(expression: Boolean) {
}
@Suppress("PropertyName")
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class)
internal class GroupImpl(
bot: QQAndroidBot, override val coroutineContext: CoroutineContext,
override val id: Long,
groupInfo: GroupInfo,
members: Sequence<MemberInfo>
) : Group() {
@Suppress("\"RemoveEmptyClassBody\"") // things will go wrong after removal, don't try
companion object {
}
......@@ -383,7 +385,7 @@ internal class GroupImpl(
override lateinit var owner: Member
@UseExperimental(MiraiExperimentalAPI::class)
@OptIn(MiraiExperimentalAPI::class)
override val botAsMember: Member by lazy {
Member(object : MemberInfo {
override val nameCard: String
......@@ -401,7 +403,7 @@ internal class GroupImpl(
})
}
@UseExperimental(MiraiExperimentalAPI::class)
@OptIn(MiraiExperimentalAPI::class)
override lateinit var botPermission: MemberPermission
var _botMuteTimestamp: Int = groupInfo.botMuteRemaining
......@@ -557,10 +559,10 @@ internal class GroupImpl(
TODO("not implemented")
}
@UseExperimental(MiraiExperimentalAPI::class)
@OptIn(MiraiExperimentalAPI::class)
override fun Member(memberInfo: MemberInfo): Member {
return MemberImpl(
@UseExperimental(LowLevelAPI::class)
@OptIn(LowLevelAPI::class)
bot._lowLevelNewQQ(memberInfo) as QQImpl,
this,
this.coroutineContext,
......
......@@ -11,8 +11,8 @@ package net.mamoe.mirai.qqandroid
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.BotImpl
import net.mamoe.mirai.LowLevelAPI
......@@ -36,14 +36,14 @@ import net.mamoe.mirai.utils.*
import kotlin.collections.asSequence
import kotlin.coroutines.CoroutineContext
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class)
internal expect class QQAndroidBot constructor(
context: Context,
account: BotAccount,
configuration: BotConfiguration
) : QQAndroidBotBase
@UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
internal abstract class QQAndroidBotBase constructor(
context: Context,
account: BotAccount,
......@@ -69,7 +69,7 @@ internal abstract class QQAndroidBotBase constructor(
override val friends: ContactList<QQ> = ContactList(LockFreeLinkedList())
override val selfQQ: QQ by lazy {
@UseExperimental(LowLevelAPI::class)
@OptIn(LowLevelAPI::class)
_lowLevelNewQQ(object : FriendInfo {
override val uin: Long get() = this@QQAndroidBotBase.uin
override val nick: String get() = this@QQAndroidBotBase.nick
......@@ -101,7 +101,7 @@ internal abstract class QQAndroidBotBase constructor(
return groups.delegate.getOrNull(uin)
}
@UseExperimental(LowLevelAPI::class)
@OptIn(LowLevelAPI::class)
override suspend fun _lowLevelQueryGroupList(): Sequence<Long> {
return network.run {
FriendList.GetTroopListSimplify(bot.client)
......@@ -109,7 +109,7 @@ internal abstract class QQAndroidBotBase constructor(
}.groups.asSequence().map { it.groupUin.shl(32) and it.groupCode }
}
@UseExperimental(LowLevelAPI::class)
@OptIn(LowLevelAPI::class)
override suspend fun _lowLevelQueryGroupInfo(groupCode: Long): GroupInfo = network.run {
TroopManagement.GetGroupInfo(
client = bot.client,
......@@ -117,7 +117,7 @@ internal abstract class QQAndroidBotBase constructor(
).sendAndExpect<GroupInfoImpl>(retry = 2)
}
@UseExperimental(LowLevelAPI::class)
@OptIn(LowLevelAPI::class)
override suspend fun _lowLevelQueryGroupMemberList(
groupUin: Long,
groupCode: Long,
......@@ -187,7 +187,7 @@ internal abstract class QQAndroidBotBase constructor(
}
}
@UseExperimental(LowLevelAPI::class)
@OptIn(LowLevelAPI::class)
override suspend fun _lowLevelRecallFriendMessage(friendId: Long, messageId: Long, time: Long) {
network.run {
val response: PbMessageSvc.PbMsgWithDraw.Response =
......@@ -198,7 +198,7 @@ internal abstract class QQAndroidBotBase constructor(
}
}
@UseExperimental(LowLevelAPI::class)
@OptIn(LowLevelAPI::class)
override suspend fun _lowLevelRecallGroupMessage(groupId: Long, messageId: Long) {
network.run {
val response: PbMessageSvc.PbMsgWithDraw.Response =
......@@ -222,6 +222,10 @@ internal abstract class QQAndroidBotBase constructor(
}
override suspend fun openChannel(image: Image): ByteReadChannel {
return Http.get<HttpResponse>(queryImageUrl(image)).content
return MiraiPlatformUtils.Http.get<HttpResponse>(queryImageUrl(image)).content.toKotlinByteReadChannel()
}
}
\ No newline at end of file
}
@Suppress("DEPRECATION")
@OptIn(MiraiInternalAPI::class)
internal expect fun io.ktor.utils.io.ByteReadChannel.toKotlinByteReadChannel(): ByteReadChannel
\ No newline at end of file
package net.mamoe.mirai.qqandroid.io.serialization
import kotlinx.io.core.Input
import kotlinx.io.core.Output
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialFormat
import kotlinx.serialization.SerializationStrategy
interface IOFormat : SerialFormat {
fun <T> dumpTo(serializer: SerializationStrategy<T>, ojb: T, output: Output)
fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T
}
......@@ -5,11 +5,19 @@
* Some code changed by Mamoe is annotated around "MIRAI MODIFY START" and "MIRAI MODIFY END"
*/
@file:Suppress("DEPRECATION_ERROR")
package net.mamoe.mirai.qqandroid.io.serialization
import kotlinx.io.*
import kotlinx.io.ByteArrayOutputStream
import kotlinx.io.ByteBuffer
import kotlinx.io.ByteOrder
import kotlinx.serialization.*
import kotlinx.serialization.internal.*
import kotlinx.serialization.builtins.ByteArraySerializer
import kotlinx.serialization.builtins.MapEntrySerializer
import kotlinx.serialization.builtins.SetSerializer
import kotlinx.serialization.internal.MapLikeSerializer
import kotlinx.serialization.internal.TaggedEncoder
import kotlinx.serialization.modules.EmptyModule
import kotlinx.serialization.modules.SerialModule
import kotlinx.serialization.protobuf.ProtoBuf
......@@ -33,15 +41,19 @@ internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefa
*
* 代码复制自 kotlinx.serialization. 修改部分已进行标注 (详见 "MIRAI MODIFY START")
*/
class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : AbstractSerialFormat(context), BinaryFormat {
@OptIn(InternalSerializationApi::class)
class ProtoBufWithNullableSupport(override val context: SerialModule = EmptyModule) : SerialFormat, BinaryFormat {
internal open inner class ProtobufWriter(val encoder: ProtobufEncoder) : TaggedEncoder<ProtoDesc>() {
public override val context
internal open inner class ProtobufWriter(private val encoder: ProtobufEncoder) : TaggedEncoder<ProtoDesc>() {
override val context
get() = this@ProtoBufWithNullableSupport.context
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder = when (desc.kind) {
override fun beginStructure(
descriptor: SerialDescriptor,
vararg typeSerializers: KSerializer<*>
): CompositeEncoder = when (descriptor.kind) {
StructureKind.LIST -> RepeatedWriter(encoder, currentTag)
StructureKind.CLASS, UnionKind.OBJECT, is PolymorphicKind -> ObjectWriter(currentTagOrNull, encoder)
StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> ObjectWriter(currentTagOrNull, encoder)
StructureKind.MAP -> MapRepeatedWriter(currentTagOrNull, encoder)
else -> throw SerializationException("Primitives are not supported at top-level")
}
......@@ -82,12 +94,15 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
// encode maps as collection of map entries, not merged collection of key-values
serializer.descriptor is MapLikeDescriptor -> {
serializer.descriptor.kind == StructureKind.MAP -> {
val serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
HashSetSerializer(mapEntrySerial).serialize(this, (value as Map<*, *>).entries)
SetSerializer(mapEntrySerial).serialize(this, (value as Map<*, *>).entries)
}
serializer.descriptor == ByteArraySerializer.descriptor -> encoder.writeBytes(value as ByteArray, popTag().first)
serializer.descriptor == ByteArraySerializer().descriptor -> encoder.writeBytes(
value as ByteArray,
popTag().first
)
else -> serializer.serialize(this, value)
}
}
......@@ -96,7 +111,7 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
val parentTag: ProtoDesc?, private val parentEncoder: ProtobufEncoder,
private val stream: ByteArrayOutputStream = ByteArrayOutputStream()
) : ProtobufWriter(ProtobufEncoder(stream)) {
override fun endEncode(desc: SerialDescriptor) {
override fun endEncode(descriptor: SerialDescriptor) {
if (parentTag != null) {
parentEncoder.writeBytes(stream.toByteArray(), parentTag.first)
} else {
......@@ -111,7 +126,8 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
else 2 to (parentTag?.second ?: ProtoNumberType.DEFAULT)
}
internal inner class RepeatedWriter(encoder: ProtobufEncoder, val curTag: ProtoDesc) : ProtobufWriter(encoder) {
internal inner class RepeatedWriter(encoder: ProtobufEncoder, private val curTag: ProtoDesc) :
ProtobufWriter(encoder) {
override fun SerialDescriptor.getTag(index: Int) = curTag
}
......@@ -141,8 +157,9 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
out.write(content)
}
@OptIn(ExperimentalStdlibApi::class)
fun writeString(value: String, tag: Int) {
val bytes = value.toUtf8Bytes()
val bytes = value.encodeToByteArray()
writeBytes(bytes, tag)
}
......@@ -228,17 +245,17 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
internal const val SIZE_DELIMITED = 2
internal const val i32 = 5
val plain = ProtoBufWithNullableSupport()
private val plain = ProtoBufWithNullableSupport()
override fun <T> dump(serializer: SerializationStrategy<T>, obj: T): ByteArray = plain.dump(serializer, obj)
override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T = plain.load(deserializer, bytes)
override fun install(module: SerialModule) = throw IllegalStateException("You should not install anything to global instance")
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray = plain.dump(serializer, value)
override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T =
plain.load(deserializer, bytes)
}
override fun <T> dump(serializer: SerializationStrategy<T>, obj: T): ByteArray {
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
val encoder = ByteArrayOutputStream()
val dumper = ProtobufWriter(ProtobufEncoder(encoder))
dumper.encode(serializer, obj)
dumper.encode(serializer, value)
return encoder.toByteArray()
}
......@@ -248,20 +265,3 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
}
internal fun InputStream.readExactNBytes(bytes: Int): ByteArray {
val array = ByteArray(bytes)
var read = 0
while (read < bytes) {
val i = this.read(array, read, bytes - read)
if (i == -1) throw IOException("Unexpected EOF")
read += i
}
return array
}
internal fun InputStream.readToByteBuffer(bytes: Int): ByteBuffer {
val arr = readExactNBytes(bytes)
val buf = ByteBuffer.allocate(bytes)
buf.put(arr).flip()
return buf
}
\ No newline at end of file
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.qqandroid.io.serialization.jce
import kotlinx.io.core.*
import net.mamoe.mirai.qqandroid.io.serialization.JceCharset
import net.mamoe.mirai.utils.io.readString
/**
* Jce Input. 需要手动管理 head.
*/
internal class JceInput(
val input: Input, val charset: JceCharset
) {
private var _head: JceHead? = null
val currentHead: JceHead get() = _head ?: throw EOFException("No current JceHead available")
val currentHeadOrNull: JceHead? get() = _head
init {
prepareNextHead()
}
/**
* 读取下一个 [JceHead] 并保存. 可通过 [currentHead] 获取这个 [JceHead].
*
* @return 是否成功读取. 返回 `false` 时代表 [Input.endOfInput]
*/
fun prepareNextHead(): Boolean {
return readNextHeadButDoNotAssignTo_Head().also { _head = it; } != null
}
fun nextHead(): JceHead {
if (!prepareNextHead()) {
throw EOFException("No more JceHead available")
}
return currentHead
}
/**
* 直接读取下一个 [JceHead] 并返回.
* 返回 `null` 则代表 [Input.endOfInput]
*/
@Suppress("FunctionName")
@OptIn(ExperimentalUnsignedTypes::class)
private fun readNextHeadButDoNotAssignTo_Head(): JceHead? {
if (input.endOfInput) {
return null
}
val var2 = input.readUByte()
val type = var2 and 15u
var tag = var2.toUInt() shr 4
if (tag == 15u) {
tag = input.readUByte().toUInt()
}
return JceHead(
tag = tag.toInt(),
type = type.toByte()
)
}
/**
* 使用这个 [JceHead].
* [block] 结束后将会 [准备下一个 [JceHead]][prepareNextHead]
*/
inline fun <R> useHead(crossinline block: (JceHead) -> R): R {
return currentHead.let(block).also { prepareNextHead() }
}
/**
* 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则返回 `null`
*/
inline fun <R> skipToHeadAndUseIfPossibleOrNull(tag: Int, crossinline block: (JceHead) -> R): R? {
return skipToHeadOrNull(tag)?.let(block).also { prepareNextHead() }
}
/**
* 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则抛出异常
*/
inline fun <R : Any> skipToHeadAndUseIfPossibleOrFail(
tag: Int,
crossinline message: () -> String = { "tag not found: $tag" },
crossinline block: (JceHead) -> R
): R {
return checkNotNull<R>(skipToHeadAndUseIfPossibleOrNull(tag, block), message)
}
tailrec fun skipToHeadOrNull(tag: Int): JceHead? {
val current: JceHead = currentHeadOrNull ?: return null // no backing field
return when {
current.tag > tag -> null // tag 大了,即找不到
current.tag == tag -> current // 满足需要.
else -> { // tag 小了
skipField(current.type)
check(prepareNextHead()) { "cannot skip to tag $tag, early EOF" }
skipToHeadOrNull(tag)
}
}
}
inline fun skipToHeadOrFail(
tag: Int,
message: () -> String = { "head not found: $tag" }
): JceHead {
return checkNotNull(skipToHeadOrNull(tag), message)
}
@OptIn(ExperimentalUnsignedTypes::class)
@PublishedApi
internal fun skipField(type: Byte): Unit = when (type) {
Jce.BYTE -> this.input.discardExact(1)
Jce.SHORT -> this.input.discardExact(2)
Jce.INT -> this.input.discardExact(4)
Jce.LONG -> this.input.discardExact(8)
Jce.FLOAT -> this.input.discardExact(4)
Jce.DOUBLE -> this.input.discardExact(8)
Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
Jce.STRING4 -> this.input.discardExact(this.input.readInt())
Jce.MAP -> { // map
repeat(skipToHeadAndUseIfPossibleOrFail(0) {
readJceIntValue(it)
} * 2) {
useHead { skipField(it.type) }
}
}
Jce.LIST -> { // list
repeat(skipToHeadAndUseIfPossibleOrFail(0) {
readJceIntValue(it)
}) {
useHead { skipField(it.type) }
}
}
Jce.STRUCT_BEGIN -> {
fun skipToStructEnd() {
var head: JceHead
do {
head = nextHead()
skipField(head.type)
} while (head.type.toInt() != 11)
}
skipToStructEnd()
}
Jce.STRUCT_END, Jce.ZERO_TYPE -> {
}
Jce.SIMPLE_LIST -> {
val head = nextHead()
check(head.type.toInt() == 0) { "skipField with invalid type, type value: " + type + ", " + head.type }
this.input.discardExact(
skipToHeadAndUseIfPossibleOrFail(0) {
readJceIntValue(it)
}
)
}
else -> error("invalid type: $type")
}
// region readers
fun readJceIntValue(head: JceHead): Int {
//println("readJceIntValue: $head")
return when (head.type) {
Jce.ZERO_TYPE -> 0
Jce.BYTE -> input.readByte().toInt()
Jce.SHORT -> input.readShort().toInt()
Jce.INT -> input.readInt()
else -> error("type mismatch: ${head.type}")
}
}
fun readJceShortValue(head: JceHead): Short {
return when (head.type) {
Jce.ZERO_TYPE -> 0
Jce.BYTE -> input.readByte().toShort()
Jce.SHORT -> input.readShort()
else -> error("type mismatch: ${head.type}")
}
}
fun readJceLongValue(head: JceHead): Long {
return when (head.type) {
Jce.ZERO_TYPE -> 0
Jce.BYTE -> input.readByte().toLong()
Jce.SHORT -> input.readShort().toLong()
Jce.INT -> input.readInt().toLong()
Jce.LONG -> input.readLong()
else -> error("type mismatch ${head.type}")
}
}
fun readJceByteValue(head: JceHead): Byte {
//println("readJceByteValue: $head")
return when (head.type) {
Jce.ZERO_TYPE -> 0
Jce.BYTE -> input.readByte()
else -> error("type mismatch: ${head.type}")
}
}
fun readJceFloatValue(head: JceHead): Float {
return when (head.type) {
Jce.ZERO_TYPE -> 0f
Jce.FLOAT -> input.readFloat()
else -> error("type mismatch: ${head.type}")
}
}
@OptIn(ExperimentalUnsignedTypes::class)
fun readJceStringValue(head: JceHead): String {
//println("readJceStringValue: $head")
return when (head.type) {
Jce.STRING1 -> input.readString(input.readUByte().toInt(), charset = charset.kotlinCharset)
Jce.STRING4 -> input.readString(
input.readUInt().toInt().also { require(it in 1 until 104857600) { "bad string length: $it" } },
charset = charset.kotlinCharset
)
else -> error("type mismatch: ${head.type}, expecting 6 or 7 (for string)")
}
}
fun readJceDoubleValue(head: JceHead): Double {
return when (head.type.toInt()) {
12 -> 0.0
4 -> input.readFloat().toDouble()
5 -> input.readDouble()
else -> error("type mismatch: ${head.type}")
}
}
fun readJceBooleanValue(head: JceHead): Boolean {
return readJceByteValue(head) == 1.toByte()
}
}
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.qqandroid.io.serialization.jce
import kotlinx.io.core.*
import kotlinx.serialization.BinaryFormat
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialFormat
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.modules.EmptyModule
import kotlinx.serialization.modules.SerialModule
import net.mamoe.mirai.qqandroid.io.serialization.IOFormat
import net.mamoe.mirai.qqandroid.io.serialization.JceCharset
import net.mamoe.mirai.qqandroid.io.serialization.JceOld
import net.mamoe.mirai.utils.io.toReadPacket
/**
* Jce 数据结构序列化和反序列化器.
*
* @author Him188
*/
class Jce(
override val context: SerialModule,
val charset: JceCharset
) : SerialFormat, IOFormat, BinaryFormat {
override fun <T> dumpTo(serializer: SerializationStrategy<T>, ojb: T, output: Output) {
output.writePacket(JceOld.byCharSet(this.charset).dumpAsPacket(serializer, ojb))
}
override fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T {
return JceDecoder(JceInput(input, charset), context).decodeSerializableValue(deserializer)
}
override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
return buildPacket { dumpTo(serializer, value, this) }.readBytes()
}
override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
return load(deserializer, bytes.toReadPacket())
}
companion object {
val UTF_8 = Jce(EmptyModule, JceCharset.UTF8)
val GBK = Jce(EmptyModule, JceCharset.GBK)
fun byCharSet(c: JceCharset): Jce {
return if (c == JceCharset.UTF8) UTF_8 else GBK
}
internal const val BYTE: Byte = 0
internal const val DOUBLE: Byte = 5
internal const val FLOAT: Byte = 4
internal const val INT: Byte = 2
internal const val JCE_MAX_STRING_LENGTH = 104857600
internal const val LIST: Byte = 9
internal const val LONG: Byte = 3
internal const val MAP: Byte = 8
internal const val SHORT: Byte = 1
internal const val SIMPLE_LIST: Byte = 13
internal const val STRING1: Byte = 6
internal const val STRING4: Byte = 7
internal const val STRUCT_BEGIN: Byte = 10
internal const val STRUCT_END: Byte = 11
internal const val ZERO_TYPE: Byte = 12
}
}
\ No newline at end of file
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.qqandroid.io.serialization.jce
import kotlinx.io.core.Output
import kotlinx.serialization.SerialInfo
/**
* 标注 JCE 序列化时使用的 ID
*/
@SerialInfo
@Target(AnnotationTarget.PROPERTY)
annotation class JceId(val id: Int)
/**
* 类中元素的 tag
*
* 保留这个结构, 为将来增加功能的兼容性.
*/
@PublishedApi
internal abstract class JceTag {
abstract val id: Int
internal var isSimpleByteArray: Boolean = false
}
internal object JceTagListElement : JceTag() {
override val id: Int get() = 0
override fun toString(): String {
return "JceTagListElement"
}
}
internal object JceTagMapEntryKey : JceTag() {
override val id: Int get() = 0
override fun toString(): String {
return "JceTagMapEntryKey"
}
}
internal object JceTagMapEntryValue : JceTag() {
override val id: Int get() = 1
override fun toString(): String {
return "JceTagMapEntryValue"
}
}
internal data class JceTagCommon(
override val id: Int
) : JceTag()
fun JceHead.checkType(type: Byte) {
check(this.type == type) { "type mismatch. Expected $type, actual ${this.type}" }
}
@PublishedApi
internal fun Output.writeJceHead(type: Byte, tag: Int) {
if (tag < 15) {
writeByte(((tag shl 4) or type.toInt()).toByte())
return
}
if (tag < 256) {
writeByte((type.toInt() or 0xF0).toByte())
writeByte(tag.toByte())
return
}
error("tag is too large: $tag")
}
@OptIn(ExperimentalUnsignedTypes::class)
inline class JceHead(private val value: Long) {
constructor(tag: Int, type: Byte) : this(tag.toLong().shl(32) or type.toLong())
val tag: Int get() = (value ushr 32).toInt()
val type: Byte get() = value.toUInt().toByte()
override fun toString(): String {
val typeString = when (type) {
Jce.BYTE -> "Byte"
Jce.DOUBLE -> "Double"
Jce.FLOAT -> "Float"
Jce.INT -> "Int"
Jce.LIST -> "List"
Jce.LONG -> "Long"
Jce.MAP -> "Map"
Jce.SHORT -> "Short"
Jce.SIMPLE_LIST -> "SimpleList"
Jce.STRING1 -> "String1"
Jce.STRING4 -> "String4"
Jce.STRUCT_BEGIN -> "StructBegin"
Jce.STRUCT_END -> "StructEnd"
Jce.ZERO_TYPE -> "Zero"
else -> error("illegal jce type: $type")
}
return "JceHead(tag=$tag, type=$type($typeString))"
}
}
\ No newline at end of file
......@@ -12,30 +12,35 @@
package net.mamoe.mirai.qqandroid.io.serialization
import io.ktor.utils.io.core.BytePacketBuilder
import io.ktor.utils.io.core.ByteReadPacket
import io.ktor.utils.io.core.readBytes
import io.ktor.utils.io.core.writeFully
import kotlinx.io.core.*
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.SerializationStrategy
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion2
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.firstValue
import net.mamoe.mirai.utils.io.read
import net.mamoe.mirai.utils.io.readPacketExact
import net.mamoe.mirai.utils.io.toReadPacket
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
fun <T : JceStruct> ByteArray.loadAs(deserializer: DeserializationStrategy<T>, c: JceCharset = JceCharset.UTF8): T {
return Jce.byCharSet(c).load(deserializer, this)
return Jce.byCharSet(c).load(deserializer, this.toReadPacket())
}
fun <T : JceStruct> BytePacketBuilder.writeJceStruct(serializer: SerializationStrategy<T>, struct: T, charset: JceCharset = JceCharset.GBK) {
this.writePacket(Jce.byCharSet(charset).dumpAsPacket(serializer, struct))
fun <T : JceStruct> BytePacketBuilder.writeJceStruct(
serializer: SerializationStrategy<T>,
struct: T,
charset: JceCharset = JceCharset.GBK
) {
Jce.byCharSet(charset).dumpTo(serializer, struct, this)
}
fun <T : JceStruct> ByteReadPacket.readJceStruct(
......@@ -43,7 +48,8 @@ fun <T : JceStruct> ByteReadPacket.readJceStruct(
charset: JceCharset = JceCharset.UTF8,
length: Int = this.remaining.toInt()
): T {
return Jce.byCharSet(charset).load(serializer, this, length)
@OptIn(MiraiInternalAPI::class)
return Jce.byCharSet(charset).load(serializer, this.readPacketExact(length))
}
/**
......@@ -73,18 +79,20 @@ fun <T : ProtoBuf> ByteReadPacket.decodeUniPacket(deserializer: DeserializationS
fun <R> ByteReadPacket.decodeUniRequestPacketAndDeserialize(name: String? = null, block: (ByteArray) -> R): R {
val request = this.readJceStruct(RequestPacket.serializer())
return block(if (name == null) when (request.iVersion.toInt()) {
return block(if (name == null) when (request.iVersion?.toInt() ?: 3) {
2 -> request.sBuffer.loadAs(RequestDataVersion2.serializer()).map.firstValue().firstValue()
3 -> request.sBuffer.loadAs(RequestDataVersion3.serializer()).map.firstValue()
else -> error("unsupported version ${request.iVersion}")
} else when (request.iVersion.toInt()) {
2 -> request.sBuffer.loadAs(RequestDataVersion2.serializer()).map.getOrElse(name) { error("cannot find $name") }.firstValue()
} else when (request.iVersion?.toInt() ?: 3) {
2 -> request.sBuffer.loadAs(RequestDataVersion2.serializer()).map.getOrElse(name) { error("cannot find $name") }
.firstValue()
3 -> request.sBuffer.loadAs(RequestDataVersion3.serializer()).map.getOrElse(name) { error("cannot find $name") }
else -> error("unsupported version ${request.iVersion}")
})
}
fun <T : JceStruct> T.toByteArray(serializer: SerializationStrategy<T>, c: JceCharset = JceCharset.GBK): ByteArray = Jce.byCharSet(c).dump(serializer, this)
fun <T : JceStruct> T.toByteArray(serializer: SerializationStrategy<T>, c: JceCharset = JceCharset.GBK): ByteArray =
Jce.byCharSet(c).dump(serializer, this)
fun <T : ProtoBuf> BytePacketBuilder.writeProtoBuf(serializer: SerializationStrategy<T>, v: T) {
this.writeFully(v.toByteArray(serializer))
......
......@@ -23,6 +23,7 @@ import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
import net.mamoe.mirai.utils.MiraiExperimentalAPI
......@@ -40,7 +41,7 @@ internal class MessageSourceFromServer(
?: error("cannot find sequenceId from ImMsgBody.SourceMsg")).toLong().shl(32) or
delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.and(0xFFFFFFFF)
override val toUin: Long get() = delegate.toUin
override val toUin: Long get() = delegate.toUin // always 0
override suspend fun ensureSequenceIdAvailable() {
// nothing to do
......@@ -118,38 +119,16 @@ internal class MessageSourceFromMsg(
}
private fun toJceDataImplForGroup(): ImMsgBody.SourceMsg {
val groupUin = Group.calculateGroupUinByGroupCode(groupId)
return ImMsgBody.SourceMsg(
origSeqs = listOf(delegate.msgHead.msgSeq),
senderUin = delegate.msgHead.fromUin,
toUin = groupUin,
toUin = 0,
flag = 1,
elems = delegate.msgBody.richText.elems,
type = 0,
time = delegate.msgHead.msgTime,
pbReserve = SourceMsg.ResvAttr(
origUids = messageRandom.toLong() and 0xffFFffFF
).toByteArray(SourceMsg.ResvAttr.serializer()),
srcMsg = MsgComm.Msg(
msgHead = MsgComm.MsgHead(
fromUin = delegate.msgHead.fromUin, // qq
toUin = groupUin, // group
msgType = delegate.msgHead.msgType, // 82?
c2cCmd = delegate.msgHead.c2cCmd,
msgSeq = delegate.msgHead.msgSeq,
msgTime = delegate.msgHead.msgTime,
msgUid = messageRandom.toLong() and 0xffFFffFF, // ok
groupInfo = MsgComm.GroupInfo(groupCode = groupId),
isSrcMsg = true
),
msgBody = ImMsgBody.MsgBody(
richText = ImMsgBody.RichText(
elems = elems
)
)
).toByteArray(MsgComm.Msg.serializer())
pbReserve = EMPTY_BYTE_ARRAY,
srcMsg = EMPTY_BYTE_ARRAY
)
}
......@@ -253,7 +232,7 @@ internal class MessageSourceFromSendFriend(
val sequenceId: Int,
override val originalMessage: MessageChain
) : MessageSourceFromSend() {
@UseExperimental(ExperimentalCoroutinesApi::class)
@OptIn(ExperimentalCoroutinesApi::class)
override val id: Long
get() = sequenceId.toLong().shl(32) or
messageRandom.toLong().and(0xFFFFFFFF)
......@@ -277,12 +256,12 @@ internal class MessageSourceFromSendGroup(
) : MessageSourceFromSend() {
private lateinit var sequenceIdDeferred: Deferred<Int>
@UseExperimental(ExperimentalCoroutinesApi::class)
@OptIn(ExperimentalCoroutinesApi::class)
override val id: Long
get() = sequenceIdDeferred.getCompleted().toLong().shl(32) or
messageRandom.toLong().and(0xFFFFFFFF)
@UseExperimental(MiraiExperimentalAPI::class)
@OptIn(MiraiExperimentalAPI::class)
internal fun startWaitingSequenceId(coroutineScope: CoroutineScope) {
sequenceIdDeferred =
coroutineScope.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(
......
......@@ -9,15 +9,14 @@
package net.mamoe.mirai.qqandroid.message
import io.ktor.utils.io.core.*
import kotlinx.io.core.*
import net.mamoe.mirai.LowLevelAPI
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiDebugAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.encodeToString
import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.read
import net.mamoe.mirai.utils.io.toByteArray
......@@ -219,7 +218,7 @@ private val atAllData = ImMsgBody.Elem(
)
)
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgBody.Elem> {
val elements = mutableListOf<ImMsgBody.Elem>()
......@@ -240,6 +239,25 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
elements.add(ImMsgBody.Elem(text = it.toJceData()))
elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " ")))
}
is LightApp -> elements.add(
ImMsgBody.Elem(
lightApp = ImMsgBody.LightAppElem(
data = byteArrayOf(1) + MiraiPlatformUtils.zip(it.content.toByteArray())
)
)
)
is RichMessage -> elements.add(
ImMsgBody.Elem(
richMsg = ImMsgBody.RichMsg(
serviceId = when (it) {
is XmlMessage -> 60
is JsonMessage -> 1
else -> error("unsupported RichMessage")
},
template1 = byteArrayOf(1) + MiraiPlatformUtils.zip(it.content.toByteArray())
)
)
)
is OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
......@@ -249,7 +267,7 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
is QuoteReplyToSend -> {
if (forGroup) {
check(it is QuoteReplyToSend.ToGroup) {
"sending a quote to group suing QuoteReplyToSend.ToFriend"
"sending a quote to group using QuoteReplyToSend.ToFriend"
}
if (it.sender is Member) {
transformOneMessage(it.createAt())
......@@ -266,9 +284,10 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
}
this.forEach(::transformOneMessage)
// if(this.any<QuoteReply>()){
elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes())))
// }
if (this.any<RichMessage>()) {
// 08 09 78 00 A0 01 81 DC 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 02 08 03 90 04 80 80 80 10 B8 04 00 C0 04 00
elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "08 09 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 C8 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 02 08 03 90 04 80 80 80 10 B8 04 00 C0 04 00".hexToBytes())))
} else elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes())))
return elements
}
......@@ -332,7 +351,7 @@ internal class OnlineFriendImageImpl(
}
}
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
internal fun MsgComm.Msg.toMessageChain(): MessageChain {
val elements = this.msgBody.richText.elems
......@@ -343,7 +362,7 @@ internal fun MsgComm.Msg.toMessageChain(): MessageChain {
}
// These two functions are not identical, dont combine.
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain {
val elements = this.elems!!
......@@ -368,7 +387,9 @@ private fun MessageChain.removeAtIfHasQuoteReply(): MessageChain =
}.asMessageChain()
} else this*/
@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class, LowLevelAPI::class)
@OptIn(
MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class, LowLevelAPI::class
)
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilder) {
this.forEach {
when {
......@@ -395,6 +416,23 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilde
}
}
}
it.lightApp != null -> {
val content = MiraiPlatformUtils.unzip(it.lightApp.data, 1).encodeToString()
message.add(LightApp(content))
}
it.richMsg != null -> {
val content = MiraiPlatformUtils.unzip(it.richMsg.template1, 1).encodeToString()
when (it.richMsg.serviceId) {
1 -> message.add(JsonMessage(content))
60 -> message.add(XmlMessage(content))
else -> {
@Suppress("DEPRECATION")
MiraiLogger.debug {
"unknown richMsg.serviceId: ${it.richMsg.serviceId}, content=${it.richMsg.template1.contentToString()}, \ntryUnzip=${content}"
}
}
}
}
}
}
......
......@@ -9,12 +9,15 @@
package net.mamoe.mirai.qqandroid.network
import io.ktor.utils.io.core.*
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket
import kotlinx.io.core.use
import net.mamoe.mirai.data.MultiPacket
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.*
......@@ -45,7 +48,7 @@ import kotlin.jvm.Volatile
import kotlin.time.ExperimentalTime
@Suppress("MemberVisibilityCanBePrivate")
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class)
internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler() {
override val bot: QQAndroidBot by bot.unsafeWeakRef()
override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job])
......@@ -67,14 +70,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
_packetReceiverJob?.join()
return this.launch(CoroutineName("Incoming Packet Receiver")) {
while (channel.isOpen) {
while (channel.isOpen && isActive) {
val rawInput = try {
channel.read()
} catch (e: CancellationException) {
return@launch
} catch (e: Throwable) {
if (this@QQAndroidBotNetworkHandler.isActive) {
BotOfflineEvent.Dropped(bot, e).broadcast()
bot.launch { BotOfflineEvent.Dropped(bot, e).broadcast() }
}
return@launch
}
......@@ -141,10 +144,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
continue@mainloop
}
is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> {
var ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url)
if (ticket == null) {
ticket = ""
}
val ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url).orEmpty()
response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
continue@mainloop
}
......@@ -183,7 +183,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
internal var pendingIncomingPackets: LockFreeLinkedList<KnownPacketFactories.IncomingPacket<*>>? =
LockFreeLinkedList()
@UseExperimental(MiraiExperimentalAPI::class, ExperimentalTime::class)
@OptIn(MiraiExperimentalAPI::class, ExperimentalTime::class)
override suspend fun init(): Unit = coroutineScope {
check(bot.isActive) { "bot is dead therefore network can't init" }
check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't init" }
......@@ -338,6 +338,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
suspend fun doHeartBeat(): Exception? {
val lastException: Exception?
try {
kotlin.runCatching {
Heartbeat.Alive(bot.client)
.sendAndExpect<Heartbeat.Alive.Response>(
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
retry = 2
)
return null
}
Heartbeat.Alive(bot.client)
.sendAndExpect<Heartbeat.Alive.Response>(
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
......@@ -372,10 +380,17 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
*
* @param input 一个完整的包的内容, 去掉开头的 int 包长度
*/
@UseExperimental(ExperimentalCoroutinesApi::class)
@OptIn(ExperimentalCoroutinesApi::class)
fun parsePacketAsync(input: Input): Job {
return this.launch(start = CoroutineStart.ATOMIC) {
input.use { parsePacket(it) }
return this.launch(
start = CoroutineStart.ATOMIC
) {
try {
input.use { parsePacket(it) }
} catch (e: Exception) {
// 傻逼协程吞异常
logger.error(e)
}
}
}
......
......@@ -11,9 +11,9 @@
package net.mamoe.mirai.qqandroid.network
import io.ktor.utils.io.core.*
import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic
import kotlinx.io.core.*
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.RawAccountIdUse
import net.mamoe.mirai.data.OnlineStatus
......@@ -40,7 +40,7 @@ import net.mamoe.mirai.utils.io.*
DOMAINS
Pskey: "openmobile.qq.com"
*/
@UseExperimental(MiraiExperimentalAPI::class, MiraiInternalAPI::class)
@OptIn(MiraiExperimentalAPI::class, MiraiInternalAPI::class)
@PublishedApi
internal open class QQAndroidClient(
context: Context,
......@@ -158,8 +158,8 @@ internal open class QQAndroidClient(
*/
val uin: Long get() = _uin
@UseExperimental(RawAccountIdUse::class)
@Suppress("PropertyName")
@OptIn(RawAccountIdUse::class)
@Suppress("PropertyName", "DEPRECATION_ERROR")
internal var _uin: Long = bot.account.id
var t530: ByteArray? = null
......@@ -191,8 +191,9 @@ internal open class QQAndroidClient(
lateinit var t104: ByteArray
}
@OptIn(MiraiInternalAPI::class)
internal fun generateTgtgtKey(guid: ByteArray): ByteArray =
md5(getRandomByteArray(16) + guid)
MiraiPlatformUtils.md5(getRandomByteArray(16) + guid)
internal class ReserveUinInfo(
......@@ -315,7 +316,7 @@ internal class Pt4Token(data: ByteArray, creationTime: Long, expireTime: Long) :
internal typealias PSKeyMap = MutableMap<String, PSKey>
internal typealias Pt4TokenMap = MutableMap<String, Pt4Token>
internal inline fun Input.readUShortLVString(): String = io.ktor.utils.io.core.String(this.readUShortLVByteArray())
internal inline fun Input.readUShortLVString(): String = kotlinx.io.core.String(this.readUShortLVByteArray())
internal inline fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt())
......
......@@ -16,25 +16,27 @@ import io.ktor.http.HttpStatusCode
import io.ktor.http.URLProtocol
import io.ktor.http.content.OutgoingContent
import io.ktor.http.userAgent
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.copyAndClose
import io.ktor.utils.io.core.Input
import io.ktor.utils.io.core.readAvailable
import io.ktor.utils.io.core.readInt
import io.ktor.utils.io.core.use
import io.ktor.utils.io.pool.useInstance
import io.ktor.utils.io.ByteWriteChannel
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.io.InputStream
import kotlinx.io.core.Input
import kotlinx.io.core.discardExact
import kotlinx.io.core.readAvailable
import kotlinx.io.core.use
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.copyAndClose
import net.mamoe.mirai.utils.io.ByteArrayPool
import net.mamoe.mirai.utils.io.PlatformSocket
import net.mamoe.mirai.utils.io.withUse
import kotlinx.serialization.InternalSerializationApi
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
@Suppress("SpellCheckingInspection")
internal suspend fun HttpClient.postImage(
htcmd: String,
......@@ -67,7 +69,7 @@ internal suspend fun HttpClient.postImage(
override val contentType: ContentType = ContentType.Image.Any
override val contentLength: Long = inputSize
override suspend fun writeTo(channel: io.ktor.utils.io.ByteWriteChannel) {
override suspend fun writeTo(channel: ByteWriteChannel) {
ByteArrayPool.useInstance { buffer: ByteArray ->
when (imageInput) {
is Input -> {
......@@ -92,9 +94,9 @@ internal suspend fun HttpClient.postImage(
}
} == HttpStatusCode.OK
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
internal object HighwayHelper {
@UseExperimental(InternalCoroutinesApi::class)
@OptIn(InternalCoroutinesApi::class)
suspend fun uploadImage(
client: QQAndroidClient,
serverIp: String,
......
......@@ -11,19 +11,24 @@
package net.mamoe.mirai.qqandroid.network.highway
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.core.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.io.ByteReadChannel
import kotlinx.io.InputStream
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket
import kotlinx.io.core.writeFully
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.*
import kotlinx.serialization.InternalSerializationApi
import net.mamoe.mirai.utils.MiraiPlatformUtils
@UseExperimental(MiraiInternalAPI::class)
@OptIn(MiraiInternalAPI::class, InternalSerializationApi::class)
internal fun createImageDataPacketSequence( // RequestDataTrans
client: QQAndroidClient,
command: String,
......@@ -73,7 +78,7 @@ internal fun createImageDataPacketSequence( // RequestDataTrans
dataoffset = offset,
filesize = dataSize.toLong(),
serviceticket = uKey,
md5 = net.mamoe.mirai.utils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize),
md5 = MiraiPlatformUtils.md5(chunkedInput.buffer, 0, chunkedInput.bufferSize),
fileMd5 = fileMd5,
flag = 0,
rtcode = 0
......
......@@ -9,139 +9,139 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
@Serializable
internal class BigDataChannel(
@SerialId(0) val vBigdataIplists: List<BigDataIpList>,
@SerialId(1) val sBigdataSigSession: ByteArray? = null,
@SerialId(2) val sBigdataKeySession: ByteArray? = null,
@SerialId(3) val uSigUin: Long? = null,
@SerialId(4) val iConnectFlag: Int? = 1,
@SerialId(5) val vBigdataPbBuf: ByteArray? = null
@JceId(0) val vBigdataIplists: List<BigDataIpList>,
@JceId(1) val sBigdataSigSession: ByteArray? = null,
@JceId(2) val sBigdataKeySession: ByteArray? = null,
@JceId(3) val uSigUin: Long? = null,
@JceId(4) val iConnectFlag: Int? = 1,
@JceId(5) val vBigdataPbBuf: ByteArray? = null
) : JceStruct
@Serializable
internal class BigDataIpInfo(
@SerialId(0) val uType: Long,
@SerialId(1) val sIp: String = "",
@SerialId(2) val uPort: Long
@JceId(0) val uType: Long,
@JceId(1) val sIp: String = "",
@JceId(2) val uPort: Long
) : JceStruct
@Serializable
internal class BigDataIpList(
@SerialId(0) val uServiceType: Long,
@SerialId(1) val vIplist: List<BigDataIpInfo>,
@SerialId(2) val netSegConfs: List<NetSegConf>? = null,
@SerialId(3) val ufragmentSize: Long? = null
@JceId(0) val uServiceType: Long,
@JceId(1) val vIplist: List<BigDataIpInfo>,
@JceId(2) val netSegConfs: List<NetSegConf>? = null,
@JceId(3) val ufragmentSize: Long? = null
) : JceStruct
@Serializable
internal class ClientLogConfig(
@SerialId(1) val type: Int,
@SerialId(2) val timeStart: TimeStamp? = null,
@SerialId(3) val timeFinish: TimeStamp? = null,
@SerialId(4) val loglevel: Byte? = null,
@SerialId(5) val cookie: Int? = null,
@SerialId(6) val lseq: Long? = null
@JceId(1) val type: Int,
@JceId(2) val timeStart: TimeStamp? = null,
@JceId(3) val timeFinish: TimeStamp? = null,
@JceId(4) val loglevel: Byte? = null,
@JceId(5) val cookie: Int? = null,
@JceId(6) val lseq: Long? = null
) : JceStruct
@Serializable
internal class DomainIpChannel(
@SerialId(0) val vDomainIplists: List<DomainIpList>
@JceId(0) val vDomainIplists: List<DomainIpList>
) : JceStruct
@Serializable
internal class DomainIpInfo(
@SerialId(1) val uIp: Int,
@SerialId(2) val uPort: Int
@JceId(1) val uIp: Int,
@JceId(2) val uPort: Int
) : JceStruct
@Serializable
internal class DomainIpList(
@SerialId(0) val uDomainType: Int,
@SerialId(1) val vIplist: List<DomainIpInfo>
@JceId(0) val uDomainType: Int,
@JceId(1) val vIplist: List<DomainIpInfo>
) : JceStruct
@Serializable
internal class FileStoragePushFSSvcList(
@SerialId(0) val vUpLoadList: List<FileStorageServerListInfo>,
@SerialId(1) val vPicDownLoadList: List<FileStorageServerListInfo>,
@SerialId(2) val vGPicDownLoadList: List<FileStorageServerListInfo>? = null,
@SerialId(3) val vQzoneProxyServiceList: List<FileStorageServerListInfo>? = null,
@SerialId(4) val vUrlEncodeServiceList: List<FileStorageServerListInfo>? = null,
@SerialId(5) val bigDataChannel: BigDataChannel? = null,
@SerialId(6) val vVipEmotionList: List<FileStorageServerListInfo>? = null,
@SerialId(7) val vC2CPicDownList: List<FileStorageServerListInfo>? = null,
@SerialId(8) val fmtIPInfo: FmtIPInfo? = null,
@SerialId(9) val domainIpChannel: DomainIpChannel? = null,
@SerialId(10) val pttlist: ByteArray? = null
@JceId(0) val vUpLoadList: List<FileStorageServerListInfo>,
@JceId(1) val vPicDownLoadList: List<FileStorageServerListInfo>,
@JceId(2) val vGPicDownLoadList: List<FileStorageServerListInfo>? = null,
@JceId(3) val vQzoneProxyServiceList: List<FileStorageServerListInfo>? = null,
@JceId(4) val vUrlEncodeServiceList: List<FileStorageServerListInfo>? = null,
@JceId(5) val bigDataChannel: BigDataChannel? = null,
@JceId(6) val vVipEmotionList: List<FileStorageServerListInfo>? = null,
@JceId(7) val vC2CPicDownList: List<FileStorageServerListInfo>? = null,
@JceId(8) val fmtIPInfo: FmtIPInfo? = null,
@JceId(9) val domainIpChannel: DomainIpChannel? = null,
@JceId(10) val pttlist: ByteArray? = null
) : JceStruct
@Serializable
internal class FileStorageServerListInfo(
@SerialId(1) val sIP: String = "",
@SerialId(2) val iPort: Int
@JceId(1) val sIP: String = "",
@JceId(2) val iPort: Int
) : JceStruct
@Serializable
internal class FmtIPInfo(
@SerialId(0) val sGateIp: String = "",
@SerialId(1) val iGateIpOper: Long
@JceId(0) val sGateIp: String = "",
@JceId(1) val iGateIpOper: Long
) : JceStruct
@Serializable
internal class NetSegConf(
@SerialId(0) val uint32NetType: Long? = null,
@SerialId(1) val uint32Segsize: Long? = null,
@SerialId(2) val uint32Segnum: Long? = null,
@SerialId(3) val uint32Curconnnum: Long? = null
@JceId(0) val uint32NetType: Long? = null,
@JceId(1) val uint32Segsize: Long? = null,
@JceId(2) val uint32Segnum: Long? = null,
@JceId(3) val uint32Curconnnum: Long? = null
) : JceStruct
@Suppress("ArrayInDataClass")
@Serializable
internal data class PushReq(
@SerialId(1) val type: Int,
@SerialId(2) val jcebuf: ByteArray,
@SerialId(3) val seq: Long
@JceId(1) val type: Int,
@JceId(2) val jcebuf: ByteArray,
@JceId(3) val seq: Long
) : JceStruct, Packet
@Serializable
internal class PushResp(
@SerialId(1) val type: Int,
@SerialId(2) val seq: Long,
@SerialId(3) val jcebuf: ByteArray? = null
@JceId(1) val type: Int,
@JceId(2) val seq: Long,
@JceId(3) val jcebuf: ByteArray? = null
) : JceStruct
@Serializable
internal class SsoServerList(
@SerialId(1) val v2G3GList: List<SsoServerListInfo>,
@SerialId(3) val vWifiList: List<SsoServerListInfo>,
@SerialId(4) val iReconnect: Int,
@SerialId(5) val testSpeed: Byte? = null,
@SerialId(6) val useNewList: Byte? = null,
@SerialId(7) val iMultiConn: Int? = 1,
@SerialId(8) val vHttp2g3glist: List<SsoServerListInfo>? = null,
@SerialId(9) val vHttpWifilist: List<SsoServerListInfo>? = null
@JceId(1) val v2G3GList: List<SsoServerListInfo>,
@JceId(3) val vWifiList: List<SsoServerListInfo>,
@JceId(4) val iReconnect: Int,
@JceId(5) val testSpeed: Byte? = null,
@JceId(6) val useNewList: Byte? = null,
@JceId(7) val iMultiConn: Int? = 1,
@JceId(8) val vHttp2g3glist: List<SsoServerListInfo>? = null,
@JceId(9) val vHttpWifilist: List<SsoServerListInfo>? = null
) : JceStruct
@Serializable
internal class SsoServerListInfo(
@SerialId(1) val sIP: String = "",
@SerialId(2) val iPort: Int,
@SerialId(3) val linkType: Byte,
@SerialId(4) val proxy: Byte,
@SerialId(5) val protocolType: Byte? = null,
@SerialId(6) val iTimeOut: Int? = 10
@JceId(1) val sIP: String = "",
@JceId(2) val iPort: Int,
@JceId(3) val linkType: Byte,
@JceId(4) val proxy: Byte,
@JceId(5) val protocolType: Byte? = null,
@JceId(6) val iTimeOut: Int? = 10
) : JceStruct
@Serializable
internal class TimeStamp(
@SerialId(1) val year: Int,
@SerialId(2) val month: Byte,
@SerialId(3) val day: Byte,
@SerialId(4) val hour: Byte
@JceId(1) val year: Int,
@JceId(2) val month: Byte,
@JceId(3) val day: Byte,
@JceId(4) val hour: Byte
) : JceStruct
......@@ -9,72 +9,72 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
@Suppress("ArrayInDataClass")
@Serializable
internal data class RequestPushNotify(
@SerialId(0) val uin: Long? = 0L,
@SerialId(1) val ctype: Byte = 0,
@SerialId(2) val strService: String?,
@SerialId(3) val strCmd: String?,
@SerialId(4) val vNotifyCookie: ByteArray? = EMPTY_BYTE_ARRAY,
@SerialId(5) val usMsgType: Int?,
@SerialId(6) val wUserActive: Int?,
@SerialId(7) val wGeneralFlag: Int?,
@SerialId(8) val bindedUin: Long?,
@SerialId(9) val stMsgInfo: MsgInfo?,
@SerialId(10) val msgCtrlBuf: String?,
@SerialId(11) val serverBuf: ByteArray?,
@SerialId(12) val pingFlag: Long?,
@SerialId(13) val svrip: Int?
@JceId(0) val uin: Long? = 0L,
@JceId(1) val ctype: Byte = 0,
@JceId(2) val strService: String?,
@JceId(3) val strCmd: String?,
@JceId(4) val vNotifyCookie: ByteArray? = EMPTY_BYTE_ARRAY,
@JceId(5) val usMsgType: Int?,
@JceId(6) val wUserActive: Int?,
@JceId(7) val wGeneralFlag: Int?,
@JceId(8) val bindedUin: Long?,
@JceId(9) val stMsgInfo: MsgInfo?,
@JceId(10) val msgCtrlBuf: String?,
@JceId(11) val serverBuf: ByteArray?,
@JceId(12) val pingFlag: Long?,
@JceId(13) val svrip: Int?
) : JceStruct, Packet
@Serializable
internal class MsgInfo(
@SerialId(0) val lFromUin: Long? = 0L,
@SerialId(1) val uMsgTime: Long? = 0L,
@SerialId(2) val shMsgType: Short,
@SerialId(3) val shMsgSeq: Short,
@SerialId(4) val strMsg: String?,
@SerialId(5) val uRealMsgTime: Int?,
@SerialId(6) val vMsg: ByteArray?,
@SerialId(7) val uAppShareID: Long?,
@SerialId(8) val vMsgCookies: ByteArray? = EMPTY_BYTE_ARRAY,
@SerialId(9) val vAppShareCookie: ByteArray? = EMPTY_BYTE_ARRAY,
@SerialId(10) val lMsgUid: Long?,
@SerialId(11) val lLastChangeTime: Long?,
@SerialId(12) val vCPicInfo: List<CPicInfo>?,
@SerialId(13) val stShareData: ShareData?,
@SerialId(14) val lFromInstId: Long?,
@SerialId(15) val vRemarkOfSender: ByteArray?,
@SerialId(16) val strFromMobile: String?,
@SerialId(17) val strFromName: String?,
@SerialId(18) val vNickName: List<String>?//,
@JceId(0) val lFromUin: Long? = 0L,
@JceId(1) val uMsgTime: Long? = 0L,
@JceId(2) val shMsgType: Short,
@JceId(3) val shMsgSeq: Short,
@JceId(4) val strMsg: String?,
@JceId(5) val uRealMsgTime: Int?,
@JceId(6) val vMsg: ByteArray?,
@JceId(7) val uAppShareID: Long?,
@JceId(8) val vMsgCookies: ByteArray? = EMPTY_BYTE_ARRAY,
@JceId(9) val vAppShareCookie: ByteArray? = EMPTY_BYTE_ARRAY,
@JceId(10) val lMsgUid: Long?,
@JceId(11) val lLastChangeTime: Long?,
@JceId(12) val vCPicInfo: List<CPicInfo>?,
@JceId(13) val stShareData: ShareData?,
@JceId(14) val lFromInstId: Long?,
@JceId(15) val vRemarkOfSender: ByteArray?,
@JceId(16) val strFromMobile: String?,
@JceId(17) val strFromName: String?,
@JceId(18) val vNickName: List<String>?//,
//@SerialId(19) val stC2CTmpMsgHead: TempMsgHead?
) : JceStruct
@Serializable
internal class ShareData(
@SerialId(0) val pkgname: String = "",
@SerialId(1) val msgtail: String = "",
@SerialId(2) val picurl: String = "",
@SerialId(3) val url: String = ""
@JceId(0) val pkgname: String = "",
@JceId(1) val msgtail: String = "",
@JceId(2) val picurl: String = "",
@JceId(3) val url: String = ""
) : JceStruct
@Serializable
internal class TempMsgHead(
@SerialId(0) val c2c_type: Int? = 0,
@SerialId(1) val serviceType: Int? = 0
@JceId(0) val c2c_type: Int? = 0,
@JceId(1) val serviceType: Int? = 0
) : JceStruct
@Serializable
internal class CPicInfo(
@SerialId(0) val vPath: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(1) val vHost: ByteArray? = EMPTY_BYTE_ARRAY
@JceId(0) val vPath: ByteArray = EMPTY_BYTE_ARRAY,
@JceId(1) val vHost: ByteArray? = EMPTY_BYTE_ARRAY
) : JceStruct
\ No newline at end of file
......@@ -9,38 +9,38 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
private val EMPTY_MAP = mapOf<String, String>()
@Serializable
internal class RequestPacket(
@SerialId(1) val iVersion: Short = 3,
@SerialId(2) val cPacketType: Byte = 0,
@SerialId(3) val iMessageType: Int = 0,
@SerialId(4) val iRequestId: Int,
@SerialId(5) val sServantName: String = "",
@SerialId(6) val sFuncName: String = "",
@SerialId(7) val sBuffer: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(8) val iTimeout: Int? = 0,
@SerialId(9) val context: Map<String, String>? = EMPTY_MAP,
@SerialId(10) val status: Map<String, String>? = EMPTY_MAP
@JceId(1) val iVersion: Short? = 3,
@JceId(2) val cPacketType: Byte = 0,
@JceId(3) val iMessageType: Int = 0,
@JceId(4) val iRequestId: Int,
@JceId(5) val sServantName: String = "",
@JceId(6) val sFuncName: String = "",
@JceId(7) val sBuffer: ByteArray = EMPTY_BYTE_ARRAY,
@JceId(8) val iTimeout: Int? = 0,
@JceId(9) val context: Map<String, String>? = EMPTY_MAP,
@JceId(10) val status: Map<String, String>? = EMPTY_MAP
) : JceStruct
@Serializable
internal class RequestDataVersion3(
@SerialId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
@JceId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
) : JceStruct
@Serializable
internal class RequestDataVersion2(
@SerialId(0) val map: Map<String, Map<String, ByteArray>>
@JceId(0) val map: Map<String, Map<String, ByteArray>>
) : JceStruct
@Serializable
internal class RequestDataStructSvcReqRegister(
@SerialId(0) val struct: SvcReqRegister
@JceId(0) val struct: SvcReqRegister
) : JceStruct
\ No newline at end of file
......@@ -9,14 +9,15 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoId
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
@Serializable
internal class RequestPushForceOffline(
@SerialId(0) val uin: Long,
@SerialId(1) val title: String? = "",
@SerialId(2) val tips: String? = "",
@SerialId(3) val sameDevice: Byte? = null
@JceId(0) val uin: Long,
@JceId(1) val title: String? = "",
@JceId(2) val tips: String? = "",
@JceId(3) val sameDevice: Byte? = null
) : JceStruct
\ No newline at end of file
......@@ -9,47 +9,47 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
@Serializable
internal class SvcReqRegister(
@SerialId(0) val lUin: Long = 0L,
@SerialId(1) val lBid: Long = 0L,
@SerialId(2) val cConnType: Byte = 0,
@SerialId(3) val sOther: String = "",
@SerialId(4) val iStatus: Int = 11,
@SerialId(5) val bOnlinePush: Byte = 0,
@SerialId(6) val bIsOnline: Byte = 0,
@SerialId(7) val bIsShowOnline: Byte = 0,
@SerialId(8) val bKikPC: Byte = 0,
@SerialId(9) val bKikWeak: Byte = 0,
@SerialId(10) val timeStamp: Long = 0L,
@SerialId(11) val iOSVersion: Long = 0L,
@SerialId(12) val cNetType: Byte = 0,
@SerialId(13) val sBuildVer: String? = "",
@SerialId(14) val bRegType: Byte = 0,
@SerialId(15) val vecDevParam: ByteArray? = null,
@SerialId(16) val vecGuid: ByteArray? = null,
@SerialId(17) val iLocaleID: Int = 2052,
@SerialId(18) val bSlientPush: Byte = 0,
@SerialId(19) val strDevName: String? = null,
@SerialId(20) val strDevType: String? = null,
@SerialId(21) val strOSVer: String? = null,
@SerialId(22) val bOpenPush: Byte = 1,
@SerialId(23) val iLargeSeq: Long = 0L,
@SerialId(24) val iLastWatchStartTime: Long = 0L,
@SerialId(26) val uOldSSOIp: Long = 0L,
@SerialId(27) val uNewSSOIp: Long = 0L,
@SerialId(28) val sChannelNo: String? = null,
@SerialId(29) val lCpId: Long = 0L,
@SerialId(30) val strVendorName: String? = null,
@SerialId(31) val strVendorOSName: String? = null,
@SerialId(32) val strIOSIdfa: String? = null,
@SerialId(33) val bytes_0x769_reqbody: ByteArray? = null,
@SerialId(34) val bIsSetStatus: Byte = 0,
@SerialId(35) val vecServerBuf: ByteArray? = null,
@SerialId(36) val bSetMute: Byte = 0
@JceId(0) val lUin: Long = 0L,
@JceId(1) val lBid: Long = 0L,
@JceId(2) val cConnType: Byte = 0,
@JceId(3) val sOther: String = "",
@JceId(4) val iStatus: Int = 11,
@JceId(5) val bOnlinePush: Byte = 0,
@JceId(6) val bIsOnline: Byte = 0,
@JceId(7) val bIsShowOnline: Byte = 0,
@JceId(8) val bKikPC: Byte = 0,
@JceId(9) val bKikWeak: Byte = 0,
@JceId(10) val timeStamp: Long = 0L,
@JceId(11) val iOSVersion: Long = 0L,
@JceId(12) val cNetType: Byte = 0,
@JceId(13) val sBuildVer: String? = "",
@JceId(14) val bRegType: Byte = 0,
@JceId(15) val vecDevParam: ByteArray? = null,
@JceId(16) val vecGuid: ByteArray? = null,
@JceId(17) val iLocaleID: Int = 2052,
@JceId(18) val bSlientPush: Byte = 0,
@JceId(19) val strDevName: String? = null,
@JceId(20) val strDevType: String? = null,
@JceId(21) val strOSVer: String? = null,
@JceId(22) val bOpenPush: Byte = 1,
@JceId(23) val iLargeSeq: Long = 0L,
@JceId(24) val iLastWatchStartTime: Long = 0L,
@JceId(26) val uOldSSOIp: Long = 0L,
@JceId(27) val uNewSSOIp: Long = 0L,
@JceId(28) val sChannelNo: String? = null,
@JceId(29) val lCpId: Long = 0L,
@JceId(30) val strVendorName: String? = null,
@JceId(31) val strVendorOSName: String? = null,
@JceId(32) val strIOSIdfa: String? = null,
@JceId(33) val bytes_0x769_reqbody: ByteArray? = null,
@JceId(34) val bIsSetStatus: Byte = 0,
@JceId(35) val vecServerBuf: ByteArray? = null,
@JceId(36) val bSetMute: Byte = 0
// @SerialId(25) var vecBindUin: ArrayList<*>? = null // ?? 未知泛型
) : JceStruct
\ No newline at end of file
......@@ -11,8 +11,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoId
import kotlinx.serialization.protobuf.ProtoNumberType
import kotlinx.serialization.protobuf.ProtoType
import net.mamoe.mirai.qqandroid.io.ProtoBuf
......@@ -22,74 +22,74 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
class Oidb0x858 : ProtoBuf {
@Serializable
class GoldMsgTipsElem(
@SerialId(1) val type: Int = 0,
@SerialId(2) val billno: String = "",
@SerialId(3) val result: Int = 0,
@SerialId(4) val amount: Int = 0,
@SerialId(5) val total: Int = 0,
@SerialId(6) val interval: Int = 0,
@SerialId(7) val finish: Int = 0,
@SerialId(8) val uin: List<Long>? = null,
@SerialId(9) val action: Int = 0
@ProtoId(1) val type: Int = 0,
@ProtoId(2) val billno: String = "",
@ProtoId(3) val result: Int = 0,
@ProtoId(4) val amount: Int = 0,
@ProtoId(5) val total: Int = 0,
@ProtoId(6) val interval: Int = 0,
@ProtoId(7) val finish: Int = 0,
@ProtoId(8) val uin: List<Long>? = null,
@ProtoId(9) val action: Int = 0
) : ProtoBuf
@Serializable
class MessageRecallReminder(
@SerialId(1) val uin: Long = 0L,
@SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(3) val recalledMsgList: List<MessageMeta> = listOf(),
@SerialId(4) val reminderContent: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(5) val userdef: ByteArray = EMPTY_BYTE_ARRAY
@ProtoId(1) val uin: Long = 0L,
@ProtoId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(3) val recalledMsgList: List<MessageMeta> = listOf(),
@ProtoId(4) val reminderContent: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(5) val userdef: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf {
@Serializable
class MessageMeta(
@SerialId(1) val seq: Int = 0,
@SerialId(2) val time: Int = 0,
@SerialId(3) val msgRandom: Int = 0
@ProtoId(1) val seq: Int = 0,
@ProtoId(2) val time: Int = 0,
@ProtoId(3) val msgRandom: Int = 0
) : ProtoBuf
}
@Serializable
class NotifyMsgBody(
@SerialId(1) val optEnumType: Int /* enum */ = 5,
@SerialId(2) val optUint64MsgTime: Long = 0L,
@SerialId(3) val optUint64MsgExpires: Long = 0L,
@SerialId(4) val optUint64ConfUin: Long = 0L,
@SerialId(5) val optMsgRedtips: RedGrayTipsInfo? = null,
@SerialId(6) val optMsgRecallReminder: MessageRecallReminder? = null,
@SerialId(7) val optMsgObjUpdate: NotifyObjmsgUpdate? = null,
@ProtoId(1) val optEnumType: Int /* enum */ = 5,
@ProtoId(2) val optUint64MsgTime: Long = 0L,
@ProtoId(3) val optUint64MsgExpires: Long = 0L,
@ProtoId(4) val optUint64ConfUin: Long = 0L,
@ProtoId(5) val optMsgRedtips: RedGrayTipsInfo? = null,
@ProtoId(6) val optMsgRecallReminder: MessageRecallReminder? = null,
@ProtoId(7) val optMsgObjUpdate: NotifyObjmsgUpdate? = null,
// @SerialId(8) val optStcmGameState: ApolloGameStatus.STCMGameMessage? = null,
// @SerialId(9) val aplloMsgPush: ApolloPushMsgInfo.STPushMsgElem? = null,
@SerialId(10) val optMsgGoldtips: GoldMsgTipsElem? = null
@ProtoId(10) val optMsgGoldtips: GoldMsgTipsElem? = null
) : ProtoBuf
@Serializable
class NotifyObjmsgUpdate(
@SerialId(1) val objmsgId: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(2) val updateType: Int = 0,
@SerialId(3) val extMsg: ByteArray = EMPTY_BYTE_ARRAY
@ProtoId(1) val objmsgId: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(2) val updateType: Int = 0,
@ProtoId(3) val extMsg: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
class RedGrayTipsInfo(
@SerialId(1) val optUint32ShowLastest: Int = 0,
@SerialId(2) val senderUin: Long = 0L,
@SerialId(3) val receiverUin: Long = 0L,
@SerialId(4) val senderRichContent: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(5) val receiverRichContent: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(6) val authkey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoType(ProtoNumberType.SIGNED) @SerialId(7) val sint32Msgtype: Int = 0,
@SerialId(8) val luckyFlag: Int = 0,
@SerialId(9) val hideFlag: Int = 0,
@SerialId(10) val pcBody: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(11) val icon: Int = 0,
@SerialId(12) val luckyUin: Long = 0L,
@SerialId(13) val time: Int = 0,
@SerialId(14) val random: Int = 0,
@SerialId(15) val broadcastRichContent: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(16) val idiom: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(17) val idiomSeq: Int = 0,
@SerialId(18) val idiomAlpha: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(19) val jumpurl: ByteArray = EMPTY_BYTE_ARRAY
@ProtoId(1) val optUint32ShowLastest: Int = 0,
@ProtoId(2) val senderUin: Long = 0L,
@ProtoId(3) val receiverUin: Long = 0L,
@ProtoId(4) val senderRichContent: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(5) val receiverRichContent: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(6) val authkey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoType(ProtoNumberType.SIGNED) @ProtoId(7) val sint32Msgtype: Int = 0,
@ProtoId(8) val luckyFlag: Int = 0,
@ProtoId(9) val hideFlag: Int = 0,
@ProtoId(10) val pcBody: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(11) val icon: Int = 0,
@ProtoId(12) val luckyUin: Long = 0L,
@ProtoId(13) val time: Int = 0,
@ProtoId(14) val random: Int = 0,
@ProtoId(15) val broadcastRichContent: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(16) val idiom: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(17) val idiomSeq: Int = 0,
@ProtoId(18) val idiomAlpha: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(19) val jumpurl: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
}
......@@ -9,8 +9,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoId
import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
......@@ -18,40 +18,40 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
internal class Vec0xd50 : ProtoBuf {
@Serializable
internal class ExtSnsFrdData(
@SerialId(1) val frdUin: Long = 0L,
@SerialId(91001) val musicSwitch: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(101001) val mutualmarkAlienation: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(141001) val mutualmarkScore: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(151001) val ksingSwitch: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(181001) val lbsShare: ByteArray = EMPTY_BYTE_ARRAY
@ProtoId(1) val frdUin: Long = 0L,
@ProtoId(91001) val musicSwitch: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(101001) val mutualmarkAlienation: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(141001) val mutualmarkScore: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(151001) val ksingSwitch: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(181001) val lbsShare: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class RspBody(
@SerialId(1) val msgUpdateData: List<Vec0xd50.ExtSnsFrdData>? = null,
@SerialId(11) val over: Int = 0,
@SerialId(12) val nextStart: Int = 0,
@SerialId(13) val uint64UnfinishedUins: List<Long>? = null
@ProtoId(1) val msgUpdateData: List<Vec0xd50.ExtSnsFrdData>? = null,
@ProtoId(11) val over: Int = 0,
@ProtoId(12) val nextStart: Int = 0,
@ProtoId(13) val uint64UnfinishedUins: List<Long>? = null
) : ProtoBuf
@Serializable
internal class ReqBody(
@SerialId(1) val appid: Long = 0L,
@SerialId(2) val maxPkgSize: Int = 0,
@SerialId(3) val startTime: Int = 0,
@SerialId(4) val startIndex: Int = 0,
@SerialId(5) val reqNum: Int = 0,
@SerialId(6) val uinList: List<Long>? = null,
@SerialId(91001) val reqMusicSwitch: Int = 0,
@SerialId(101001) val reqMutualmarkAlienation: Int = 0,
@SerialId(141001) val reqMutualmarkScore: Int = 0,
@SerialId(151001) val reqKsingSwitch: Int = 0,
@SerialId(181001) val reqMutualmarkLbsshare: Int = 0
@ProtoId(1) val appid: Long = 0L,
@ProtoId(2) val maxPkgSize: Int = 0,
@ProtoId(3) val startTime: Int = 0,
@ProtoId(4) val startIndex: Int = 0,
@ProtoId(5) val reqNum: Int = 0,
@ProtoId(6) val uinList: List<Long>? = null,
@ProtoId(91001) val reqMusicSwitch: Int = 0,
@ProtoId(101001) val reqMutualmarkAlienation: Int = 0,
@ProtoId(141001) val reqMutualmarkScore: Int = 0,
@ProtoId(151001) val reqKsingSwitch: Int = 0,
@ProtoId(181001) val reqMutualmarkLbsshare: Int = 0
) : ProtoBuf
@Serializable
internal class KSingRelationInfo(
@SerialId(1) val flag: Int = 0
@ProtoId(1) val flag: Int = 0
) : ProtoBuf
}
......@@ -59,21 +59,21 @@ internal class Vec0xd50 : ProtoBuf {
internal class Vec0xd6b : ProtoBuf {
@Serializable
internal class ReqBody(
@SerialId(1) val maxPkgSize: Int = 0,
@SerialId(2) val startTime: Int = 0,
@SerialId(11) val uinList: List<Long>? = null
@ProtoId(1) val maxPkgSize: Int = 0,
@ProtoId(2) val startTime: Int = 0,
@ProtoId(11) val uinList: List<Long>? = null
) : ProtoBuf
@Serializable
internal class RspBody(
@SerialId(11) val msgMutualmarkData: List<Vec0xd6b.MutualMarkData>? = null,
@SerialId(12) val uint64UnfinishedUins: List<Long>? = null
@ProtoId(11) val msgMutualmarkData: List<Vec0xd6b.MutualMarkData>? = null,
@ProtoId(12) val uint64UnfinishedUins: List<Long>? = null
) : ProtoBuf
@Serializable
internal class MutualMarkData(
@SerialId(1) val frdUin: Long = 0L,
@SerialId(2) val result: Int = 0
@ProtoId(1) val frdUin: Long = 0L,
@ProtoId(2) val result: Int = 0
// @SerialId(11) val mutualmarkInfo: List<Mutualmark.MutualMark>? = null
) : ProtoBuf
}
......@@ -82,26 +82,26 @@ internal class Vec0xd6b : ProtoBuf {
internal class Mutualmark : ProtoBuf {
@Serializable
internal class MutualmarkInfo(
@SerialId(1) val lastActionTime: Long = 0L,
@SerialId(2) val level: Int = 0,
@SerialId(3) val lastChangeTime: Long = 0L,
@SerialId(4) val continueDays: Int = 0,
@SerialId(5) val wildcardWording: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(6) val notifyTime: Long = 0L,
@SerialId(7) val iconStatus: Long = 0L,
@SerialId(8) val iconStatusEndTime: Long = 0L,
@SerialId(9) val closeFlag: Int = 0,
@SerialId(10) val resourceInfo: ByteArray = EMPTY_BYTE_ARRAY
@ProtoId(1) val lastActionTime: Long = 0L,
@ProtoId(2) val level: Int = 0,
@ProtoId(3) val lastChangeTime: Long = 0L,
@ProtoId(4) val continueDays: Int = 0,
@ProtoId(5) val wildcardWording: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(6) val notifyTime: Long = 0L,
@ProtoId(7) val iconStatus: Long = 0L,
@ProtoId(8) val iconStatusEndTime: Long = 0L,
@ProtoId(9) val closeFlag: Int = 0,
@ProtoId(10) val resourceInfo: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
internal class ResourceInfo17(
@SerialId(1) val dynamicUrl: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(2) val staticUrl: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(3) val cartoonUrl: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(4) val cartoonMd5: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(5) val playCartoon: Int = 0,
@SerialId(6) val word: ByteArray = EMPTY_BYTE_ARRAY
@ProtoId(1) val dynamicUrl: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(2) val staticUrl: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(3) val cartoonUrl: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(4) val cartoonMd5: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(5) val playCartoon: Int = 0,
@ProtoId(6) val word: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
}
......
......@@ -9,8 +9,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoId
import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.utils.currentTimeSeconds
......@@ -20,22 +20,22 @@ interface ImgReq : ProtoBuf
@Serializable
internal class GetImgUrlReq(
@SerialId(1) val srcUni: Int,
@SerialId(2) val dstUni: Int,
@SerialId(3) val fileResID: String,//UUID
@ProtoId(1) val srcUni: Int,
@ProtoId(2) val dstUni: Int,
@ProtoId(3) val fileResID: String,//UUID
/**
* UUID例子: 没有找到
*/
@SerialId(4) val urlFlag: Int = 1,
@ProtoId(4) val urlFlag: Int = 1,
//5 unknown, 好像没用
@SerialId(6) val urlType: Int = 4,
@SerialId(7) val requestTerm: Int = 5,//确定
@SerialId(8) val requestPlatformType: Int = 9,//确定
@SerialId(9) val srcFileType: Int = 1,//2=ftn,1=picplatform,255
@SerialId(10) val innerIP: Int = 0,//确定
@SerialId(11) val addressBook: Int = 0,//[ChatType.internalID]== 1006为1[为CONTACT时] 我觉得发0没问题
@SerialId(12) val buType: Int = 1,//确定
@SerialId(13) val buildVer: String = "8.2.7.4410",//版本号
@SerialId(14) val timestamp: Int = currentTimeSeconds.toInt(),//(pic_up_timestamp)
@SerialId(15) val requestTransferType: Int = 1
@ProtoId(6) val urlType: Int = 4,
@ProtoId(7) val requestTerm: Int = 5,//确定
@ProtoId(8) val requestPlatformType: Int = 9,//确定
@ProtoId(9) val srcFileType: Int = 1,//2=ftn,1=picplatform,255
@ProtoId(10) val innerIP: Int = 0,//确定
@ProtoId(11) val addressBook: Int = 0,//[ChatType.internalID]== 1006为1[为CONTACT时] 我觉得发0没问题
@ProtoId(12) val buType: Int = 1,//确定
@ProtoId(13) val buildVer: String = "8.2.7.4410",//版本号
@ProtoId(14) val timestamp: Int = currentTimeSeconds.toInt(),//(pic_up_timestamp)
@ProtoId(15) val requestTransferType: Int = 1
) : ImgReq
\ No newline at end of file
......@@ -9,14 +9,17 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet
import io.ktor.utils.io.core.*
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.buildPacket
import kotlinx.io.core.writeFully
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.utils.cryptor.ECDH
import net.mamoe.mirai.utils.cryptor.ECDHKeyPair
import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeShortLVByteArray
@UseExperimental(ExperimentalUnsignedTypes::class)
@OptIn(ExperimentalUnsignedTypes::class)
internal interface EncryptMethod {
val id: Int
......
......@@ -9,7 +9,7 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
import io.ktor.utils.io.core.ByteReadPacket
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
......
This diff is collapsed.
This diff is collapsed.
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