mirror of
https://github.com/mamoe/mirai.git
synced 2024-12-31 04:19:17 +08:00
base framework
This commit is contained in:
parent
30d41f6e9d
commit
4b9900e0bd
@ -3,12 +3,11 @@
|
||||
<b>
|
||||
Mirai-API-http 提供HTTP API供所有语言使用mirai<br>
|
||||
</b>
|
||||
|
||||
|
||||
### 开始会话-认证(Authorize)
|
||||
|
||||
```php
|
||||
路径: /auth
|
||||
方法: POST
|
||||
```
|
||||
[POST] /auth
|
||||
```
|
||||
使用此方法验证你的会话连接, 并将这个会话绑定一个BOT<br>
|
||||
注意: 每个会话只能绑定一个BOT.
|
||||
@ -20,36 +19,34 @@ Mirai-API-http 提供HTTP API供所有语言使用mirai<br>
|
||||
| key | String |false|U9HSaDXl39ksd918273hU|MIRAI API HTTP key, HTTP API的核心key|
|
||||
| qq | String |false|1040400290|需要绑定的BOT QQ号|
|
||||
|
||||
|
||||
|
||||
#### 返回(成功):<br>
|
||||
|
||||
| 名字 | 类型 | 举例 | 说明|
|
||||
| --- | --- | --- | --- |
|
||||
| success |Boolean |true|是否验证成功|
|
||||
| code |Int |0|返回状态|
|
||||
| session |String |UANSHDKSLAOISN|你的session key|
|
||||
|
||||
|
||||
#### 返回(失败):<br>
|
||||
|
||||
| name | type | example|note|
|
||||
| --- | --- | --- | --- |
|
||||
| success |Boolean |false|是否验证成功|
|
||||
| session |String |null|你的session key|
|
||||
| error |int |0|错误码|
|
||||
|
||||
#### 错误码:<br>
|
||||
#### 状态码:<br>
|
||||
|
||||
| 代码 | 原因|
|
||||
| --- | --- |
|
||||
| 0 | 错误的MIRAI API HTTP key |
|
||||
| 1 | 试图绑定不存在的bot|
|
||||
|
||||
| 0 | 正常 |
|
||||
| 1 | 错误的MIRAI API HTTP key|
|
||||
| 2 | 试图绑定不存在的bot|
|
||||
|
||||
session key 是使用以下方法必须携带的</br>
|
||||
session key 需要被以cookie的形式上报 <b>cookies</b> :
|
||||
|
||||
| name | value |
|
||||
| --- | --- |
|
||||
| session |your session key here |
|
||||
|
||||
如果出现HTTP 403错误码,代表session key已过期, 需要重新获取
|
||||
|
||||
| 名字 | 值 |
|
||||
| --- | --- |
|
||||
| session |your session key here |
|
||||
|
||||
如果出现HTTP 403错误码,代表session key已过期, 需要重新获取
|
||||
|
||||
### 发送好友消息
|
||||
|
||||
```
|
||||
[POST] /sendFriendMessage
|
||||
```
|
||||
|
||||
|
@ -42,6 +42,7 @@ kotlin {
|
||||
implementation(ktor("server-cio"))
|
||||
implementation(kotlinx("io-jvm", kotlinXIoVersion))
|
||||
implementation(ktor("http-jvm"))
|
||||
implementation("org.slf4j:slf4j-simple:1.7.26")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
package net.mamoe.mirai.api.http
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import net.mamoe.mirai.api.http.route.mirai
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
|
||||
object MiraiHttpAPIServer {
|
||||
|
||||
private val logger = DefaultLogger("Mirai HTTP API")
|
||||
|
||||
init {
|
||||
SessionManager.authKey = generateSessionKey()//用于验证的key, 使用和SessionKey相同的方法生成, 但意义不同
|
||||
}
|
||||
|
||||
@UseExperimental(KtorExperimentalAPI::class)
|
||||
fun start(
|
||||
port: Int = 8080,
|
||||
authKey: String? = null,
|
||||
callback: (() -> Unit)? = null
|
||||
) {
|
||||
authKey?.apply {
|
||||
if (authKey.length in 8..128) {
|
||||
SessionManager.authKey = authKey
|
||||
} else {
|
||||
logger.error("Expected authKey length is between 8 to 128")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: start是无阻塞的,理应获取启动状态后再执行后续代码
|
||||
try {
|
||||
embeddedServer(CIO, port, module = Application::mirai).start()
|
||||
|
||||
logger.info("Http api server is running with authKey: ${SessionManager.authKey}")
|
||||
callback?.invoke()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Http api server launch error")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
package net.mamoe.mirai.api.http
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import java.lang.StringBuilder
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.api.http.queue.MessageQueue
|
||||
import net.mamoe.mirai.event.Listener
|
||||
import net.mamoe.mirai.event.subscribeMessages
|
||||
import net.mamoe.mirai.message.MessagePacket
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
@ -44,6 +46,10 @@ object SessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(sessionKey: String) = allSession[sessionKey]
|
||||
|
||||
fun containSession(sessionKey: String): Boolean = allSession.containsKey(sessionKey)
|
||||
|
||||
fun closeSession(sessionKey: String) = allSession.remove(sessionKey)?.also {it.close() }
|
||||
|
||||
fun closeSession(session: Session) = closeSession(session.key)
|
||||
@ -69,7 +75,7 @@ abstract class Session internal constructor(
|
||||
val key:String = generateSessionKey()
|
||||
|
||||
|
||||
internal fun close(){
|
||||
internal open fun close(){
|
||||
supervisorJob.complete()
|
||||
}
|
||||
}
|
||||
@ -89,11 +95,20 @@ class TempSession internal constructor(coroutineContext: CoroutineContext) : Ses
|
||||
* 任何[TempSession]认证后转化为一个[AuthedSession]
|
||||
* 在这一步[AuthedSession]应该已经有assigned的bot
|
||||
*/
|
||||
class AuthedSession internal constructor(val botNumber:Int, coroutineContext: CoroutineContext):Session(coroutineContext){
|
||||
class AuthedSession internal constructor(val bot: Bot, coroutineContext: CoroutineContext):Session(coroutineContext){
|
||||
|
||||
val messageQueue = MessageQueue()
|
||||
private val _listener : Listener<MessagePacket<*, *>>
|
||||
|
||||
init {
|
||||
bot.subscribeMessages {
|
||||
_listener = always { this.run(messageQueue::add) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
_listener.complete()
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
package net.mamoe.mirai.api.http.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class AuthDTO(val authKey: String) : DTO
|
||||
|
||||
@Serializable
|
||||
data class AuthResDTO(val code: Int, val session: String) : DTO
|
@ -0,0 +1,39 @@
|
||||
package net.mamoe.mirai.api.http.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
|
||||
@Serializable
|
||||
abstract class ContactDTO : DTO {
|
||||
abstract val id: Long
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class QQDTO(
|
||||
override val id: Long,
|
||||
val nickName: String,
|
||||
val remark: String
|
||||
) : ContactDTO()
|
||||
|
||||
suspend fun QQDTO(qq: QQ): QQDTO = QQDTO(qq.id, qq.queryProfile().nickname, qq.queryRemark().value)
|
||||
|
||||
@Serializable
|
||||
data class MemberDTO(
|
||||
override val id: Long,
|
||||
val memberName: String = "",
|
||||
val group: GroupDTO,
|
||||
val permission: MemberPermission
|
||||
) : ContactDTO()
|
||||
|
||||
fun MemberDTO(member: Member, name: String = ""): MemberDTO = MemberDTO(member.id, name, GroupDTO(member.group), member.permission)
|
||||
|
||||
@Serializable
|
||||
data class GroupDTO(
|
||||
override val id: Long,
|
||||
val name: String
|
||||
) : ContactDTO()
|
||||
|
||||
fun GroupDTO(group: Group): GroupDTO = GroupDTO(group.id, group.name)
|
@ -0,0 +1,42 @@
|
||||
package net.mamoe.mirai.api.http.dto
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
|
||||
interface DTO
|
||||
|
||||
object MiraiJson {
|
||||
val json = Json(context = SerializersModule {
|
||||
polymorphic(MessagePacketDTO.serializer()) {
|
||||
GroupMessagePacketDTO::class with GroupMessagePacketDTO.serializer()
|
||||
FriendMessagePacketDTO::class with FriendMessagePacketDTO.serializer()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 解析失败时直接返回null,由路由判断响应400状态
|
||||
@UseExperimental(ImplicitReflectionSerializer::class)
|
||||
inline fun <reified T : Any> String.jsonParseOrNull(
|
||||
serializer: DeserializationStrategy<T>? = null
|
||||
): T? = try {
|
||||
if(serializer == null) MiraiJson.json.parse(this) else MiraiJson.json.parse(serializer, this)
|
||||
} catch (e: Exception) { throw e }
|
||||
|
||||
|
||||
|
||||
@UseExperimental(ImplicitReflectionSerializer::class, UnstableDefault::class)
|
||||
inline fun <reified T : Any> T.toJson(
|
||||
serializer: SerializationStrategy<T>? = null
|
||||
): String = if (serializer == null) MiraiJson.json.stringify(this)
|
||||
else Json.stringify(serializer, this)
|
||||
|
||||
|
||||
|
||||
// 序列化列表时,stringify需要使用的泛型是T,而非List<T>
|
||||
// 因为使用的stringify的stringify(objs: List<T>)重载
|
||||
@UseExperimental(ImplicitReflectionSerializer::class, UnstableDefault::class)
|
||||
inline fun <reified T : Any> List<T>.toJson(
|
||||
serializer: SerializationStrategy<List<T>>? = null
|
||||
): String = if (serializer == null) MiraiJson.json.stringify(this)
|
||||
else Json.stringify(serializer, this)
|
@ -0,0 +1,86 @@
|
||||
package net.mamoe.mirai.api.http.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.FriendMessage
|
||||
import net.mamoe.mirai.message.GroupMessage
|
||||
import net.mamoe.mirai.message.MessagePacket
|
||||
import net.mamoe.mirai.message.data.*
|
||||
|
||||
/*
|
||||
DTO data class
|
||||
*/
|
||||
|
||||
@Serializable
|
||||
@SerialName("FriendMessage")
|
||||
data class FriendMessagePacketDTO(val sender: QQDTO) : MessagePacketDTO()
|
||||
|
||||
@Serializable
|
||||
@SerialName("GroupMessage")
|
||||
data class GroupMessagePacketDTO(val sender: MemberDTO) : MessagePacketDTO()
|
||||
|
||||
@Serializable
|
||||
data class MessageDTO(val type: MessageType, val data: String) : DTO
|
||||
|
||||
typealias MessageChainDTO = Array<MessageDTO>
|
||||
|
||||
@Serializable
|
||||
abstract class MessagePacketDTO : DTO {
|
||||
lateinit var messageChain : MessageChainDTO
|
||||
|
||||
companion object {
|
||||
val EMPTY = @SerialName("UnknownMessage") object : MessagePacketDTO() {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Extend function
|
||||
*/
|
||||
suspend fun MessagePacket<*, *>.toDTO(): MessagePacketDTO = when (this) {
|
||||
is FriendMessage -> FriendMessagePacketDTO(QQDTO(sender))
|
||||
is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender, senderName))
|
||||
else -> MessagePacketDTO.EMPTY
|
||||
}.apply { messageChain = Array(message.size){ message[it].toDTO() }}
|
||||
|
||||
fun MessageChainDTO.toMessageChain() =
|
||||
MessageChain().apply { this@toMessageChain.forEach { add(it.toMessage()) } }
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
fun Message.toDTO() = when (this) {
|
||||
is At -> MessageDTO(MessageType.AT, target.toString())
|
||||
is Face -> MessageDTO(MessageType.FACE, id.value.toString())
|
||||
is PlainText -> MessageDTO(MessageType.PLAIN, stringValue)
|
||||
// is Image -> MessageDTO(MessageType.IMAGE, ???)
|
||||
is Image -> MessageDTO(MessageType.IMAGE, "NOT SUPPORT IMAGE NOW")
|
||||
is XMLMessage -> MessageDTO(MessageType.XML, stringValue)
|
||||
else -> MessageDTO(MessageType.UNKNOWN, "not support type")
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
fun MessageDTO.toMessage() = when (type) {
|
||||
MessageType.AT -> At(data.toLong())
|
||||
MessageType.FACE -> Face(FaceId(data.toUByte()))
|
||||
MessageType.PLAIN -> PlainText(data)
|
||||
// MessageType.IMAGE -> Image(???)
|
||||
MessageType.IMAGE -> PlainText(data)
|
||||
MessageType.XML -> XMLMessage(data)
|
||||
MessageType.UNKNOWN -> PlainText(data)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Enum
|
||||
*/
|
||||
|
||||
// TODO: will be replace by [net.mamoe.mirai.message.MessageType]
|
||||
enum class MessageType {
|
||||
AT,
|
||||
FACE,
|
||||
PLAIN,
|
||||
IMAGE,
|
||||
XML,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,35 @@
|
||||
package net.mamoe.mirai.api.http.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.api.http.AuthedSession
|
||||
|
||||
@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()
|
||||
|
||||
|
||||
// 写成data class并继承DTO接口是为了返回时的形式统一
|
||||
@Serializable
|
||||
open class StateCodeDTO(val code: Int, val msg: String) : DTO {
|
||||
companion object {
|
||||
val SUCCESS = StateCodeDTO(0, "success") // 成功
|
||||
// val AUTH_WRONG = CodeDTO(1) // AuthKey错误, @see AuthResDTO
|
||||
val NO_BOT = StateCodeDTO(2, "指定Bot不存在")
|
||||
val ILLEGAL_SESSION = StateCodeDTO(3, "Session失效或不存在")
|
||||
val NOT_VERIFIED_SESSION = StateCodeDTO(3, "Session未认证")
|
||||
}
|
||||
class ILLEGAL_ACCESS(msg: String) : StateCodeDTO(400, msg) // 非法访问
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class SendDTO(
|
||||
override val sessionKey: String,
|
||||
val target: Long,
|
||||
val messageChain: MessageChainDTO
|
||||
) : VerifyDTO()
|
@ -0,0 +1,17 @@
|
||||
package net.mamoe.mirai.api.http.queue
|
||||
|
||||
import net.mamoe.mirai.message.MessagePacket
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class MessageQueue : ConcurrentLinkedDeque<MessagePacket<*, *>>() {
|
||||
|
||||
fun fetch(size: Int): List<MessagePacket<*, *>> {
|
||||
var count = size
|
||||
val ret = ArrayList<MessagePacket<*, *>>(count)
|
||||
while (!this.isEmpty() && count-- > 0) {
|
||||
ret.add(this.pop())
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
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.Bot
|
||||
import net.mamoe.mirai.api.http.AuthedSession
|
||||
import net.mamoe.mirai.api.http.SessionManager
|
||||
import net.mamoe.mirai.api.http.dto.*
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
|
||||
fun Application.authModule() {
|
||||
routing {
|
||||
miraiAuth("/auth") {
|
||||
if (it.authKey != SessionManager.authKey) {
|
||||
call.respondDTO(AuthResDTO(1, ""))
|
||||
} else {
|
||||
call.respondDTO(AuthResDTO(0, SessionManager.createTempSession().key))
|
||||
}
|
||||
}
|
||||
|
||||
miraiVerify<BindDTO>("/verify", verifiedSessionKey = false) {
|
||||
try {
|
||||
val bot = Bot.instanceWhose(it.qq)
|
||||
with(SessionManager) {
|
||||
closeSession(it.sessionKey)
|
||||
allSession[it.sessionKey] = AuthedSession(bot, EmptyCoroutineContext)
|
||||
}
|
||||
call.respondDTO(StateCodeDTO.SUCCESS)
|
||||
} catch (e: NoSuchElementException) {
|
||||
call.respondDTO(StateCodeDTO.NO_BOT)
|
||||
}
|
||||
}
|
||||
|
||||
miraiVerify<BindDTO>("/release") {
|
||||
SessionManager.closeSession(it.sessionKey)
|
||||
call.respondDTO(StateCodeDTO.SUCCESS)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package net.mamoe.mirai.api.http.route
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.application.install
|
||||
import io.ktor.features.CallLogging
|
||||
import io.ktor.features.DefaultHeaders
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.request.receive
|
||||
import io.ktor.response.defaultTextContentType
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.route
|
||||
import io.ktor.util.pipeline.ContextDsl
|
||||
import io.ktor.util.pipeline.PipelineContext
|
||||
import net.mamoe.mirai.api.http.AuthedSession
|
||||
import net.mamoe.mirai.api.http.SessionManager
|
||||
import net.mamoe.mirai.api.http.TempSession
|
||||
import net.mamoe.mirai.api.http.dto.*
|
||||
|
||||
fun Application.mirai() {
|
||||
install(DefaultHeaders)
|
||||
install(CallLogging)
|
||||
|
||||
authModule()
|
||||
messageModule()
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth,处理http server的验证
|
||||
* 为闭包传入一个AuthDTO对象
|
||||
*/
|
||||
@ContextDsl
|
||||
internal fun Route.miraiAuth(
|
||||
path: String,
|
||||
body: suspend PipelineContext<Unit, ApplicationCall>.(AuthDTO) -> Unit
|
||||
): Route {
|
||||
return route(path, HttpMethod.Post) {
|
||||
intercept {
|
||||
val dto = context.receiveDTO<AuthDTO>() ?: throw IllegalParamException("参数格式错误")
|
||||
this.body(dto)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get,用于获取bot的属性
|
||||
* 验证请求参数中sessionKey参数的有效性
|
||||
*/
|
||||
@ContextDsl
|
||||
internal fun Route.miraiGet(
|
||||
path: String,
|
||||
body: suspend PipelineContext<Unit, ApplicationCall>.(AuthedSession) -> Unit
|
||||
): Route {
|
||||
return route(path, HttpMethod.Get) {
|
||||
intercept {
|
||||
val sessionKey = call.parameters["sessionKey"] ?: throw IllegalParamException("参数格式错误")
|
||||
if (!SessionManager.containSession(sessionKey)) throw IllegalSessionException
|
||||
|
||||
when(val session = SessionManager[sessionKey]) {
|
||||
is TempSession -> throw NotVerifiedSessionException
|
||||
is AuthedSession -> this.body(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify,用于处理bot的行为请求
|
||||
* 验证数据传输对象(DTO)中是否包含sessionKey字段
|
||||
* 且验证sessionKey的有效性
|
||||
*
|
||||
* @param verifiedSessionKey 是否验证sessionKey是否被激活
|
||||
*
|
||||
* it 为json解析出的DTO对象
|
||||
*/
|
||||
@ContextDsl
|
||||
internal inline fun <reified T : VerifyDTO> Route.miraiVerify(
|
||||
path: String,
|
||||
verifiedSessionKey: Boolean = true,
|
||||
crossinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit
|
||||
): Route {
|
||||
return route(path, HttpMethod.Post) {
|
||||
intercept {
|
||||
val dto = context.receiveDTO<T>() ?: throw IllegalParamException("参数格式错误")
|
||||
SessionManager[dto.sessionKey]?.let {
|
||||
when {
|
||||
it is TempSession && verifiedSessionKey -> throw NotVerifiedSessionException
|
||||
it is AuthedSession -> dto.session = it
|
||||
}
|
||||
} ?: throw IllegalSessionException
|
||||
|
||||
this.body(dto)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一捕获并处理异常
|
||||
*/
|
||||
internal inline fun Route.intercept(crossinline blk: suspend PipelineContext<Unit, ApplicationCall>.() -> Unit) = handle {
|
||||
try {
|
||||
blk(this)
|
||||
} catch (e: IllegalSessionException) {
|
||||
call.respondDTO(StateCodeDTO.ILLEGAL_SESSION)
|
||||
} catch (e: NotVerifiedSessionException) {
|
||||
call.respondDTO(StateCodeDTO.NOT_VERIFIED_SESSION)
|
||||
} catch (e: IllegalAccessException) {
|
||||
call.respondDTO(StateCodeDTO.ILLEGAL_ACCESS(e.message), HttpStatusCode.BadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
extend function
|
||||
*/
|
||||
internal suspend inline fun <reified T : DTO> ApplicationCall.respondDTO(dto: T, status: HttpStatusCode = HttpStatusCode.OK)
|
||||
= respondJson(dto.toJson(), status)
|
||||
|
||||
internal suspend fun ApplicationCall.respondJson(json: String, status: HttpStatusCode = HttpStatusCode.OK) =
|
||||
respondText(json, defaultTextContentType(ContentType("application", "json")), status)
|
||||
|
||||
internal suspend inline fun <reified T : DTO> ApplicationCall.receiveDTO(): T? = receive<String>().apply(::println).jsonParseOrNull()
|
||||
|
||||
|
||||
|
||||
|
||||
fun PipelineContext<Unit, ApplicationCall>.illegalParam(
|
||||
expectingType: String?,
|
||||
paramName: String,
|
||||
actualValue: String? = call.parameters[paramName]
|
||||
): Nothing = throw IllegalParamException("Illegal param. A $expectingType is required for `$paramName` while `$actualValue` is given")
|
||||
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
internal inline fun <reified R> PipelineContext<Unit, ApplicationCall>.paramOrNull(name: String): R =
|
||||
when (R::class) {
|
||||
Byte::class -> call.parameters[name]?.toByte()
|
||||
Int::class -> call.parameters[name]?.toInt()
|
||||
Short::class -> call.parameters[name]?.toShort()
|
||||
Float::class -> call.parameters[name]?.toFloat()
|
||||
Long::class -> call.parameters[name]?.toLong()
|
||||
Double::class -> call.parameters[name]?.toDouble()
|
||||
Boolean::class -> when (call.parameters[name]) {
|
||||
"true" -> true
|
||||
"false" -> false
|
||||
"0" -> false
|
||||
"1" -> true
|
||||
null -> null
|
||||
else -> illegalParam("boolean", name)
|
||||
}
|
||||
|
||||
String::class -> call.parameters[name]
|
||||
|
||||
UByte::class -> call.parameters[name]?.toUByte()
|
||||
UInt::class -> call.parameters[name]?.toUInt()
|
||||
UShort::class -> call.parameters[name]?.toUShort()
|
||||
|
||||
else -> error(name::class.simpleName + " is not supported")
|
||||
} 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未激活")
|
||||
|
||||
/**
|
||||
* 错误参数
|
||||
*/
|
||||
class IllegalParamException(message: String) : IllegalAccessException(message)
|
||||
|
@ -0,0 +1,2 @@
|
||||
package net.mamoe.mirai.api.http.route
|
||||
|
@ -0,0 +1,39 @@
|
||||
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.AuthedSession
|
||||
import net.mamoe.mirai.api.http.SessionManager
|
||||
import net.mamoe.mirai.api.http.dto.*
|
||||
|
||||
fun Application.messageModule() {
|
||||
routing {
|
||||
|
||||
miraiGet("/fetchMessage") {
|
||||
val count: Int = paramOrNull("count")
|
||||
val fetch = it.messageQueue.fetch(count)
|
||||
val ls = Array(fetch.size) { index -> fetch[index].toDTO() }
|
||||
|
||||
call.respondJson(ls.toList().toJson())
|
||||
}
|
||||
|
||||
miraiVerify<SendDTO>("/sendFriendMessage") {
|
||||
it.session.bot.getQQ(it.target).sendMessage(it.messageChain.toMessageChain())
|
||||
call.respondDTO(StateCodeDTO.SUCCESS)
|
||||
}
|
||||
|
||||
miraiVerify<SendDTO>("/sendGroupMessage") {
|
||||
it.session.bot.getGroup(it.target).sendMessage(it.messageChain.toMessageChain())
|
||||
call.respondDTO(StateCodeDTO.SUCCESS)
|
||||
}
|
||||
|
||||
miraiVerify<VerifyDTO>("/event/message") {
|
||||
|
||||
}
|
||||
|
||||
miraiVerify<VerifyDTO>("/addFriend") {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user