Commit 94570ec4 authored by Him188's avatar Him188

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
parents e1162241 c4c64059
...@@ -220,6 +220,75 @@ fun main() { ...@@ -220,6 +220,75 @@ fun main() {
### 发送图片消息(通过URL)
```
[POST] /sendGroupMessage
```
使用此方法向指定群发送消息
#### 请求
```json5
{
"sessionKey": "YourSession",
"target": 987654321,
"qq": 1234567890,
"group": 987654321,
"urls": [
"https://xxx.yyy.zzz/",
"https://aaa.bbb.ccc/"
]
}
```
| 名字 | 类型 | 可选 | 举例 | 说明 |
| ------------ | ------ | ----- | ----------- | ---------------------------------- |
| sessionKey | String | false | YourSession | 已经激活的Session |
| target | Long | true | 987654321 | 发送对象的QQ号或群号,可能存在歧义 |
| qq | Long | true | 123456789 | 发送对象的QQ号 |
| group | Long | true | 987654321 | 发送对象的群号 |
| urls | Array | false | [] | 是一个url字符串构成的数组 |
#### 响应: 图片的imageId数组
```json5
[
"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}.jpg",
"{YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY}.jpg"
]
```
### 图片文件上传
```
[POST] /sendGroupMessage
```
使用此方法上传图片文件至服务器并返回ImageId
#### 请求
Content-Type:multipart/form-data
| 名字 | 类型 | 可选 | 举例 | 说明 |
| ------------ | ------ | ----- | ----------- | ---------------------------------- |
| sessionKey | String | false | YourSession | 已经激活的Session |
| type | String | false | "friend " | "friend" 或 "group" |
| img | File | false | - | 图片文件 |
#### 响应: 图片的imageId(好友图片与群聊图片Id不同)
```
{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}.jpg
```
### 获取Bot收到的消息 ### 获取Bot收到的消息
``` ```
......
...@@ -18,6 +18,7 @@ import io.ktor.features.DefaultHeaders ...@@ -18,6 +18,7 @@ import io.ktor.features.DefaultHeaders
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.http.HttpMethod import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.http.content.PartData
import io.ktor.request.receive import io.ktor.request.receive
import io.ktor.response.defaultTextContentType import io.ktor.response.defaultTextContentType
import io.ktor.response.respondText import io.ktor.response.respondText
...@@ -182,3 +183,20 @@ internal inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNu ...@@ -182,3 +183,20 @@ 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)
/**
* multi part
*/
internal fun List<PartData>.value(name: String) =
try {
(filter { it.name == name }[0] as PartData.FormItem).value
} catch (e: Exception) {
throw IllegalParamException("参数格式错误")
}
internal fun List<PartData>.file(name: String) =
try {
filter { it.name == name }[0] as? PartData.FileItem
} catch (e: Exception) {
throw IllegalParamException("参数格式错误")
}
\ No newline at end of file
...@@ -11,14 +11,25 @@ package net.mamoe.mirai.api.http.route ...@@ -11,14 +11,25 @@ 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.http.content.readAllParts
import io.ktor.http.content.streamProvider
import io.ktor.request.receiveMultipart
import io.ktor.response.respondText
import io.ktor.routing.post
import io.ktor.routing.routing import io.ktor.routing.routing
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.mamoe.mirai.api.http.data.StateCode import net.mamoe.mirai.api.http.AuthedSession
import net.mamoe.mirai.api.http.SessionManager
import net.mamoe.mirai.api.http.data.*
import net.mamoe.mirai.api.http.data.common.MessageChainDTO import net.mamoe.mirai.api.http.data.common.MessageChainDTO
import net.mamoe.mirai.api.http.data.common.VerifyDTO import net.mamoe.mirai.api.http.data.common.VerifyDTO
import net.mamoe.mirai.api.http.data.common.toDTO import net.mamoe.mirai.api.http.data.common.toDTO
import net.mamoe.mirai.api.http.data.common.toMessageChain import net.mamoe.mirai.api.http.data.common.toMessageChain
import net.mamoe.mirai.api.http.util.toJson import net.mamoe.mirai.api.http.util.toJson
import net.mamoe.mirai.contact.toList
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.uploadImage
import java.net.URL
fun Application.messageModule() { fun Application.messageModule() {
routing { routing {
...@@ -41,6 +52,42 @@ fun Application.messageModule() { ...@@ -41,6 +52,42 @@ fun Application.messageModule() {
call.respondStateCode(StateCode.Success) call.respondStateCode(StateCode.Success)
} }
miraiVerify<SendImageDTO>("sendImageMessage") {
val bot = it.session.bot
val contact = when {
it.target != null -> bot[it.target]
it.qq != null -> bot.getFriend(it.qq)
it.group != null -> bot.getGroup(it.group)
else -> throw IllegalParamException("target、qq、group不可全为null")
}
val ls = it.urls.map { url -> contact.uploadImage(URL(url)) }
contact.sendMessage(MessageChain(ls))
call.respondJson(ls.map { image -> image.imageId }.toJson())
}
// TODO: 重构
post("uploadImage") {
val parts = call.receiveMultipart().readAllParts()
val sessionKey = parts.value("sessionKey")
if (!SessionManager.containSession(sessionKey)) throw IllegalSessionException
val session = try {
SessionManager[sessionKey] as AuthedSession
} catch (e: TypeCastException) { throw NotVerifiedSessionException }
val type = parts.value("type")
parts.file("img")?.apply {
val image = streamProvider().use {
when(type) {
"group" -> session.bot.groups.toList().random().uploadImage(it)
"friend" -> session.bot.qqs.toList().random().uploadImage(it)
else -> null
}
}
image?.apply {
call.respondText(imageId)
} ?: throw IllegalAccessException("图片上传错误")
} ?: throw IllegalAccessException("未知错误")
}
} }
} }
...@@ -49,4 +96,14 @@ private data class SendDTO( ...@@ -49,4 +96,14 @@ private data class SendDTO(
override val sessionKey: String, override val sessionKey: String,
val target: Long, val target: Long,
val messageChain: MessageChainDTO val messageChain: MessageChainDTO
) : VerifyDTO() ) : VerifyDTO()
\ No newline at end of file
@Serializable
private data class SendImageDTO(
override val sessionKey: String,
val target: Long? = null,
val qq: Long? = null,
val group: Long? = null,
val urls: List<String>
) : VerifyDTO()
...@@ -32,7 +32,11 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm ...@@ -32,7 +32,11 @@ 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.message.toMessageChain
import net.mamoe.mirai.qqandroid.message.toRichTextElems
import net.mamoe.mirai.utils.MiraiDebugAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.currentTimeSeconds import net.mamoe.mirai.utils.currentTimeSeconds
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.random.Random import kotlin.random.Random
...@@ -44,13 +48,16 @@ internal class MessageSvc { ...@@ -44,13 +48,16 @@ internal class MessageSvc {
internal object PushNotify : IncomingPacketFactory<RequestPushNotify>("MessageSvc.PushNotify") { internal object PushNotify : IncomingPacketFactory<RequestPushNotify>("MessageSvc.PushNotify") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): RequestPushNotify { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): RequestPushNotify {
discardExact(4) // don't remove discardExact(4) // don't remove
return decodeUniPacket(RequestPushNotify.serializer()) return decodeUniPacket(RequestPushNotify.serializer())
} }
override suspend fun QQAndroidBot.handle(packet: RequestPushNotify, sequenceId: Int): OutgoingPacket? { override suspend fun QQAndroidBot.handle(packet: RequestPushNotify, sequenceId: Int): OutgoingPacket? {
network.run { network.run {
return PbGetMsg(client, MsgSvc.SyncFlag.START, packet.stMsgInfo?.uMsgTime ?: currentTimeSeconds) return PbGetMsg(
client,
MsgSvc.SyncFlag.START,
packet.stMsgInfo?.uMsgTime ?: currentTimeSeconds
)
} }
} }
} }
...@@ -129,6 +136,10 @@ internal class MessageSvc { ...@@ -129,6 +136,10 @@ internal class MessageSvc {
val messages = resp.uinPairMsgs.asSequence().filterNot { it.msg == null }.flatMap { it.msg!!.asSequence() }.mapNotNull { val messages = resp.uinPairMsgs.asSequence().filterNot { it.msg == null }.flatMap { it.msg!!.asSequence() }.mapNotNull {
when (it.msgHead.msgType) { when (it.msgHead.msgType) {
33 -> {
println("GroupUin" + it.msgHead.fromUin + "新群员" + it.msgHead.authUin + " 出现了[" + it.msgHead.authNick + "] 添加刷新")
null
}
166 -> { 166 -> {
when { when {
it.msgHead.fromUin == bot.uin -> null it.msgHead.fromUin == bot.uin -> null
......
...@@ -111,9 +111,9 @@ internal class OnlinePush { ...@@ -111,9 +111,9 @@ internal class OnlinePush {
val groupUin = content.fromUin val groupUin = content.fromUin
val target = var7 val target = var7
if (this.readByte().toInt() == 1) { if (this.readByte().toInt() == 1) {
println("群" + groupUin + "新增管理员" + target) println("群uin" + groupUin + "新增管理员" + target)
} else { } else {
println("群" + groupUin + "减少管理员" + target) println("群uin" + groupUin + "减少管理员" + target)
} }
} }
} }
...@@ -122,8 +122,9 @@ internal class OnlinePush { ...@@ -122,8 +122,9 @@ internal class OnlinePush {
if (readByte().toInt() == 1) { if (readByte().toInt() == 1) {
val target = readUInt().toLong() val target = readUInt().toLong()
val groupUin = content.fromUin val groupUin = content.fromUin
println("群" + groupUin + "t掉了" + target) println("群uin" + groupUin + "t掉了" + target)
} }
} }
} }
} }
...@@ -200,6 +201,10 @@ internal class OnlinePush { ...@@ -200,6 +201,10 @@ internal class OnlinePush {
} }
} }
} }
4352 -> {
println(msgInfo.contentToString())
println(msgInfo.vMsg.toUHexString())
}
else -> { else -> {
println("unknown group internal type $internalType , data: " + this.readBytes().toUHexString() + " ") println("unknown group internal type $internalType , data: " + this.readBytes().toUHexString() + " ")
} }
...@@ -207,8 +212,6 @@ internal class OnlinePush { ...@@ -207,8 +212,6 @@ internal class OnlinePush {
} else if (msgInfo.shMsgType.toInt() == 528) { } else if (msgInfo.shMsgType.toInt() == 528) {
val content = msgInfo.vMsg.loadAs(OnlinePushPack.MsgType0x210.serializer()) val content = msgInfo.vMsg.loadAs(OnlinePushPack.MsgType0x210.serializer())
println(content.contentToString()) println(content.contentToString())
} else if (msgInfo.shMsgType.toInt() == 4352) {
println("4352")
} else { } else {
println("unknown shtype ${msgInfo.shMsgType.toInt()}") println("unknown shtype ${msgInfo.shMsgType.toInt()}")
} }
......
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