mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-08 06:47:05 +08:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
This commit is contained in:
commit
571b4c4162
@ -300,13 +300,13 @@ fun main() {
|
|||||||
```json5
|
```json5
|
||||||
{
|
{
|
||||||
"type": "Face",
|
"type": "Face",
|
||||||
"faceID": 123
|
"faceId": 123
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
| 名字 | 类型 | 说明 |
|
| 名字 | 类型 | 说明 |
|
||||||
| ------ | ---- | ---------- |
|
| ------ | ---- | ---------- |
|
||||||
| faceID | Int | QQ表情编号 |
|
| faceId | Int | QQ表情编号 |
|
||||||
|
|
||||||
#### Plain
|
#### Plain
|
||||||
|
|
||||||
@ -453,7 +453,7 @@ fun main() {
|
|||||||
|
|
||||||
### 群全体禁言
|
### 群全体禁言
|
||||||
|
|
||||||
使用此方法令指定群进行全体禁言
|
使用此方法令指定群进行全体禁言(需要有相关限权)
|
||||||
|
|
||||||
```
|
```
|
||||||
[POST] /muteAll
|
[POST] /muteAll
|
||||||
@ -487,7 +487,7 @@ fun main() {
|
|||||||
|
|
||||||
### 群解除全体禁言
|
### 群解除全体禁言
|
||||||
|
|
||||||
使用此方法令指定群解除全体禁言
|
使用此方法令指定群解除全体禁言(需要有相关限权)
|
||||||
|
|
||||||
```
|
```
|
||||||
[POST] /unmuteAll
|
[POST] /unmuteAll
|
||||||
@ -505,7 +505,7 @@ fun main() {
|
|||||||
|
|
||||||
### 群禁言群成员
|
### 群禁言群成员
|
||||||
|
|
||||||
使用此方法指定群禁言指定群员
|
使用此方法指定群禁言指定群员(需要有相关限权)
|
||||||
|
|
||||||
```
|
```
|
||||||
[POST] /mute
|
[POST] /mute
|
||||||
@ -517,7 +517,7 @@ fun main() {
|
|||||||
{
|
{
|
||||||
"sessionKey": "YourSessionKey",
|
"sessionKey": "YourSessionKey",
|
||||||
"target": 123456789,
|
"target": 123456789,
|
||||||
"member": 987654321,
|
"memberId": 987654321,
|
||||||
"time": 1800
|
"time": 1800
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -526,7 +526,7 @@ fun main() {
|
|||||||
| ---------- | ----- | ------ | ---------------- | ------------------------------------- |
|
| ---------- | ----- | ------ | ---------------- | ------------------------------------- |
|
||||||
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
||||||
| target | false | Long | 123456789 | 指定群的群号 |
|
| target | false | Long | 123456789 | 指定群的群号 |
|
||||||
| member | false | Long | 987654321 | 指定群员QQ号 |
|
| memberId | false | Long | 987654321 | 指定群员QQ号 |
|
||||||
| time | true | Int | 1800 | 禁言时长,单位为秒,最多30天,默认为0 |
|
| time | true | Int | 1800 | 禁言时长,单位为秒,最多30天,默认为0 |
|
||||||
|
|
||||||
#### 响应: 返回统一状态码
|
#### 响应: 返回统一状态码
|
||||||
@ -542,7 +542,7 @@ fun main() {
|
|||||||
|
|
||||||
### 群解除群成员禁言
|
### 群解除群成员禁言
|
||||||
|
|
||||||
使用此方法令指定群解除全体禁言
|
使用此方法令指定群解除全体禁言(需要有相关限权)
|
||||||
|
|
||||||
```
|
```
|
||||||
[POST] /unmute
|
[POST] /unmute
|
||||||
@ -554,7 +554,7 @@ fun main() {
|
|||||||
{
|
{
|
||||||
"sessionKey": "YourSessionKey",
|
"sessionKey": "YourSessionKey",
|
||||||
"target": 123456789,
|
"target": 123456789,
|
||||||
"member": 987654321
|
"memberId": 987654321
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -564,9 +564,48 @@ fun main() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 移除群成员
|
||||||
|
|
||||||
|
使用此方法移除指定群成员(需要有相关限权)
|
||||||
|
|
||||||
|
```
|
||||||
|
[POST] /kick
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 请求:
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"sessionKey": "YourSessionKey",
|
||||||
|
"target": 123456789,
|
||||||
|
"memberId": 987654321,
|
||||||
|
"msg": "您已被移出群聊"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||||
|
| ---------- | ----- | ------ | ---------------- | --------------- |
|
||||||
|
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
||||||
|
| target | false | Long | 123456789 | 指定群的群号 |
|
||||||
|
| memberId | false | Long | 987654321 | 指定群员QQ号 |
|
||||||
|
| msg | true | String | "" | 信息 |
|
||||||
|
|
||||||
|
#### 响应
|
||||||
|
|
||||||
|
#### 响应: 返回统一状态码
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"msg": "success"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 群设置
|
### 群设置
|
||||||
|
|
||||||
使用此方法修改群设置(需要又相关限权)
|
使用此方法修改群设置(需要有相关限权)
|
||||||
|
|
||||||
```
|
```
|
||||||
[POST] /groupConfig
|
[POST] /groupConfig
|
||||||
@ -638,3 +677,70 @@ fun main() {
|
|||||||
"anonymousChat": true
|
"anonymousChat": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### 修改群员资料
|
||||||
|
|
||||||
|
使用此方法修改群员资料(需要有相关限权)
|
||||||
|
|
||||||
|
```
|
||||||
|
[POST] /memberInfo
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 请求:
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"sessionKey": "YourSessionKey",
|
||||||
|
"target": 123456789,
|
||||||
|
"memberId": 987654321,
|
||||||
|
"info": {
|
||||||
|
"name": "群名片",
|
||||||
|
"specialTitle": "群头衔"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||||
|
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
||||||
|
| sessionKey | false | String | "YourSessionKey" | 你的session key |
|
||||||
|
| target | false | Long | 123456789 | 指定群的群号 |
|
||||||
|
| memberId | false | Long | 987654321 | 群员QQ号 |
|
||||||
|
| info | false | Object | {} | 群员资料 |
|
||||||
|
| name | true | String | "Name" | 群名片,即群昵称 |
|
||||||
|
| specialTitle | true | String | "Title" | 群头衔 |
|
||||||
|
|
||||||
|
#### 响应: 返回统一状态码
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"msg": "success"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取群员资料
|
||||||
|
|
||||||
|
使用此方法获取群员资料
|
||||||
|
|
||||||
|
```
|
||||||
|
[Get] /groupConfig?sessionKey=YourSessionKey&target=123456789
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 请求:
|
||||||
|
|
||||||
|
| 名字 | 可选 | 类型 | 举例 | 说明 |
|
||||||
|
| ----------------- | ----- | ------- | ---------------- | -------------------- |
|
||||||
|
| sessionKey | false | String | YourSessionKey | 你的session key |
|
||||||
|
| target | false | Long | 123456789 | 指定群的群号 |
|
||||||
|
| memberId | false | Long | 987654321 | 群员QQ号 |
|
||||||
|
|
||||||
|
|
||||||
|
#### 响应
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
"name": "群名片",
|
||||||
|
"announcement": "群头衔"
|
||||||
|
}
|
||||||
|
```
|
@ -0,0 +1,38 @@
|
|||||||
|
package net.mamoe.mirai.api.http.data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误请求. 抛出这个异常后将会返回错误给一个请求
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
open class IllegalAccessException : Exception {
|
||||||
|
override val message: String get() = super.message!!
|
||||||
|
|
||||||
|
constructor(message: String) : super(message, null)
|
||||||
|
constructor(cause: Throwable) : super(cause.toString(), cause)
|
||||||
|
constructor(message: String, cause: Throwable?) : super(message, cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session失效或不存在
|
||||||
|
*/
|
||||||
|
object IllegalSessionException : IllegalAccessException("Session失效或不存在")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session未激活
|
||||||
|
*/
|
||||||
|
object NotVerifiedSessionException : IllegalAccessException("Session未激活")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定Bot不存在
|
||||||
|
*/
|
||||||
|
object NoSuchBotException: IllegalAccessException("指定Bot不存在")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定Bot不存在
|
||||||
|
*/
|
||||||
|
object PermissionDeniedException: IllegalAccessException("无操作限权")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误参数
|
||||||
|
*/
|
||||||
|
class IllegalParamException(message: String) : IllegalAccessException(message)
|
@ -0,0 +1,21 @@
|
|||||||
|
package net.mamoe.mirai.api.http.data
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
open class StateCode(val code: Int, var msg: String) {
|
||||||
|
object Success : StateCode(0, "success") // 成功
|
||||||
|
object NoBot : StateCode(2, "指定Bot不存在")
|
||||||
|
object IllegalSession : StateCode(3, "Session失效或不存在")
|
||||||
|
object NotVerifySession : StateCode(4, "Session未认证")
|
||||||
|
object NoElement : StateCode(5, "指定对象不存在")
|
||||||
|
object PermissionDenied : StateCode(10, "无操作权限")
|
||||||
|
|
||||||
|
// KS bug: 主构造器中不能有非字段参数 https://github.com/Kotlin/kotlinx.serialization/issues/575
|
||||||
|
@Serializable
|
||||||
|
class IllegalAccess() : StateCode(400, "") { // 非法访问
|
||||||
|
constructor(msg: String) : this() {
|
||||||
|
this.msg = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package net.mamoe.mirai.api.http.dto
|
package net.mamoe.mirai.api.http.data.common
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.contact.Group
|
import net.mamoe.mirai.contact.Group
|
||||||
@ -30,7 +30,8 @@ data class MemberDTO(
|
|||||||
val group: GroupDTO
|
val group: GroupDTO
|
||||||
) : ContactDTO() {
|
) : ContactDTO() {
|
||||||
constructor(member: Member) : this (
|
constructor(member: Member) : this (
|
||||||
member.id, member.groupCard, member.permission, GroupDTO(member.group)
|
member.id, member.groupCard, member.permission,
|
||||||
|
GroupDTO(member.group)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
|||||||
|
package net.mamoe.mirai.api.http.data.common
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
|
import net.mamoe.mirai.api.http.AuthedSession
|
||||||
|
|
||||||
|
interface DTO
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AuthDTO(val authKey: String) : DTO
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
abstract class VerifyDTO : DTO {
|
||||||
|
abstract val sessionKey: String
|
||||||
|
@Transient
|
||||||
|
lateinit var session: AuthedSession // 反序列化验证成功后传入
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package net.mamoe.mirai.api.http.dto
|
package net.mamoe.mirai.api.http.data.common
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
@ -1,6 +0,0 @@
|
|||||||
package net.mamoe.mirai.api.http.dto
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class AuthDTO(val authKey: String) : DTO
|
|
@ -1,97 +0,0 @@
|
|||||||
package net.mamoe.mirai.api.http.dto
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.Transient
|
|
||||||
import net.mamoe.mirai.api.http.AuthedSession
|
|
||||||
import net.mamoe.mirai.contact.Group
|
|
||||||
import net.mamoe.mirai.contact.Member
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
abstract class VerifyDTO : DTO {
|
|
||||||
abstract val sessionKey: String
|
|
||||||
@Transient
|
|
||||||
lateinit var session: AuthedSession // 反序列化验证后传入
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class BindDTO(override val sessionKey: String, val qq: Long) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class SendDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long,
|
|
||||||
val messageChain: MessageChainDTO
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
typealias GroupTargetDTO = FriendTargetDTO
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class FriendTargetDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class MuteDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long,
|
|
||||||
val member: Long = 0,
|
|
||||||
val time: Int = 0
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class GroupConfigDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long,
|
|
||||||
val config: GroupInfoDTO
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class GroupInfoDTO(
|
|
||||||
val name: String? = null,
|
|
||||||
val announcement: String? = null,
|
|
||||||
val confessTalk: Boolean? = null,
|
|
||||||
val allowMemberInvite: Boolean? = null,
|
|
||||||
val autoApprove: Boolean? = null,
|
|
||||||
val anonymousChat: Boolean? = null
|
|
||||||
) : DTO {
|
|
||||||
constructor(group: Group) : this(
|
|
||||||
group.name, group.announcement, group.confessTalk, group.allowMemberInvite,
|
|
||||||
group.autoApprove, group.anonymousChat
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class MemberConfigDTO(
|
|
||||||
override val sessionKey: String,
|
|
||||||
val target: Long,
|
|
||||||
val memberId: Long,
|
|
||||||
val config: MemberInfoDTO
|
|
||||||
) : VerifyDTO()
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class MemberInfoDTO(
|
|
||||||
val name: String? = null,
|
|
||||||
val specialTitle: String? = null
|
|
||||||
) : DTO {
|
|
||||||
constructor(member: Member) : this(member.groupCard, member.specialTitle)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
open class StateCode(val code: Int, var msg: String) {
|
|
||||||
object Success : StateCode(0, "success") // 成功
|
|
||||||
object NoBot : StateCode(2, "指定Bot不存在")
|
|
||||||
object IllegalSession : StateCode(3, "Session失效或不存在")
|
|
||||||
object NotVerifySession : StateCode(4, "Session未认证")
|
|
||||||
object NoElement : StateCode(5, "指定对象不存在")
|
|
||||||
object PermissionDenied : StateCode(10, "无操作权限")
|
|
||||||
|
|
||||||
// KS bug: 主构造器中不能有非字段参数 https://github.com/Kotlin/kotlinx.serialization/issues/575
|
|
||||||
@Serializable
|
|
||||||
class IllegalAccess() : StateCode(400, "") { // 非法访问
|
|
||||||
constructor(msg: String) : this() {
|
|
||||||
this.msg = msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,10 +3,12 @@ package net.mamoe.mirai.api.http.route
|
|||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.api.http.AuthedSession
|
import net.mamoe.mirai.api.http.AuthedSession
|
||||||
import net.mamoe.mirai.api.http.SessionManager
|
import net.mamoe.mirai.api.http.SessionManager
|
||||||
import net.mamoe.mirai.api.http.dto.*
|
import net.mamoe.mirai.api.http.data.*
|
||||||
|
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
|
||||||
@ -43,6 +45,9 @@ fun Application.authModule() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class BindDTO(override val sessionKey: String, val qq: Long) : VerifyDTO()
|
||||||
|
|
||||||
private fun getBotOrThrow(qq: Long) = try {
|
private fun getBotOrThrow(qq: Long) = try {
|
||||||
Bot.instanceWhose(qq)
|
Bot.instanceWhose(qq)
|
||||||
} catch (e: NoSuchElementException) {
|
} catch (e: NoSuchElementException) {
|
||||||
|
@ -19,7 +19,10 @@ import io.ktor.util.pipeline.PipelineContext
|
|||||||
import net.mamoe.mirai.api.http.AuthedSession
|
import net.mamoe.mirai.api.http.AuthedSession
|
||||||
import net.mamoe.mirai.api.http.SessionManager
|
import net.mamoe.mirai.api.http.SessionManager
|
||||||
import net.mamoe.mirai.api.http.TempSession
|
import net.mamoe.mirai.api.http.TempSession
|
||||||
import net.mamoe.mirai.api.http.dto.*
|
import net.mamoe.mirai.api.http.data.*
|
||||||
|
import net.mamoe.mirai.api.http.data.common.*
|
||||||
|
import net.mamoe.mirai.api.http.util.jsonParseOrNull
|
||||||
|
import net.mamoe.mirai.api.http.util.toJson
|
||||||
|
|
||||||
fun Application.mirai() {
|
fun Application.mirai() {
|
||||||
install(DefaultHeaders)
|
install(DefaultHeaders)
|
||||||
@ -136,14 +139,13 @@ internal suspend fun ApplicationCall.respondJson(json: String, status: HttpStatu
|
|||||||
internal suspend inline fun <reified T : DTO> ApplicationCall.receiveDTO(): T? = receive<String>().jsonParseOrNull()
|
internal suspend inline fun <reified T : DTO> ApplicationCall.receiveDTO(): T? = receive<String>().jsonParseOrNull()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun PipelineContext<Unit, ApplicationCall>.illegalParam(
|
fun PipelineContext<Unit, ApplicationCall>.illegalParam(
|
||||||
expectingType: String?,
|
expectingType: String?,
|
||||||
paramName: String,
|
paramName: String,
|
||||||
actualValue: String? = call.parameters[paramName]
|
actualValue: String? = call.parameters[paramName]
|
||||||
): Nothing = throw IllegalParamException("Illegal param. A $expectingType is required for `$paramName` while `$actualValue` is given")
|
): Nothing = throw IllegalParamException("Illegal param. A $expectingType is required for `$paramName` while `$actualValue` is given")
|
||||||
|
|
||||||
|
|
||||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||||
internal inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNull(name: String): R =
|
internal inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNull(name: String): R =
|
||||||
@ -171,42 +173,3 @@ internal inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNu
|
|||||||
|
|
||||||
else -> error(name::class.simpleName + " is not supported")
|
else -> error(name::class.simpleName + " is not supported")
|
||||||
} as R ?: illegalParam(R::class.simpleName, name)
|
} as R ?: illegalParam(R::class.simpleName, name)
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误请求. 抛出这个异常后将会返回错误给一个请求
|
|
||||||
*/
|
|
||||||
@Suppress("unused")
|
|
||||||
open class IllegalAccessException : Exception {
|
|
||||||
override val message: String get() = super.message!!
|
|
||||||
|
|
||||||
constructor(message: String) : super(message, null)
|
|
||||||
constructor(cause: Throwable) : super(cause.toString(), cause)
|
|
||||||
constructor(message: String, cause: Throwable?) : super(message, cause)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session失效或不存在
|
|
||||||
*/
|
|
||||||
object IllegalSessionException : IllegalAccessException("Session失效或不存在")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Session未激活
|
|
||||||
*/
|
|
||||||
object NotVerifiedSessionException : IllegalAccessException("Session未激活")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 指定Bot不存在
|
|
||||||
*/
|
|
||||||
object NoSuchBotException: IllegalAccessException("指定Bot不存在")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 指定Bot不存在
|
|
||||||
*/
|
|
||||||
object PermissionDeniedException: IllegalAccessException("无操作限权")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误参数
|
|
||||||
*/
|
|
||||||
class IllegalParamException(message: String) : IllegalAccessException(message)
|
|
||||||
|
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
package net.mamoe.mirai.api.http.route
|
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.routing.routing
|
|
||||||
import net.mamoe.mirai.api.http.dto.*
|
|
||||||
|
|
||||||
|
|
||||||
fun Application.groupManageModule() {
|
|
||||||
routing {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 禁言(需要相关权限)
|
|
||||||
*/
|
|
||||||
miraiVerify<MuteDTO>("/muteAll") {
|
|
||||||
it.session.bot.getGroup(it.target).muteAll = true
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<MuteDTO>("/unmuteAll") {
|
|
||||||
it.session.bot.getGroup(it.target).muteAll = false
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<MuteDTO>("/mute") {
|
|
||||||
when(it.session.bot.getGroup(it.target)[it.member].mute(it.time)) {
|
|
||||||
true -> call.respondStateCode(StateCode.Success)
|
|
||||||
else -> throw PermissionDeniedException
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<MuteDTO>("/unmute") {
|
|
||||||
when(it.session.bot.getGroup(it.target).members[it.member].unmute()) {
|
|
||||||
true -> call.respondStateCode(StateCode.Success)
|
|
||||||
else -> throw PermissionDeniedException
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群设置(需要相关权限)
|
|
||||||
*/
|
|
||||||
miraiGet("/groupConfig") {
|
|
||||||
val group = it.bot.getGroup(paramOrNull("target"))
|
|
||||||
call.respondDTO(GroupInfoDTO(group))
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<GroupConfigDTO>("/groupConfig") { dto ->
|
|
||||||
val group = dto.session.bot.getGroup(dto.target)
|
|
||||||
with(dto.config) {
|
|
||||||
name?.let { group.name = it }
|
|
||||||
announcement?.let { group.announcement = it }
|
|
||||||
confessTalk?.let { group.confessTalk = it }
|
|
||||||
allowMemberInvite?.let{ group.allowMemberInvite = it }
|
|
||||||
// TODO: 待core接口实现设置可改
|
|
||||||
// autoApprove?.let { group.autoApprove = it }
|
|
||||||
// anonymousChat?.let { group.anonymousChat = it }
|
|
||||||
}
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 群员信息管理(需要相关权限)
|
|
||||||
*/
|
|
||||||
miraiGet("/memberInfo") {
|
|
||||||
val member = it.bot.getGroup(paramOrNull("target"))[paramOrNull("memberID")]
|
|
||||||
call.respondDTO(MemberInfoDTO(member))
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<MemberConfigDTO>("/memberInfo") { dto ->
|
|
||||||
val member = dto.session.bot.getGroup(dto.target)[dto.memberId]
|
|
||||||
with(dto.config) {
|
|
||||||
name?.let { member.groupCard = it }
|
|
||||||
specialTitle?.let { member.specialTitle = it }
|
|
||||||
}
|
|
||||||
call.respondStateCode(StateCode.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,149 @@
|
|||||||
|
package net.mamoe.mirai.api.http.route
|
||||||
|
|
||||||
|
import io.ktor.application.Application
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.routing.routing
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import net.mamoe.mirai.api.http.data.*
|
||||||
|
import net.mamoe.mirai.api.http.data.common.DTO
|
||||||
|
import net.mamoe.mirai.api.http.data.common.VerifyDTO
|
||||||
|
import net.mamoe.mirai.contact.Group
|
||||||
|
import net.mamoe.mirai.contact.Member
|
||||||
|
|
||||||
|
|
||||||
|
fun Application.groupManageModule() {
|
||||||
|
routing {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁言(需要相关权限)
|
||||||
|
*/
|
||||||
|
miraiVerify<MuteDTO>("/muteAll") {
|
||||||
|
it.session.bot.getGroup(it.target).muteAll = true
|
||||||
|
call.respondStateCode(StateCode.Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
miraiVerify<MuteDTO>("/unmuteAll") {
|
||||||
|
it.session.bot.getGroup(it.target).muteAll = false
|
||||||
|
call.respondStateCode(StateCode.Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
miraiVerify<MuteDTO>("/mute") {
|
||||||
|
when (it.session.bot.getGroup(it.target)[it.memberId].mute(it.time)) {
|
||||||
|
true -> call.respondStateCode(StateCode.Success)
|
||||||
|
else -> throw PermissionDeniedException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
miraiVerify<MuteDTO>("/unmute") {
|
||||||
|
when (it.session.bot.getGroup(it.target).members[it.memberId].unmute()) {
|
||||||
|
true -> call.respondStateCode(StateCode.Success)
|
||||||
|
else -> throw PermissionDeniedException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移出群聊(需要相关权限)
|
||||||
|
*/
|
||||||
|
miraiVerify<KickDTO>("/kick") {
|
||||||
|
when (it.session.bot.getGroup(it.target)[it.memberId].kick(it.msg)) {
|
||||||
|
true -> call.respondStateCode(StateCode.Success)
|
||||||
|
else -> throw PermissionDeniedException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群设置(需要相关权限)
|
||||||
|
*/
|
||||||
|
miraiGet("/groupConfig") {
|
||||||
|
val group = it.bot.getGroup(paramOrNull("target"))
|
||||||
|
call.respondDTO(GroupDetailDTO(group))
|
||||||
|
}
|
||||||
|
|
||||||
|
miraiVerify<GroupConfigDTO>("/groupConfig") { dto ->
|
||||||
|
val group = dto.session.bot.getGroup(dto.target)
|
||||||
|
with(dto.config) {
|
||||||
|
name?.let { group.name = it }
|
||||||
|
announcement?.let { group.announcement = it }
|
||||||
|
confessTalk?.let { group.confessTalk = it }
|
||||||
|
allowMemberInvite?.let { group.allowMemberInvite = it }
|
||||||
|
// TODO: 待core接口实现设置可改
|
||||||
|
// autoApprove?.let { group.autoApprove = it }
|
||||||
|
// anonymousChat?.let { group.anonymousChat = it }
|
||||||
|
}
|
||||||
|
call.respondStateCode(StateCode.Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群员信息管理(需要相关权限)
|
||||||
|
*/
|
||||||
|
miraiGet("/memberInfo") {
|
||||||
|
val member = it.bot.getGroup(paramOrNull("target"))[paramOrNull("memberId")]
|
||||||
|
call.respondDTO(MemberDetailDTO(member))
|
||||||
|
}
|
||||||
|
|
||||||
|
miraiVerify<MemberInfoDTO>("/memberInfo") { dto ->
|
||||||
|
val member = dto.session.bot.getGroup(dto.target)[dto.memberId]
|
||||||
|
with(dto.info) {
|
||||||
|
name?.let { member.groupCard = it }
|
||||||
|
specialTitle?.let { member.specialTitle = it }
|
||||||
|
}
|
||||||
|
call.respondStateCode(StateCode.Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class MuteDTO(
|
||||||
|
override val sessionKey: String,
|
||||||
|
val target: Long,
|
||||||
|
val memberId: Long = 0,
|
||||||
|
val time: Int = 0
|
||||||
|
) : VerifyDTO()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class KickDTO(
|
||||||
|
override val sessionKey: String,
|
||||||
|
val target: Long,
|
||||||
|
val memberId: Long,
|
||||||
|
val msg: String = ""
|
||||||
|
) : VerifyDTO()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class GroupConfigDTO(
|
||||||
|
override val sessionKey: String,
|
||||||
|
val target: Long,
|
||||||
|
val config: GroupDetailDTO
|
||||||
|
) : VerifyDTO()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class GroupDetailDTO(
|
||||||
|
val name: String? = null,
|
||||||
|
val announcement: String? = null,
|
||||||
|
val confessTalk: Boolean? = null,
|
||||||
|
val allowMemberInvite: Boolean? = null,
|
||||||
|
val autoApprove: Boolean? = null,
|
||||||
|
val anonymousChat: Boolean? = null
|
||||||
|
) : DTO {
|
||||||
|
constructor(group: Group) : this(
|
||||||
|
group.name, group.announcement, group.confessTalk, group.allowMemberInvite,
|
||||||
|
group.autoApprove, group.anonymousChat
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class MemberInfoDTO(
|
||||||
|
override val sessionKey: String,
|
||||||
|
val target: Long,
|
||||||
|
val memberId: Long,
|
||||||
|
val info: MemberDetailDTO
|
||||||
|
) : VerifyDTO()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class MemberDetailDTO(
|
||||||
|
val name: String? = null,
|
||||||
|
val specialTitle: String? = null
|
||||||
|
) : DTO {
|
||||||
|
constructor(member: Member) : this(member.groupCard, member.specialTitle)
|
||||||
|
}
|
@ -3,10 +3,10 @@ package net.mamoe.mirai.api.http.route
|
|||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
import net.mamoe.mirai.api.http.dto.GroupDTO
|
import net.mamoe.mirai.api.http.data.common.GroupDTO
|
||||||
import net.mamoe.mirai.api.http.dto.MemberDTO
|
import net.mamoe.mirai.api.http.data.common.MemberDTO
|
||||||
import net.mamoe.mirai.api.http.dto.QQDTO
|
import net.mamoe.mirai.api.http.data.common.QQDTO
|
||||||
import net.mamoe.mirai.api.http.dto.toJson
|
import net.mamoe.mirai.api.http.util.toJson
|
||||||
import net.mamoe.mirai.contact.toMutableList
|
import net.mamoe.mirai.contact.toMutableList
|
||||||
|
|
||||||
fun Application.infoModule() {
|
fun Application.infoModule() {
|
||||||
|
@ -3,7 +3,10 @@ package net.mamoe.mirai.api.http.route
|
|||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
import net.mamoe.mirai.api.http.dto.*
|
import kotlinx.serialization.Serializable
|
||||||
|
import net.mamoe.mirai.api.http.data.*
|
||||||
|
import net.mamoe.mirai.api.http.data.common.*
|
||||||
|
import net.mamoe.mirai.api.http.util.toJson
|
||||||
|
|
||||||
fun Application.messageModule() {
|
fun Application.messageModule() {
|
||||||
routing {
|
routing {
|
||||||
@ -26,12 +29,12 @@ fun Application.messageModule() {
|
|||||||
call.respondStateCode(StateCode.Success)
|
call.respondStateCode(StateCode.Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
miraiVerify<VerifyDTO>("/event/message") {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
miraiVerify<VerifyDTO>("/addFriend") {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
private data class SendDTO(
|
||||||
|
override val sessionKey: String,
|
||||||
|
val target: Long,
|
||||||
|
val messageChain: MessageChainDTO
|
||||||
|
) : VerifyDTO()
|
@ -1,10 +1,9 @@
|
|||||||
package net.mamoe.mirai.api.http.dto
|
package net.mamoe.mirai.api.http.util
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
|
import net.mamoe.mirai.api.http.data.common.*
|
||||||
interface DTO
|
|
||||||
|
|
||||||
// 解析失败时直接返回null,由路由判断响应400状态
|
// 解析失败时直接返回null,由路由判断响应400状态
|
||||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||||
@ -20,7 +19,7 @@ inline fun <reified T : Any> String.jsonParseOrNull(
|
|||||||
inline fun <reified T : Any> T.toJson(
|
inline fun <reified T : Any> T.toJson(
|
||||||
serializer: SerializationStrategy<T>? = null
|
serializer: SerializationStrategy<T>? = null
|
||||||
): String = if (serializer == null) MiraiJson.json.stringify(this)
|
): String = if (serializer == null) MiraiJson.json.stringify(this)
|
||||||
else MiraiJson.json.stringify(serializer, this)
|
else MiraiJson.json.stringify(serializer, this)
|
||||||
|
|
||||||
|
|
||||||
// 序列化列表时,stringify需要使用的泛型是T,而非List<T>
|
// 序列化列表时,stringify需要使用的泛型是T,而非List<T>
|
@ -1,4 +1,4 @@
|
|||||||
package net.mamoe.mirai.qqandroid.utils
|
package net.mamoe.mirai.qqandroid.message
|
||||||
|
|
||||||
import kotlinx.io.core.readUInt
|
import kotlinx.io.core.readUInt
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
@ -33,6 +33,8 @@ import net.mamoe.mirai.utils.*
|
|||||||
import net.mamoe.mirai.utils.io.*
|
import net.mamoe.mirai.utils.io.*
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.jvm.Volatile
|
import kotlin.jvm.Volatile
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.time.measureTime
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
@UseExperimental(MiraiInternalAPI::class)
|
@UseExperimental(MiraiInternalAPI::class)
|
||||||
@ -106,8 +108,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>(6000) // it's slow
|
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>(6000) // it's slow
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseExperimental(MiraiExperimentalAPI::class)
|
@UseExperimental(MiraiExperimentalAPI::class, ExperimentalTime::class)
|
||||||
override suspend fun init() {
|
override suspend fun init(): Unit = coroutineScope {
|
||||||
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
|
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
|
||||||
if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
|
if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
|
||||||
this.bot.logger.error("被挤下线")
|
this.bot.logger.error("被挤下线")
|
||||||
@ -117,102 +119,106 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
|
|
||||||
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
|
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
|
||||||
|
|
||||||
//val msg = MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect<MessageSvc.PbGetMsg.Response>()
|
|
||||||
//println(msg.contentToString())
|
|
||||||
bot.qqs.delegate.clear()
|
bot.qqs.delegate.clear()
|
||||||
bot.groups.delegate.clear()
|
bot.groups.delegate.clear()
|
||||||
|
|
||||||
val startTime = currentTimeMillis
|
val friendListLoadTime = async {
|
||||||
try {
|
measureTime {
|
||||||
bot.logger.info("开始加载好友信息")
|
try {
|
||||||
var currentFriendCount = 0
|
bot.logger.info("开始加载好友信息")
|
||||||
var totalFriendCount: Short
|
var currentFriendCount = 0
|
||||||
while (true) {
|
var totalFriendCount: Short
|
||||||
val data = FriendList.GetFriendGroupList(
|
while (true) {
|
||||||
bot.client,
|
val data = FriendList.GetFriendGroupList(
|
||||||
currentFriendCount,
|
bot.client,
|
||||||
150,
|
currentFriendCount,
|
||||||
0,
|
150,
|
||||||
0
|
0,
|
||||||
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 5000, retry = 2)
|
0
|
||||||
|
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 5000, retry = 2)
|
||||||
|
|
||||||
totalFriendCount = data.totalFriendCount
|
totalFriendCount = data.totalFriendCount
|
||||||
data.friendList.forEach {
|
data.friendList.forEach {
|
||||||
// atomic add
|
// atomic add
|
||||||
bot.qqs.delegate.addLast(QQImpl(bot, bot.coroutineContext, it.friendUin)).also {
|
bot.qqs.delegate.addLast(QQImpl(bot, bot.coroutineContext, it.friendUin)).also {
|
||||||
currentFriendCount++
|
currentFriendCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bot.logger.verbose("正在加载好友列表 ${currentFriendCount}/${totalFriendCount}")
|
||||||
|
if (currentFriendCount >= totalFriendCount) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// delay(200)
|
||||||
}
|
}
|
||||||
|
bot.logger.info("好友列表加载完成, 共 ${currentFriendCount}个")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
bot.logger.error("加载好友列表失败|一般这是由于加载过于频繁导致/将以热加载方式加载好友列表")
|
||||||
}
|
}
|
||||||
bot.logger.verbose("正在加载好友列表 ${currentFriendCount}/${totalFriendCount}")
|
|
||||||
if (currentFriendCount >= totalFriendCount) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// delay(200)
|
|
||||||
}
|
}
|
||||||
bot.logger.info("好友列表加载完成, 共 ${currentFriendCount}个")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
bot.logger.error("加载好友列表失败|一般这是由于加载过于频繁导致/将以热加载方式加载好友列表")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val friendLoadFinish = currentTimeMillis
|
|
||||||
val groupInfo = mutableMapOf<Long, Int>()
|
val groupInfo = mutableMapOf<Long, Int>()
|
||||||
coroutineScope {
|
|
||||||
try {
|
val groupTime = async {
|
||||||
bot.logger.info("开始加载群组列表与群成员列表")
|
measureTime {
|
||||||
val troopListData = FriendList.GetTroopListSimplify(bot.client)
|
try {
|
||||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(timeoutMillis = 5000, retry = 2)
|
bot.logger.info("开始加载群组列表与群成员列表")
|
||||||
// println("获取到群数量" + troopData.groups.size)
|
val troopListData = FriendList.GetTroopListSimplify(bot.client)
|
||||||
val toGet: MutableMap<GroupImpl, ContactList<Member>> = mutableMapOf()
|
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(timeoutMillis = 5000, retry = 2)
|
||||||
troopListData.groups.forEach { troopNum ->
|
// println("获取到群数量" + troopData.groups.size)
|
||||||
val contactList = ContactList(LockFreeLinkedList<Member>())
|
val toGet: MutableMap<GroupImpl, ContactList<Member>> = mutableMapOf()
|
||||||
val groupInfoResponse = try {
|
troopListData.groups.forEach { troopNum ->
|
||||||
TroopManagement.GetGroupOperationInfo(
|
val contactList = ContactList(LockFreeLinkedList<Member>())
|
||||||
client = bot.client,
|
val groupInfoResponse = try {
|
||||||
groupCode = troopNum.groupCode
|
TroopManagement.GetGroupOperationInfo(
|
||||||
).sendAndExpect<TroopManagement.GetGroupOperationInfo.Response>()
|
client = bot.client,
|
||||||
} catch (e: Exception) {
|
groupCode = troopNum.groupCode
|
||||||
bot.logger.info("获取" + troopNum.groupCode + "的群设置失败")
|
).sendAndExpect<TroopManagement.GetGroupOperationInfo.Response>()
|
||||||
TroopManagement.GetGroupOperationInfo.Response(
|
|
||||||
allowAnonymousChat = false,
|
|
||||||
allowMemberInvite = false,
|
|
||||||
autoApprove = false,
|
|
||||||
confessTalk = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val group =
|
|
||||||
GroupImpl(
|
|
||||||
bot = bot,
|
|
||||||
coroutineContext = bot.coroutineContext,
|
|
||||||
id = troopNum.groupCode,
|
|
||||||
uin = troopNum.groupUin,
|
|
||||||
_name = troopNum.groupName,
|
|
||||||
_announcement = troopNum.groupMemo,
|
|
||||||
_allowMemberInvite = groupInfoResponse.allowMemberInvite,
|
|
||||||
_confessTalk = groupInfoResponse.confessTalk,
|
|
||||||
_muteAll = troopNum.dwShutUpTimestamp != 0L,
|
|
||||||
_autoApprove = groupInfoResponse.autoApprove,
|
|
||||||
_anonymousChat = groupInfoResponse.allowAnonymousChat,
|
|
||||||
members = contactList
|
|
||||||
)
|
|
||||||
toGet[group] = contactList
|
|
||||||
bot.groups.delegate.addLast(group)
|
|
||||||
launch {
|
|
||||||
try {
|
|
||||||
getTroopMemberList(group, contactList, troopNum.dwGroupOwnerUin)
|
|
||||||
groupInfo[troopNum.groupCode] = contactList.size
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
groupInfo[troopNum.groupCode] = -1
|
bot.logger.info("获取" + troopNum.groupCode + "的群设置失败")
|
||||||
bot.logger.info("群${troopNum.groupCode}的列表拉取失败, 将采用动态加入")
|
TroopManagement.GetGroupOperationInfo.Response(
|
||||||
bot.logger.error(e)
|
allowAnonymousChat = false,
|
||||||
|
allowMemberInvite = false,
|
||||||
|
autoApprove = false,
|
||||||
|
confessTalk = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val group =
|
||||||
|
GroupImpl(
|
||||||
|
bot = bot,
|
||||||
|
coroutineContext = bot.coroutineContext,
|
||||||
|
id = troopNum.groupCode,
|
||||||
|
uin = troopNum.groupUin,
|
||||||
|
_name = troopNum.groupName,
|
||||||
|
_announcement = troopNum.groupMemo,
|
||||||
|
_allowMemberInvite = groupInfoResponse.allowMemberInvite,
|
||||||
|
_confessTalk = groupInfoResponse.confessTalk,
|
||||||
|
_muteAll = troopNum.dwShutUpTimestamp != 0L,
|
||||||
|
_autoApprove = groupInfoResponse.autoApprove,
|
||||||
|
_anonymousChat = groupInfoResponse.allowAnonymousChat,
|
||||||
|
members = contactList
|
||||||
|
)
|
||||||
|
toGet[group] = contactList
|
||||||
|
bot.groups.delegate.addLast(group)
|
||||||
|
launch {
|
||||||
|
try {
|
||||||
|
fillTroopMemberList(group, contactList, troopNum.dwGroupOwnerUin)
|
||||||
|
groupInfo[troopNum.groupCode] = contactList.size
|
||||||
|
} catch (e: Exception) {
|
||||||
|
groupInfo[troopNum.groupCode] = -1
|
||||||
|
bot.logger.warning("群${troopNum.groupCode}的列表拉取失败, 将采用动态加入")
|
||||||
|
bot.logger.error(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bot.logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}个")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
bot.logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
|
||||||
|
bot.logger.error(e)
|
||||||
}
|
}
|
||||||
bot.logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}个")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
bot.logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
|
|
||||||
bot.logger.error(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//===log===//
|
//===log===//
|
||||||
fun fillUntil(long: Number, size: Int): String {
|
fun fillUntil(long: Number, size: Int): String {
|
||||||
val x = long.toString()
|
val x = long.toString()
|
||||||
@ -225,9 +231,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
joinAll(friendListLoadTime, groupTime)
|
||||||
|
|
||||||
bot.logger.info("====================Mirai Bot List初始化完毕====================")
|
bot.logger.info("====================Mirai Bot List初始化完毕====================")
|
||||||
bot.logger.info("好友数量: ${fillUntil(bot.qqs.size, 9)}\t\t\t 加载时间: ${friendLoadFinish - startTime}ms")
|
bot.logger.info("好友数量: ${fillUntil(bot.qqs.size, 9)}\t\t\t 加载时间: ${friendListLoadTime.await().inMilliseconds}ms")
|
||||||
bot.logger.info("加入群组: ${fillUntil(bot.groups.size, 9)}\t\t\t 加载时间: ${currentTimeMillis - friendLoadFinish}ms")
|
bot.logger.info("加入群组: ${fillUntil(bot.groups.size, 9)}\t\t\t 加载时间: ${groupTime.await().inMilliseconds}ms")
|
||||||
groupInfo.forEach {
|
groupInfo.forEach {
|
||||||
if (it.value == -1) {
|
if (it.value == -1) {
|
||||||
bot.logger.error("群组号码: ${fillUntil(it.key, 9)}\t 成员数量加载失败")
|
bot.logger.error("群组号码: ${fillUntil(it.key, 9)}\t 成员数量加载失败")
|
||||||
@ -242,34 +250,40 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
}
|
}
|
||||||
bot.logger.info("====================Mirai Bot List初始化完毕====================")
|
bot.logger.info("====================Mirai Bot List初始化完毕====================")
|
||||||
|
|
||||||
bot.firstLoginSucceed = true
|
this@QQAndroidBotNetworkHandler.launch(CoroutineName("Heartbeat")) {
|
||||||
|
|
||||||
launch {
|
|
||||||
while (this.isActive) {
|
while (this.isActive) {
|
||||||
delay(bot.configuration.heartbeatPeriodMillis)
|
delay(bot.configuration.heartbeatPeriodMillis)
|
||||||
var lastException: Exception?
|
val failException = null//doHeartBeat()
|
||||||
try {
|
if (failException != null) {
|
||||||
check(
|
delay(bot.configuration.firstReconnectDelayMillis)
|
||||||
StatSvc.GetOnlineStatus(bot.client)
|
close()
|
||||||
.sendAndExpect<StatSvc.GetOnlineStatus.Response>(
|
bot.tryReinitializeNetworkHandler(failException)
|
||||||
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
|
||||||
retry = 1
|
|
||||||
) is StatSvc.GetOnlineStatus.Response.Success
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
} catch (e: Exception) {
|
|
||||||
lastException = e
|
|
||||||
}
|
}
|
||||||
delay(bot.configuration.firstReconnectDelayMillis)
|
|
||||||
close()
|
|
||||||
bot.tryReinitializeNetworkHandler(lastException)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bot.firstLoginSucceed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun doHeartBeat(): Exception? {
|
||||||
|
var lastException: Exception?
|
||||||
|
try {
|
||||||
|
check(
|
||||||
|
StatSvc.GetOnlineStatus(bot.client)
|
||||||
|
.sendAndExpect<StatSvc.GetOnlineStatus.Response>(
|
||||||
|
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
||||||
|
retry = 1
|
||||||
|
) is StatSvc.GetOnlineStatus.Response.Success
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
lastException = e
|
||||||
|
}
|
||||||
|
return lastException
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getTroopMemberList(group: GroupImpl, list: ContactList<Member>, owner: Long): ContactList<Member> {
|
suspend fun fillTroopMemberList(group: GroupImpl, list: ContactList<Member>, owner: Long) {
|
||||||
bot.logger.info("开始获取群[${group.uin}]成员列表")
|
bot.logger.verbose("开始获取群[${group.uin}]成员列表")
|
||||||
var size = 0
|
var size = 0
|
||||||
var nextUin = 0L
|
var nextUin = 0L
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -306,10 +320,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
if (nextUin == 0L) {
|
if (nextUin == 0L) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
//println("已获取群[${group.uin}]成员列表前" + size + "个成员")
|
|
||||||
}
|
}
|
||||||
//println("群[${group.uin}]成员全部获取完成, 共${list.size}个成员")
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -473,7 +484,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
|||||||
cachedPacketTimeoutJob = launch {
|
cachedPacketTimeoutJob = launch {
|
||||||
delay(1000)
|
delay(1000)
|
||||||
if (cachedPacketTimeoutJob == this.coroutineContext[Job] && cachedPacket.getAndSet(null) != null) {
|
if (cachedPacketTimeoutJob == this.coroutineContext[Job] && cachedPacket.getAndSet(null) != null) {
|
||||||
PacketLogger.verbose("等待另一部分包时超时. 将舍弃已接收的半个包")
|
PacketLogger.verbose { "等待另一部分包时超时. 将舍弃已接收的半个包" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,14 @@ internal open class QQAndroidClient(
|
|||||||
internal inline fun <R> tryDecryptOrNull(data: ByteArray, size: Int = data.size, mapper: (ByteArray) -> R): R? {
|
internal inline fun <R> tryDecryptOrNull(data: ByteArray, size: Int = data.size, mapper: (ByteArray) -> R): R? {
|
||||||
keys.forEach { (key, value) ->
|
keys.forEach { (key, value) ->
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
return mapper(data.decryptBy(value, size).also { PacketLogger.verbose("成功使用 $key 解密") })
|
return mapper(data.decryptBy(value, size).also { PacketLogger.verbose { "成功使用 $key 解密" } })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String { // net.mamoe.mirai.utils.cryptor.ProtoKt.contentToString
|
override fun toString(): String { // extremely slow
|
||||||
return "QQAndroidClient(account=$account, ecdh=$ecdh, device=$device, tgtgtKey=${tgtgtKey.contentToString()}, randomKey=${randomKey.contentToString()}, miscBitMap=$miscBitMap, mainSigMap=$mainSigMap, subSigMap=$subSigMap, openAppId=$openAppId, apkVersionName=${apkVersionName.contentToString()}, loginState=$loginState, appClientVersion=$appClientVersion, networkType=$networkType, apkSignatureMd5=${apkSignatureMd5.contentToString()}, protocolVersion=$protocolVersion, apkId=${apkId.contentToString()}, t150=${t150?.contentToString()}, rollbackSig=${rollbackSig?.contentToString()}, ipFromT149=${ipFromT149?.contentToString()}, timeDifference=$timeDifference, uin=$uin, t530=${t530?.contentToString()}, t528=${t528?.contentToString()}, ksid='$ksid', pwdFlag=$pwdFlag, loginExtraData=$loginExtraData, wFastLoginInfo=$wFastLoginInfo, reserveUinInfo=$reserveUinInfo, wLoginSigInfo=$wLoginSigInfo, tlv113=${tlv113?.contentToString()}, qrPushSig=${qrPushSig.contentToString()}, mainDisplayName='$mainDisplayName')"
|
return "QQAndroidClient(account=$account, ecdh=$ecdh, device=$device, tgtgtKey=${tgtgtKey.toUHexString()}, randomKey=${randomKey.toUHexString()}, miscBitMap=$miscBitMap, mainSigMap=$mainSigMap, subSigMap=$subSigMap, openAppId=$openAppId, apkVersionName=${apkVersionName.toUHexString()}, loginState=$loginState, appClientVersion=$appClientVersion, networkType=$networkType, apkSignatureMd5=${apkSignatureMd5.toUHexString()}, protocolVersion=$protocolVersion, apkId=${apkId.toUHexString()}, t150=${t150?.value?.toUHexString()}, rollbackSig=${rollbackSig?.toUHexString()}, ipFromT149=${ipFromT149?.toUHexString()}, timeDifference=$timeDifference, uin=$uin, t530=${t530?.toUHexString()}, t528=${t528?.toUHexString()}, ksid='$ksid', pwdFlag=$pwdFlag, loginExtraData=$loginExtraData, wFastLoginInfo=$wFastLoginInfo, reserveUinInfo=$reserveUinInfo, wLoginSigInfo=$wLoginSigInfo, tlv113=${tlv113?.toUHexString()}, qrPushSig=${qrPushSig.toUHexString()}, mainDisplayName='$mainDisplayName')"
|
||||||
}
|
}
|
||||||
|
|
||||||
var onlineStatus: OnlineStatus = OnlineStatus.ONLINE
|
var onlineStatus: OnlineStatus = OnlineStatus.ONLINE
|
||||||
@ -86,8 +86,6 @@ internal open class QQAndroidClient(
|
|||||||
var mainSigMap: Int = 16724722
|
var mainSigMap: Int = 16724722
|
||||||
var subSigMap: Int = 0x10400 //=66,560
|
var subSigMap: Int = 0x10400 //=66,560
|
||||||
|
|
||||||
var configPushSvcPushReqSequenceId: Int = 0
|
|
||||||
|
|
||||||
private val _ssoSequenceId: AtomicInt = atomic(85600)
|
private val _ssoSequenceId: AtomicInt = atomic(85600)
|
||||||
|
|
||||||
@MiraiInternalAPI("Do not use directly. Get from the lambda param of buildSsoPacket")
|
@MiraiInternalAPI("Do not use directly. Get from the lambda param of buildSsoPacket")
|
||||||
@ -136,7 +134,7 @@ internal open class QQAndroidClient(
|
|||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal val apkId: ByteArray = "com.tencent.mobileqq".toByteArray()
|
internal val apkId: ByteArray = "com.tencent.mobileqq".toByteArray()
|
||||||
|
|
||||||
var outgoingPacketUnknownValue: ByteArray = 0x02B05B8B.toByteArray()
|
var outgoingPacketSessionId: ByteArray = 0x02B05B8B.toByteArray()
|
||||||
var loginState = 0
|
var loginState = 0
|
||||||
|
|
||||||
var t150: Tlv? = null
|
var t150: Tlv? = null
|
||||||
@ -194,7 +192,7 @@ internal class ReserveUinInfo(
|
|||||||
val imgUrl: ByteArray
|
val imgUrl: ByteArray
|
||||||
) {
|
) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ReserveUinInfo(imgType=${imgType.contentToString()}, imgFormat=${imgFormat.contentToString()}, imgUrl=${imgUrl.contentToString()})"
|
return "ReserveUinInfo(imgType=${imgType.toUHexString()}, imgFormat=${imgFormat.toUHexString()}, imgUrl=${imgUrl.toUHexString()})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +220,7 @@ internal class WLoginSimpleInfo(
|
|||||||
val mainDisplayName: ByteArray
|
val mainDisplayName: ByteArray
|
||||||
) {
|
) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "WLoginSimpleInfo(uin=$uin, face=$face, age=$age, gender=$gender, nick='$nick', imgType=${imgType.contentToString()}, imgFormat=${imgFormat.contentToString()}, imgUrl=${imgUrl.contentToString()}, mainDisplayName=${mainDisplayName.contentToString()})"
|
return "WLoginSimpleInfo(uin=$uin, face=$face, age=$age, gender=$gender, nick='$nick', imgType=${imgType.toUHexString()}, imgFormat=${imgFormat.toUHexString()}, imgUrl=${imgUrl.toUHexString()}, mainDisplayName=${mainDisplayName.toUHexString()})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +231,7 @@ internal class LoginExtraData(
|
|||||||
val version: Int
|
val version: Int
|
||||||
) {
|
) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "LoginExtraData(uin=$uin, ip=${ip.contentToString()}, time=$time, version=$version)"
|
return "LoginExtraData(uin=$uin, ip=${ip.toUHexString()}, time=$time, version=$version)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +283,7 @@ internal class WLoginSigInfo(
|
|||||||
val deviceToken: ByteArray
|
val deviceToken: ByteArray
|
||||||
) {
|
) {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1.contentToString()}, noPicSig=${noPicSig.contentToString()}, G=${G.contentToString()}, dpwd=${dpwd.contentToString()}, randSeed=${randSeed.contentToString()}, simpleInfo=$simpleInfo, appPri=$appPri, a2ExpiryTime=$a2ExpiryTime, loginBitmap=$loginBitmap, tgt=${tgt.contentToString()}, a2CreationTime=$a2CreationTime, tgtKey=${tgtKey.contentToString()}, userStSig=$userStSig, userStKey=${userStKey.contentToString()}, userStWebSig=$userStWebSig, userA5=$userA5, userA8=$userA8, lsKey=$lsKey, sKey=$sKey, userSig64=$userSig64, openId=${openId.contentToString()}, openKey=$openKey, vKey=$vKey, accessToken=$accessToken, d2=$d2, d2Key=${d2Key.contentToString()}, sid=$sid, aqSig=$aqSig, psKey=${psKeyMap.contentToString()}, superKey=${superKey.contentToString()}, payToken=${payToken.contentToString()}, pf=${pf.contentToString()}, pfKey=${pfKey.contentToString()}, da2=${da2.contentToString()}, wtSessionTicket=$wtSessionTicket, wtSessionTicketKey=${wtSessionTicketKey.contentToString()}, deviceToken=${deviceToken.contentToString()})"
|
return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1?.toUHexString()}, noPicSig=${noPicSig?.toUHexString()}, G=${G.toUHexString()}, dpwd=${dpwd.toUHexString()}, randSeed=${randSeed.toUHexString()}, simpleInfo=$simpleInfo, appPri=$appPri, a2ExpiryTime=$a2ExpiryTime, loginBitmap=$loginBitmap, tgt=${tgt.toUHexString()}, a2CreationTime=$a2CreationTime, tgtKey=${tgtKey.toUHexString()}, userStSig=$userStSig, userStKey=${userStKey.toUHexString()}, userStWebSig=$userStWebSig, userA5=$userA5, userA8=$userA8, lsKey=$lsKey, sKey=$sKey, userSig64=$userSig64, openId=${openId.toUHexString()}, openKey=$openKey, vKey=$vKey, accessToken=$accessToken, d2=$d2, d2Key=${d2Key.toUHexString()}, sid=$sid, aqSig=$aqSig, psKey=${psKeyMap.toString()}, superKey=${superKey.toUHexString()}, payToken=${payToken.toUHexString()}, pf=${pf.toUHexString()}, pfKey=${pfKey.toUHexString()}, da2=${da2.toUHexString()}, wtSessionTicket=$wtSessionTicket, wtSessionTicketKey=${wtSessionTicketKey.toUHexString()}, deviceToken=${deviceToken.toUHexString()})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,9 +328,17 @@ internal open class KeyWithExpiry(
|
|||||||
data: ByteArray,
|
data: ByteArray,
|
||||||
creationTime: Long,
|
creationTime: Long,
|
||||||
val expireTime: Long
|
val expireTime: Long
|
||||||
) : KeyWithCreationTime(data, creationTime)
|
) : KeyWithCreationTime(data, creationTime) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "KeyWithExpiry(data=${data.toUHexString()}, creationTime=$creationTime)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal open class KeyWithCreationTime(
|
internal open class KeyWithCreationTime(
|
||||||
val data: ByteArray,
|
val data: ByteArray,
|
||||||
val creationTime: Long
|
val creationTime: Long
|
||||||
)
|
) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "KeyWithCreationTime(data=${data.toUHexString()}, creationTime=$creationTime)"
|
||||||
|
}
|
||||||
|
}
|
@ -80,7 +80,7 @@ internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
|
|||||||
writeStringUtf8(it)
|
writeStringUtf8(it)
|
||||||
}
|
}
|
||||||
encryptAndWrite(key) {
|
encryptAndWrite(key) {
|
||||||
writeUniPacket(commandName, client.outgoingPacketUnknownValue, extraData) {
|
writeUniPacket(commandName, client.outgoingPacketSessionId, extraData) {
|
||||||
body(sequenceId)
|
body(sequenceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ internal inline fun IncomingPacketFactory<*>.buildResponseUniPacket(
|
|||||||
writeStringUtf8(it)
|
writeStringUtf8(it)
|
||||||
}
|
}
|
||||||
encryptAndWrite(key) {
|
encryptAndWrite(key) {
|
||||||
writeUniPacket(commandName, client.outgoingPacketUnknownValue, extraData) {
|
writeUniPacket(commandName, client.outgoingPacketSessionId, extraData) {
|
||||||
body(sequenceId)
|
body(sequenceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,7 +215,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeInt(4 + 4)
|
writeInt(4 + 4)
|
||||||
writeFully(client.outgoingPacketUnknownValue) // 02 B0 5B 8B
|
writeFully(client.outgoingPacketSessionId) // 02 B0 5B 8B
|
||||||
|
|
||||||
client.device.imei.let {
|
client.device.imei.let {
|
||||||
writeInt(it.length + 4)
|
writeInt(it.length + 4)
|
||||||
|
@ -162,15 +162,15 @@ internal object KnownPacketFactories {
|
|||||||
// login
|
// login
|
||||||
val flag1 = readInt()
|
val flag1 = readInt()
|
||||||
|
|
||||||
PacketLogger.verbose("开始处理一个包")
|
PacketLogger.verbose { "开始处理一个包" }
|
||||||
PacketLogger.verbose("flag1(0A/0B) = ${flag1.toUByte().toUHexString()}")
|
PacketLogger.verbose { "flag1(0A/0B) = ${flag1.toUByte().toUHexString()}" }
|
||||||
// 00 00 05 30
|
// 00 00 05 30
|
||||||
// 00 00 00 0A // flag 1
|
// 00 00 00 0A // flag 1
|
||||||
// 01 // packet type. 02: sso, 01: uni
|
// 01 // packet type. 02: sso, 01: uni
|
||||||
//
|
//
|
||||||
// 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 40 3C 63 DC A2 8F FC E7 09 66 62 11 A3 5A B6 AB DC 6E A1 CA CF E2 0A 6F A8 6D 36 64 4E 22 4B A9 8A ED 07 7A 0A 9E F3 C7 7B 72 EF C1 C7 6E 9A 28 27 10 F8 2A 7F 37 49 B6 48 35 52 E9 CF 2A B5 F3 26 90 33 68 9B 5A 04 2A 8B F5 78 13 82 FE 3C 13 C4 F9 38 39 0E 02 4C 3D 91 0A 2A 94 3F 9F A6 52 B9 14 89 C5 D9 57 0F 96 F8 0E 7D 32 81 8E 10 DB C0 CA BE C7 3F EC D0 B1 F0 9D A2 4B 9F B3 8D E0 EB 1F 42 52 EA 5E 9E 76 E2 F4 13 9D 0E 7E 6D 0A E3 56 C3 EE 8A 80 24 DE FB 08 82 FB B7 AF CE 2A 69 16 E3 C3 79 5C C7 CD 44 BA AA 08 A2 51 0B 43 31 69 A1 12 D1 AE 48 15 AE 76 E9 AB BB D2 E0 16 03 EB 2D 47 A4 61 24 65 5E CC C5 03 B3 96 3E 7A 39 90 3D DB 63 56 2B 23 85 CE 5F 9E 04 20 45 31 79 7B BF 78 33 77 34 C1 8E 83 B3 50 88 2A 01 C0 C4 E4 BF 2D 0D B9 37 32 AB E0 BB 82 36 B1 4E 51 4B F7 07 6A 12 3E 79 EA 93 3D BD 06 4E AE 1C 49 82 17 14 00 09 59 40 A6 A9 01 56 1A 23 86 A8 33 B3 9A 70 7B 3A C1 F9 31 03 FD DB 4B 5C 7B F9 BB 43 94 65 A0 1C DA 2B 85 AA AD 7B 79 42 F2 EB 25 5E F0 DA B7 E7 AD 4B 25 02 36 BB 78 5F 83 7F F7 78 F0 99 D2 B5 A3 0C 4A 7F 0E B0 A6 C4 99 F7 9E 0B C6 4D FC F5 8D 6B 5F 35 27 36 D3 DB D0 46 C7 10 76 7D 96 91 48 EA 1C B2 B7 D7 2F D2 88 A8 4C 87 D6 A9 40 33 4C 76 C5 48 3E 32 4D C1 C3 7F 5C D9 B3 22 00 88 BE 04 82 64 A9 73 AA E1 65 1A EF 49 B4 54 74 53 FF 75 B6 E9 57 1B 89 2D 6F 2A 6A CE 23 BF 41 CB 55 B3 A0 53 87 AD A0 22 EE 6B 3F 4A 97 23 36 BF 7E 08 2D 0A 9E 2E 4B F2 2E 00 59 EC F1 21 34 45 75 DB 6B F2 EC 65 24 30 69 50 CC 45 78 00 AF C8 F6 3D 8E 03 60 CF CA A1 88 14 18 82 6F 56 58 D0 BC E0 48 FD AA 86 63 CA C1 01 63 07 16 4A 79 79 17 9D 1F E2 40 4B B6 77 6E 44 84 DE BE 02 4C 33 7A F5 2F 93 21 3E 17 62 38 81 95 E6 84 8B 7C C8 7B E2 23 FB 12 4F E8 42 5F 1D 48 92 84 B1 45 FF 69 97 3C 30 C9 09 E8 84 E8 07 0E 17 D4 A1 CC 35 D6 FE 7B D2 9A 44 8B 17 BF E7 D6 98 1D 98 D7 30 BE 55 19 A9 F4 D6 0D E8 18 80 35 85 B6 AB B9 20 32 C7 ED C6 AD A7 AE 19 48 B7 17 02 B3 45 C3 A2 B9 C9 B7 58 B5 8B 4C AF 52 AD A1 E1 62 45 AB 58 26 67 20 C7 64 AA DA 7E F3 70 8B C2 92 69 E3 3E 3E 6F 39 6F 2B 35 35 0F 00 FC 52 B5 5C 5B 73 FE F6 F5 10 55 36 7C 9A 84 FC A6 23 29 4A 75 49 7C 13 1C CA 54 A2 A2 FA 2A 63 A5 4C 9A B4 27 E8 5F 9F 23 96 B2 E7 AA E6 8B E0 E2 6A 75 8A B2 F4 E4 7E 09 E8 22 70 2A 42 8B E3 DC AD E8 A8 A2 92 71 6B A2 12 78 E1 DA CC 70 57 67 F5 B4 52 F3 B4 4C 17 AB 05 33 DA 6E 47 52 C5 B2 B7 9A D2 A8 BC 44 64 D3 26 1A 6B C6 C5 36 1C 2B 8F BD B7 27 91 3E C0 C2 FC 03 41 FE 02 D3 4B B1 E5 5F 5B 50 05 29 BD 3A 64 85 E3 8C FB 11 F2 1D 94 DB D7 78 AF AD 77 A3 9C D4 39 5D 8B EA DF 9D 08 CA 92 7C 5F D5 17 49 0E FA A1 21 1C 9F C3 88 1A DC E7 D8 82 80 85 86 32 99 15 E4 89 BA 91 2B 4B FB 87 EC 44 B4 D9 83 CC 79 77 A4 A0 D0 50 E3 4F 00 E7 DA DA 79 38 1E D8 04 86 16 CD 25 BE BA 76 E4 8C F9 86 91 69 6E C7 A0 EF 6B 44 2B C9 C3 DC 8D 2D 65 60 7A F4 37 02 D4 8F 38 D0 D5 20 30 DE A5 F5 A8 75 C7 EE 0B 0F 1B 88 C2 8A CC 6F 70 1D E4 D8 4E DD 04 A5 5B B8 04 B1 29 42 08 92 19 78 E2 26 EB 6B 07 49 DE 8A AF A3 41 72 1D E2 3C 62 0F 7E 7B DE A3 0F 71 8C 5D EC E9 96 96 45 A9 39 33 8A 87 C9 93 CE 3B 6D 75 50 21 1F 4C 03 E9 A7 AD 03 0F 5E A9 EE 60 CC EA 05 4F DF E1 B1 13 A6 7D C7 B9 37 58 53 3B 06 1A AD 98 E5 06 D9 74 2A B1 96 75 DE A6 B7 89 25 53 2A A3 07 B6 70 C6 86 1F 59 EB 53 08 57 6E 86 D7 A1 5C DB 26 D7 86 3E 97 BB FD 6A 0A 4C E1 81 B9 4C C1 A0 49 89 57 29 E0 CD 79 6F 0A 46 C1 C6 62 75 49 C6 9A B9 22 75 EE 10 C7 56 E6 D5 DE 4D EC 89 5A 6F AC 60 0F B3 CC 37 9E F2 BE 49 A7 77 3C 05 AE 92 66 C8 BE 16 E5 35 17 24 18 A5 CE B8 BB AE CD 88 DE 01 53 40 84 E0 06 C6 77 96 09 DF D7 76 3B CA C9 B5 B2 91 95 07 54 6F 51 EB 12 58 16 8A AF C3 E3 B9 4A EC 25 A5 D1 19 59 72 F5 E3 4F 7C 40 B2 D0 4E 9F 50 13 FB 86 C3 6A 88 32 5B 67 EC 4F 0E 0B 31 F8 0C 02 6C CE 8D 50 55 A2 B3 57 73 7C 78 D3 43 1F 48 33 51 E7 0A D0 6D 46 71 4A AD 66 50 F9 96 11 4F A5 5B 3C A0 3E 46 D2 CB 3B A1 03 84 9C 8E 4E 2D 83 69 2E 17 9B F8 36 63 F1 93 CA F9 32 57 2B AB 4E 14 A3 5A F1 39 B0 3F 0F 99 CC 9B FB 7E BC 0A AA C9 65 3C C8 B4 B0 1F
|
// 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 40 3C 63 DC A2 8F FC E7 09 66 62 11 A3 5A B6 AB DC 6E A1 CA CF E2 0A 6F A8 6D 36 64 4E 22 4B A9 8A ED 07 7A 0A 9E F3 C7 7B 72 EF C1 C7 6E 9A 28 27 10 F8 2A 7F 37 49 B6 48 35 52 E9 CF 2A B5 F3 26 90 33 68 9B 5A 04 2A 8B F5 78 13 82 FE 3C 13 C4 F9 38 39 0E 02 4C 3D 91 0A 2A 94 3F 9F A6 52 B9 14 89 C5 D9 57 0F 96 F8 0E 7D 32 81 8E 10 DB C0 CA BE C7 3F EC D0 B1 F0 9D A2 4B 9F B3 8D E0 EB 1F 42 52 EA 5E 9E 76 E2 F4 13 9D 0E 7E 6D 0A E3 56 C3 EE 8A 80 24 DE FB 08 82 FB B7 AF CE 2A 69 16 E3 C3 79 5C C7 CD 44 BA AA 08 A2 51 0B 43 31 69 A1 12 D1 AE 48 15 AE 76 E9 AB BB D2 E0 16 03 EB 2D 47 A4 61 24 65 5E CC C5 03 B3 96 3E 7A 39 90 3D DB 63 56 2B 23 85 CE 5F 9E 04 20 45 31 79 7B BF 78 33 77 34 C1 8E 83 B3 50 88 2A 01 C0 C4 E4 BF 2D 0D B9 37 32 AB E0 BB 82 36 B1 4E 51 4B F7 07 6A 12 3E 79 EA 93 3D BD 06 4E AE 1C 49 82 17 14 00 09 59 40 A6 A9 01 56 1A 23 86 A8 33 B3 9A 70 7B 3A C1 F9 31 03 FD DB 4B 5C 7B F9 BB 43 94 65 A0 1C DA 2B 85 AA AD 7B 79 42 F2 EB 25 5E F0 DA B7 E7 AD 4B 25 02 36 BB 78 5F 83 7F F7 78 F0 99 D2 B5 A3 0C 4A 7F 0E B0 A6 C4 99 F7 9E 0B C6 4D FC F5 8D 6B 5F 35 27 36 D3 DB D0 46 C7 10 76 7D 96 91 48 EA 1C B2 B7 D7 2F D2 88 A8 4C 87 D6 A9 40 33 4C 76 C5 48 3E 32 4D C1 C3 7F 5C D9 B3 22 00 88 BE 04 82 64 A9 73 AA E1 65 1A EF 49 B4 54 74 53 FF 75 B6 E9 57 1B 89 2D 6F 2A 6A CE 23 BF 41 CB 55 B3 A0 53 87 AD A0 22 EE 6B 3F 4A 97 23 36 BF 7E 08 2D 0A 9E 2E 4B F2 2E 00 59 EC F1 21 34 45 75 DB 6B F2 EC 65 24 30 69 50 CC 45 78 00 AF C8 F6 3D 8E 03 60 CF CA A1 88 14 18 82 6F 56 58 D0 BC E0 48 FD AA 86 63 CA C1 01 63 07 16 4A 79 79 17 9D 1F E2 40 4B B6 77 6E 44 84 DE BE 02 4C 33 7A F5 2F 93 21 3E 17 62 38 81 95 E6 84 8B 7C C8 7B E2 23 FB 12 4F E8 42 5F 1D 48 92 84 B1 45 FF 69 97 3C 30 C9 09 E8 84 E8 07 0E 17 D4 A1 CC 35 D6 FE 7B D2 9A 44 8B 17 BF E7 D6 98 1D 98 D7 30 BE 55 19 A9 F4 D6 0D E8 18 80 35 85 B6 AB B9 20 32 C7 ED C6 AD A7 AE 19 48 B7 17 02 B3 45 C3 A2 B9 C9 B7 58 B5 8B 4C AF 52 AD A1 E1 62 45 AB 58 26 67 20 C7 64 AA DA 7E F3 70 8B C2 92 69 E3 3E 3E 6F 39 6F 2B 35 35 0F 00 FC 52 B5 5C 5B 73 FE F6 F5 10 55 36 7C 9A 84 FC A6 23 29 4A 75 49 7C 13 1C CA 54 A2 A2 FA 2A 63 A5 4C 9A B4 27 E8 5F 9F 23 96 B2 E7 AA E6 8B E0 E2 6A 75 8A B2 F4 E4 7E 09 E8 22 70 2A 42 8B E3 DC AD E8 A8 A2 92 71 6B A2 12 78 E1 DA CC 70 57 67 F5 B4 52 F3 B4 4C 17 AB 05 33 DA 6E 47 52 C5 B2 B7 9A D2 A8 BC 44 64 D3 26 1A 6B C6 C5 36 1C 2B 8F BD B7 27 91 3E C0 C2 FC 03 41 FE 02 D3 4B B1 E5 5F 5B 50 05 29 BD 3A 64 85 E3 8C FB 11 F2 1D 94 DB D7 78 AF AD 77 A3 9C D4 39 5D 8B EA DF 9D 08 CA 92 7C 5F D5 17 49 0E FA A1 21 1C 9F C3 88 1A DC E7 D8 82 80 85 86 32 99 15 E4 89 BA 91 2B 4B FB 87 EC 44 B4 D9 83 CC 79 77 A4 A0 D0 50 E3 4F 00 E7 DA DA 79 38 1E D8 04 86 16 CD 25 BE BA 76 E4 8C F9 86 91 69 6E C7 A0 EF 6B 44 2B C9 C3 DC 8D 2D 65 60 7A F4 37 02 D4 8F 38 D0 D5 20 30 DE A5 F5 A8 75 C7 EE 0B 0F 1B 88 C2 8A CC 6F 70 1D E4 D8 4E DD 04 A5 5B B8 04 B1 29 42 08 92 19 78 E2 26 EB 6B 07 49 DE 8A AF A3 41 72 1D E2 3C 62 0F 7E 7B DE A3 0F 71 8C 5D EC E9 96 96 45 A9 39 33 8A 87 C9 93 CE 3B 6D 75 50 21 1F 4C 03 E9 A7 AD 03 0F 5E A9 EE 60 CC EA 05 4F DF E1 B1 13 A6 7D C7 B9 37 58 53 3B 06 1A AD 98 E5 06 D9 74 2A B1 96 75 DE A6 B7 89 25 53 2A A3 07 B6 70 C6 86 1F 59 EB 53 08 57 6E 86 D7 A1 5C DB 26 D7 86 3E 97 BB FD 6A 0A 4C E1 81 B9 4C C1 A0 49 89 57 29 E0 CD 79 6F 0A 46 C1 C6 62 75 49 C6 9A B9 22 75 EE 10 C7 56 E6 D5 DE 4D EC 89 5A 6F AC 60 0F B3 CC 37 9E F2 BE 49 A7 77 3C 05 AE 92 66 C8 BE 16 E5 35 17 24 18 A5 CE B8 BB AE CD 88 DE 01 53 40 84 E0 06 C6 77 96 09 DF D7 76 3B CA C9 B5 B2 91 95 07 54 6F 51 EB 12 58 16 8A AF C3 E3 B9 4A EC 25 A5 D1 19 59 72 F5 E3 4F 7C 40 B2 D0 4E 9F 50 13 FB 86 C3 6A 88 32 5B 67 EC 4F 0E 0B 31 F8 0C 02 6C CE 8D 50 55 A2 B3 57 73 7C 78 D3 43 1F 48 33 51 E7 0A D0 6D 46 71 4A AD 66 50 F9 96 11 4F A5 5B 3C A0 3E 46 D2 CB 3B A1 03 84 9C 8E 4E 2D 83 69 2E 17 9B F8 36 63 F1 93 CA F9 32 57 2B AB 4E 14 A3 5A F1 39 B0 3F 0F 99 CC 9B FB 7E BC 0A AA C9 65 3C C8 B4 B0 1F
|
||||||
val flag2 = readByte().toInt()
|
val flag2 = readByte().toInt()
|
||||||
PacketLogger.verbose("包类型(flag2) = $flag2. (可能是 ${if (flag2 == 2) "OicqRequest" else "Uni"})")
|
PacketLogger.verbose { "包类型(flag2) = $flag2. (可能是 ${if (flag2 == 2) "OicqRequest" else "Uni"})" }
|
||||||
|
|
||||||
val flag3 = readByte().toInt()
|
val flag3 = readByte().toInt()
|
||||||
check(flag3 == 0) { "Illegal flag3. Expected 0, whereas got $flag3. flag1=$flag1, flag2=$flag2. Remaining=${this.readBytes().toUHexString()}" }
|
check(flag3 == 0) { "Illegal flag3. Expected 0, whereas got $flag3. flag1=$flag1, flag2=$flag2. Remaining=${this.readBytes().toUHexString()}" }
|
||||||
@ -185,15 +185,15 @@ internal object KnownPacketFactories {
|
|||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
// 快速解密
|
// 快速解密
|
||||||
if (flag2 == 2) {
|
if (flag2 == 2) {
|
||||||
PacketLogger.verbose("SSO, 尝试使用 16 zero 解密.")
|
PacketLogger.verbose { "SSO, 尝试使用 16 zero 解密." }
|
||||||
data.decryptBy(DECRYPTER_16_ZERO, size).also { PacketLogger.verbose("成功使用 16 zero 解密") }
|
data.decryptBy(DECRYPTER_16_ZERO, size).also { PacketLogger.verbose { "成功使用 16 zero 解密" } }
|
||||||
} else {
|
} else {
|
||||||
PacketLogger.verbose("Uni, 尝试使用 d2Key 解密.")
|
PacketLogger.verbose { "Uni, 尝试使用 d2Key 解密." }
|
||||||
data.decryptBy(bot.client.wLoginSigInfo.d2Key, size).also { PacketLogger.verbose("成功使用 d2Key 解密") }
|
data.decryptBy(bot.client.wLoginSigInfo.d2Key, size).also { PacketLogger.verbose { "成功使用 d2Key 解密" } }
|
||||||
}
|
}
|
||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
// 慢速解密
|
// 慢速解密
|
||||||
PacketLogger.verbose("失败, 尝试其他各种key")
|
PacketLogger.verbose { "失败, 尝试其他各种key" }
|
||||||
bot.client.tryDecryptOrNull(data, size) { it }
|
bot.client.tryDecryptOrNull(data, size) { it }
|
||||||
}?.toReadPacket()?.let { decryptedData ->
|
}?.toReadPacket()?.let { decryptedData ->
|
||||||
// 解析外层包装
|
// 解析外层包装
|
||||||
@ -205,8 +205,8 @@ internal object KnownPacketFactories {
|
|||||||
}?.let {
|
}?.let {
|
||||||
// 处理内层真实的包
|
// 处理内层真实的包
|
||||||
if (it.packetFactory == null) {
|
if (it.packetFactory == null) {
|
||||||
PacketLogger.warning("找不到 PacketFactory")
|
PacketLogger.warning { "找不到 PacketFactory" }
|
||||||
PacketLogger.verbose("传递给 PacketFactory 的数据 = ${it.data.useBytes { data, length -> data.toUHexString(length = length) }}")
|
PacketLogger.verbose { "传递给 PacketFactory 的数据 = ${it.data.useBytes { data, length -> data.toUHexString(length = length) }}" }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ internal object KnownPacketFactories {
|
|||||||
}
|
}
|
||||||
} ?: inline {
|
} ?: inline {
|
||||||
// 无法解析
|
// 无法解析
|
||||||
PacketLogger.error("任何key都无法解密: ${data.take(size).toUHexString()}")
|
PacketLogger.error{"任何key都无法解密: ${data.take(size).toUHexString()}"}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,20 +260,17 @@ internal object KnownPacketFactories {
|
|||||||
// head
|
// head
|
||||||
input.readPacket(input.readInt() - 4).withUse {
|
input.readPacket(input.readInt() - 4).withUse {
|
||||||
ssoSequenceId = readInt()
|
ssoSequenceId = readInt()
|
||||||
PacketLogger.verbose("sequenceId = $ssoSequenceId")
|
PacketLogger.verbose { "sequenceId = $ssoSequenceId" }
|
||||||
val returnCode = readInt()
|
val returnCode = readInt()
|
||||||
if (returnCode != 0) {
|
if (returnCode != 0) {
|
||||||
error("returnCode = $returnCode")
|
error("returnCode = $returnCode")
|
||||||
}
|
}
|
||||||
val extraData = readBytes(readInt() - 4)
|
val extraData = readBytes(readInt() - 4)
|
||||||
PacketLogger.verbose("(sso/inner)extraData = ${extraData.toUHexString()}")
|
PacketLogger.verbose { "(sso/inner)extraData = ${extraData.toUHexString()}" }
|
||||||
|
|
||||||
commandName = readString(readInt() - 4)
|
commandName = readString(readInt() - 4)
|
||||||
bot.client.outgoingPacketUnknownValue = readBytes(readInt() - 4)
|
bot.client.outgoingPacketSessionId = readBytes(readInt() - 4)
|
||||||
|
|
||||||
if (commandName == "ConfigPushSvc.PushReq") {
|
|
||||||
bot.client.configPushSvcPushReqSequenceId = ssoSequenceId
|
|
||||||
}
|
|
||||||
dataCompressed = readInt()
|
dataCompressed = readInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,8 +21,8 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
|||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SyncCookie
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
|
||||||
import net.mamoe.mirai.qqandroid.utils.toMessageChain
|
import net.mamoe.mirai.qqandroid.message.toMessageChain
|
||||||
import net.mamoe.mirai.qqandroid.utils.toRichTextElems
|
import net.mamoe.mirai.qqandroid.message.toRichTextElems
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
@ -27,9 +27,7 @@ internal class ConfigPushSvc {
|
|||||||
return network.run {
|
return network.run {
|
||||||
buildResponseUniPacket(
|
buildResponseUniPacket(
|
||||||
client,
|
client,
|
||||||
sequenceId = client.configPushSvcPushReqSequenceId,
|
sequenceId = sequenceId
|
||||||
commandName = "ConfigPushSvc.PushResp",
|
|
||||||
name = "ConfigPushSvc.PushResp"
|
|
||||||
) {
|
) {
|
||||||
writeJceStruct(
|
writeJceStruct(
|
||||||
RequestPacket.serializer(),
|
RequestPacket.serializer(),
|
||||||
|
@ -126,14 +126,14 @@ fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 2) =
|
|||||||
|
|
||||||
fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed", { buildPacket { writeInt(it.size + 4); writeFully(it) } }) {
|
fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed", { buildPacket { writeInt(it.size + 4); writeFully(it) } }) {
|
||||||
val flag1 = readInt()
|
val flag1 = readInt()
|
||||||
println("flag1=" + flag1.contentToString())
|
print("flag1=" + flag1.contentToString() + ", ")
|
||||||
val flag2 = readByte().toInt()
|
val flag2 = readByte().toInt()
|
||||||
println("flag2=$flag2")
|
print("flag2=$flag2" + ", ")
|
||||||
if (flag1 == 0x0B) {
|
if (flag1 == 0x0B) {
|
||||||
if (flag2 == 1) {
|
if (flag2 == 1) {
|
||||||
println("sequenceId = " + readInt().toUHexString())
|
print("sequenceId = " + readInt().toUHexString() + ", ")
|
||||||
} else {
|
} else {
|
||||||
println("extra data=" + readBytes(readInt() - 4).toUHexString())
|
print("extra data=" + readBytes(readInt() - 4).toUHexString() + ", ")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//if (flag2 == 1) {
|
//if (flag2 == 1) {
|
||||||
@ -145,15 +145,15 @@ fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
println("flag3=" + readByte().toUHexString())
|
print("flag3=" + readByte().toUHexString() + ", ")
|
||||||
println("uin=" + readString(readInt() - 4))
|
readString(readInt() - 4)
|
||||||
|
|
||||||
println("// 解密 body")
|
print("// 解密 body")
|
||||||
val encrypted = readBytes()
|
val encrypted = readBytes()
|
||||||
|
|
||||||
val decrypted = encrypted.tryDecryptOrNull()
|
val decrypted = encrypted.tryDecryptOrNull()
|
||||||
if (decrypted == null) {
|
if (decrypted == null) {
|
||||||
println("cannot decrypt: ${encrypted.toUHexString()}")
|
println(", cannot decrypt: ${encrypted.toUHexString()}")
|
||||||
error("cannot decrypt: ${encrypted.toUHexString()}")
|
error("cannot decrypt: ${encrypted.toUHexString()}")
|
||||||
} else {
|
} else {
|
||||||
decrypted.toReadPacket().debugPrintThis("outer body decrypted").apply {
|
decrypted.toReadPacket().debugPrintThis("outer body decrypted").apply {
|
||||||
|
@ -16,6 +16,10 @@ object QLogReader {
|
|||||||
return (decompress(file.readBytes()))
|
return (decompress(file.readBytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun readQLog(file: ByteArray): String {
|
||||||
|
return (decompress(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun decompress(array: ByteArray): String {
|
fun decompress(array: ByteArray): String {
|
||||||
return buildString {
|
return buildString {
|
||||||
|
@ -305,12 +305,12 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
|
|||||||
): Listener<T> {
|
): Listener<T> {
|
||||||
return if (trim) {
|
return if (trim) {
|
||||||
val toCheck = suffix.trim()
|
val toCheck = suffix.trim()
|
||||||
content({ it.trimStart().startsWith(toCheck) }, {
|
content({ it.trimEnd().endsWith(toCheck) }, {
|
||||||
if (removeSuffix) this.onEvent(this.message.toString().substringBeforeLast(toCheck).trim())
|
if (removeSuffix) this.onEvent(this.message.toString().removeSuffix(toCheck).trim())
|
||||||
else onEvent(this, this.message.toString().trim())
|
else onEvent(this, this.message.toString().trim())
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
content({ it.startsWith(suffix) }, {
|
content({ it.endsWith(suffix) }, {
|
||||||
if (removeSuffix) this.onEvent(this.message.toString().removeSuffix(suffix))
|
if (removeSuffix) this.onEvent(this.message.toString().removeSuffix(suffix))
|
||||||
else onEvent(this, this.message.toString())
|
else onEvent(this, this.message.toString())
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package net.mamoe.mirai.message.data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息源, 用于被引用. 它将由协议模块实现为 `MessageSourceImpl`
|
||||||
|
*/
|
||||||
|
interface MessageSource : Message {
|
||||||
|
companion object : Message.Key<MessageSource>
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.mamoe.mirai.message.data
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群内的引用回复. 它将由协议模块实现为 `QuoteReplyImpl`
|
||||||
|
*/
|
||||||
|
interface QuoteReply : Message {
|
||||||
|
val source: MessageSource
|
||||||
|
|
||||||
|
companion object Key : Message.Key<QuoteReply>
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user