Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
This commit is contained in:
Him188 2020-02-09 23:22:10 +08:00
commit 94570ec447
5 changed files with 167 additions and 9 deletions

View File

@ -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-Typemultipart/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收到的消息
```

View File

@ -18,6 +18,7 @@ import io.ktor.features.DefaultHeaders
import io.ktor.http.ContentType
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.PartData
import io.ktor.request.receive
import io.ktor.response.defaultTextContentType
import io.ktor.response.respondText
@ -182,3 +183,20 @@ internal inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNu
else -> error(name::class.simpleName + " is not supported")
} 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("参数格式错误")
}

View File

@ -11,14 +11,25 @@ package net.mamoe.mirai.api.http.route
import io.ktor.application.Application
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 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.VerifyDTO
import net.mamoe.mirai.api.http.data.common.toDTO
import net.mamoe.mirai.api.http.data.common.toMessageChain
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() {
routing {
@ -41,6 +52,42 @@ fun Application.messageModule() {
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("未知错误")
}
}
}
@ -50,3 +97,13 @@ private data class SendDTO(
val target: Long,
val messageChain: MessageChainDTO
) : VerifyDTO()
@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()

View File

@ -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.SyncCookie
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.cryptor.contentToString
import net.mamoe.mirai.utils.currentTimeSeconds
import kotlin.math.absoluteValue
import kotlin.random.Random
@ -44,13 +48,16 @@ internal class MessageSvc {
internal object PushNotify : IncomingPacketFactory<RequestPushNotify>("MessageSvc.PushNotify") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): RequestPushNotify {
discardExact(4) // don't remove
return decodeUniPacket(RequestPushNotify.serializer())
}
override suspend fun QQAndroidBot.handle(packet: RequestPushNotify, sequenceId: Int): OutgoingPacket? {
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 {
val messages = resp.uinPairMsgs.asSequence().filterNot { it.msg == null }.flatMap { it.msg!!.asSequence() }.mapNotNull {
when (it.msgHead.msgType) {
33 -> {
println("GroupUin" + it.msgHead.fromUin + "新群员" + it.msgHead.authUin + " 出现了[" + it.msgHead.authNick + "] 添加刷新")
null
}
166 -> {
when {
it.msgHead.fromUin == bot.uin -> null

View File

@ -111,9 +111,9 @@ internal class OnlinePush {
val groupUin = content.fromUin
val target = var7
if (this.readByte().toInt() == 1) {
println("" + groupUin + "新增管理员" + target)
println("uin" + groupUin + "新增管理员" + target)
} else {
println("" + groupUin + "减少管理员" + target)
println("uin" + groupUin + "减少管理员" + target)
}
}
}
@ -122,8 +122,9 @@ internal class OnlinePush {
if (readByte().toInt() == 1) {
val target = readUInt().toLong()
val groupUin = content.fromUin
println("" + groupUin + "t掉了" + target)
println("uin" + groupUin + "t掉了" + target)
}
}
}
}
@ -200,6 +201,10 @@ internal class OnlinePush {
}
}
}
4352 -> {
println(msgInfo.contentToString())
println(msgInfo.vMsg.toUHexString())
}
else -> {
println("unknown group internal type $internalType , data: " + this.readBytes().toUHexString() + " ")
}
@ -207,8 +212,6 @@ internal class OnlinePush {
} else if (msgInfo.shMsgType.toInt() == 528) {
val content = msgInfo.vMsg.loadAs(OnlinePushPack.MsgType0x210.serializer())
println(content.contentToString())
} else if (msgInfo.shMsgType.toInt() == 4352) {
println("4352")
} else {
println("unknown shtype ${msgInfo.shMsgType.toInt()}")
}