Commit 571b4c41 authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
parents 2061683d 49959219
...@@ -300,13 +300,13 @@ fun main() { ...@@ -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() { ...@@ -453,7 +453,7 @@ fun main() {
### 群全体禁言 ### 群全体禁言
使用此方法令指定群进行全体禁言 使用此方法令指定群进行全体禁言(需要有相关限权)
``` ```
[POST] /muteAll [POST] /muteAll
...@@ -487,7 +487,7 @@ fun main() { ...@@ -487,7 +487,7 @@ fun main() {
### 群解除全体禁言 ### 群解除全体禁言
使用此方法令指定群解除全体禁言 使用此方法令指定群解除全体禁言(需要有相关限权)
``` ```
[POST] /unmuteAll [POST] /unmuteAll
...@@ -505,7 +505,7 @@ fun main() { ...@@ -505,7 +505,7 @@ fun main() {
### 群禁言群成员 ### 群禁言群成员
使用此方法指定群禁言指定群员 使用此方法指定群禁言指定群员(需要有相关限权)
``` ```
[POST] /mute [POST] /mute
...@@ -517,7 +517,7 @@ fun main() { ...@@ -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() { ...@@ -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() { ...@@ -542,7 +542,7 @@ fun main() {
### 群解除群成员禁言 ### 群解除群成员禁言
使用此方法令指定群解除全体禁言 使用此方法令指定群解除全体禁言(需要有相关限权)
``` ```
[POST] /unmute [POST] /unmute
...@@ -554,7 +554,7 @@ fun main() { ...@@ -554,7 +554,7 @@ fun main() {
{ {
"sessionKey": "YourSessionKey", "sessionKey": "YourSessionKey",
"target": 123456789, "target": 123456789,
"member": 987654321 "memberId": 987654321
} }
``` ```
...@@ -564,9 +564,48 @@ fun main() { ...@@ -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() { ...@@ -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": "群头衔"
}
```
\ No newline at end of file
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)
\ No newline at end of file
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
}
}
}
\ No newline at end of file
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( ...@@ -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)
) )
} }
......
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 // 反序列化验证成功后传入
}
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
......
package net.mamoe.mirai.api.http.dto
import kotlinx.serialization.Serializable
@Serializable
data class AuthDTO(val authKey: String) : DTO
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 ...@@ -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() { ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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)
...@@ -3,7 +3,12 @@ package net.mamoe.mirai.api.http.route ...@@ -3,7 +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 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.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() { fun Application.groupManageModule() {
...@@ -23,14 +28,24 @@ fun Application.groupManageModule() { ...@@ -23,14 +28,24 @@ fun Application.groupManageModule() {
} }
miraiVerify<MuteDTO>("/mute") { miraiVerify<MuteDTO>("/mute") {
when(it.session.bot.getGroup(it.target)[it.member].mute(it.time)) { when (it.session.bot.getGroup(it.target)[it.memberId].mute(it.time)) {
true -> call.respondStateCode(StateCode.Success) true -> call.respondStateCode(StateCode.Success)
else -> throw PermissionDeniedException else -> throw PermissionDeniedException
} }
} }
miraiVerify<MuteDTO>("/unmute") { miraiVerify<MuteDTO>("/unmute") {
when(it.session.bot.getGroup(it.target).members[it.member].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) true -> call.respondStateCode(StateCode.Success)
else -> throw PermissionDeniedException else -> throw PermissionDeniedException
} }
...@@ -41,7 +56,7 @@ fun Application.groupManageModule() { ...@@ -41,7 +56,7 @@ fun Application.groupManageModule() {
*/ */
miraiGet("/groupConfig") { miraiGet("/groupConfig") {
val group = it.bot.getGroup(paramOrNull("target")) val group = it.bot.getGroup(paramOrNull("target"))
call.respondDTO(GroupInfoDTO(group)) call.respondDTO(GroupDetailDTO(group))
} }
miraiVerify<GroupConfigDTO>("/groupConfig") { dto -> miraiVerify<GroupConfigDTO>("/groupConfig") { dto ->
...@@ -50,7 +65,7 @@ fun Application.groupManageModule() { ...@@ -50,7 +65,7 @@ fun Application.groupManageModule() {
name?.let { group.name = it } name?.let { group.name = it }
announcement?.let { group.announcement = it } announcement?.let { group.announcement = it }
confessTalk?.let { group.confessTalk = it } confessTalk?.let { group.confessTalk = it }
allowMemberInvite?.let{ group.allowMemberInvite = it } allowMemberInvite?.let { group.allowMemberInvite = it }
// TODO: 待core接口实现设置可改 // TODO: 待core接口实现设置可改
// autoApprove?.let { group.autoApprove = it } // autoApprove?.let { group.autoApprove = it }
// anonymousChat?.let { group.anonymousChat = it } // anonymousChat?.let { group.anonymousChat = it }
...@@ -62,13 +77,13 @@ fun Application.groupManageModule() { ...@@ -62,13 +77,13 @@ fun Application.groupManageModule() {
* 群员信息管理(需要相关权限) * 群员信息管理(需要相关权限)
*/ */
miraiGet("/memberInfo") { miraiGet("/memberInfo") {
val member = it.bot.getGroup(paramOrNull("target"))[paramOrNull("memberID")] val member = it.bot.getGroup(paramOrNull("target"))[paramOrNull("memberId")]
call.respondDTO(MemberInfoDTO(member)) call.respondDTO(MemberDetailDTO(member))
} }
miraiVerify<MemberConfigDTO>("/memberInfo") { dto -> miraiVerify<MemberInfoDTO>("/memberInfo") { dto ->
val member = dto.session.bot.getGroup(dto.target)[dto.memberId] val member = dto.session.bot.getGroup(dto.target)[dto.memberId]
with(dto.config) { with(dto.info) {
name?.let { member.groupCard = it } name?.let { member.groupCard = it }
specialTitle?.let { member.specialTitle = it } specialTitle?.let { member.specialTitle = it }
} }
...@@ -76,4 +91,59 @@ fun Application.groupManageModule() { ...@@ -76,4 +91,59 @@ fun Application.groupManageModule() {
} }
} }
} }
\ No newline at end of file
@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 ...@@ -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 ...@@ -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() { ...@@ -26,12 +29,12 @@ fun Application.messageModule() {
call.respondStateCode(StateCode.Success) call.respondStateCode(StateCode.Success)
} }
miraiVerify<VerifyDTO>("/event/message") {
}
miraiVerify<VerifyDTO>("/addFriend") {
}
} }
} }
\ No newline at end of file
@Serializable
private data class SendDTO(
override val sessionKey: String,
val target: Long,
val messageChain: MessageChainDTO
) : VerifyDTO()
\ No newline at end of file
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( ...@@ -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>
......
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.*
......
...@@ -64,14 +64,14 @@ internal open class QQAndroidClient( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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
) ) {
\ No newline at end of file override fun toString(): String {
return "KeyWithCreationTime(data=${data.toUHexString()}, creationTime=$creationTime)"
}
}
\ No newline at end of file
...@@ -80,7 +80,7 @@ internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket( ...@@ -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( ...@@ -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( ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 { ...@@ -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) = ...@@ -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 ...@@ -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 { ...@@ -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<*, *>>( ...@@ -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())
}) })
......
package net.mamoe.mirai.message.data
/**
* 消息源, 用于被引用. 它将由协议模块实现为 `MessageSourceImpl`
*/
interface MessageSource : Message {
companion object : Message.Key<MessageSource>
}
\ No newline at end of file
package net.mamoe.mirai.message.data
/**
* 群内的引用回复. 它将由协议模块实现为 `QuoteReplyImpl`
*/
interface QuoteReply : Message {
val source: MessageSource
companion object Key : Message.Key<QuoteReply>
}
\ No newline at end of file
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