diff --git a/mirai-api-http/README_CH.md b/mirai-api-http/README_CH.md index fb539bbc2..550b63e75 100644 --- a/mirai-api-http/README_CH.md +++ b/mirai-api-http/README_CH.md @@ -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收到的消息 ``` diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt index d6d1148ec..dd76dd3d2 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt @@ -9,6 +9,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 @@ -173,3 +174,20 @@ internal inline fun PipelineContext.paramOrNu else -> error(name::class.simpleName + " is not supported") } as R ?: illegalParam(R::class.simpleName, name) + +/** + * multi part + */ +internal fun List.value(name: String) = + try { + (filter { it.name == name }[0] as PartData.FormItem).value + } catch (e: Exception) { + throw IllegalParamException("参数格式错误") + } + +internal fun List.file(name: String) = + try { + filter { it.name == name }[0] as? PartData.FileItem + } catch (e: Exception) { + throw IllegalParamException("参数格式错误") + } \ No newline at end of file diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/SendMessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/SendMessageRouteModule.kt index b287c4a41..2ae297abe 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/SendMessageRouteModule.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/SendMessageRouteModule.kt @@ -2,11 +2,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.AuthedSession +import net.mamoe.mirai.api.http.SessionManager import net.mamoe.mirai.api.http.data.* -import net.mamoe.mirai.api.http.data.common.* +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 { @@ -29,6 +43,42 @@ fun Application.messageModule() { call.respondStateCode(StateCode.Success) } + miraiVerify("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("未知错误") + } } } @@ -37,4 +87,14 @@ private data class SendDTO( override val sessionKey: String, val target: Long, val messageChain: MessageChainDTO -) : VerifyDTO() \ No newline at end of file +) : 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 +) : VerifyDTO() +