diff --git a/mirai-api-http/README_CH.md b/mirai-api-http/README_CH.md index a56c1c5a7..45b54c36c 100644 --- a/mirai-api-http/README_CH.md +++ b/mirai-api-http/README_CH.md @@ -3,12 +3,11 @@ Mirai-API-http 提供HTTP API供所有语言使用mirai
- + ### 开始会话-认证(Authorize) -```php -路径: /auth -方法: POST +``` +[POST] /auth ``` 使用此方法验证你的会话连接, 并将这个会话绑定一个BOT
注意: 每个会话只能绑定一个BOT. @@ -20,36 +19,34 @@ Mirai-API-http 提供HTTP API供所有语言使用mirai
| key | String |false|U9HSaDXl39ksd918273hU|MIRAI API HTTP key, HTTP API的核心key| | qq | String |false|1040400290|需要绑定的BOT QQ号| - + #### 返回(成功):
| 名字 | 类型 | 举例 | 说明| | --- | --- | --- | --- | -| success |Boolean |true|是否验证成功| +| code |Int |0|返回状态| | session |String |UANSHDKSLAOISN|你的session key| - -#### 返回(失败):
- -| name | type | example|note| -| --- | --- | --- | --- | -| success |Boolean |false|是否验证成功| -| session |String |null|你的session key| -| error |int |0|错误码| - -#### 错误码:
+#### 状态码:
| 代码 | 原因| | --- | --- | -| 0 | 错误的MIRAI API HTTP key | -| 1 | 试图绑定不存在的bot| - +| 0 | 正常 | +| 1 | 错误的MIRAI API HTTP key| +| 2 | 试图绑定不存在的bot| session key 是使用以下方法必须携带的
session key 需要被以cookie的形式上报 cookies : - - | name | value | - | --- | --- | - | session |your session key here | - -如果出现HTTP 403错误码,代表session key已过期, 需要重新获取 \ No newline at end of file + +| 名字 | 值 | +| --- | --- | +| session |your session key here | + +如果出现HTTP 403错误码,代表session key已过期, 需要重新获取 + +### 发送好友消息 + +``` +[POST] /sendFriendMessage +``` + diff --git a/mirai-api-http/build.gradle.kts b/mirai-api-http/build.gradle.kts index 898ad8455..dc472dfcf 100644 --- a/mirai-api-http/build.gradle.kts +++ b/mirai-api-http/build.gradle.kts @@ -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") } } diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/MiraiHttpAPIServer.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/MiraiHttpAPIServer.kt new file mode 100644 index 000000000..2d4664b2f --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/MiraiHttpAPIServer.kt @@ -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") + } + } +} \ No newline at end of file diff --git a/mirai-api-http/src/main/kotlin/net.mamoe.mirai.api.http/Session.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/Session.kt similarity index 73% rename from mirai-api-http/src/main/kotlin/net.mamoe.mirai.api.http/Session.kt rename to mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/Session.kt index 1a03a01d1..c1c583291 100644 --- a/mirai-api-http/src/main/kotlin/net.mamoe.mirai.api.http/Session.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/Session.kt @@ -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> + init { + bot.subscribeMessages { + _listener = always { this.run(messageQueue::add) } + } + } + + override fun close() { + _listener.complete() + super.close() + } } - - - diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/AuthDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/AuthDTO.kt new file mode 100644 index 000000000..150d34c4f --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/AuthDTO.kt @@ -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 diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/ContactDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/ContactDTO.kt new file mode 100644 index 000000000..c3d254785 --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/ContactDTO.kt @@ -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) \ No newline at end of file diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/DTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/DTO.kt new file mode 100644 index 000000000..6ad39c5f1 --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/DTO.kt @@ -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 String.jsonParseOrNull( + serializer: DeserializationStrategy? = 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 T.toJson( + serializer: SerializationStrategy? = null +): String = if (serializer == null) MiraiJson.json.stringify(this) + else Json.stringify(serializer, this) + + + +// 序列化列表时,stringify需要使用的泛型是T,而非List +// 因为使用的stringify的stringify(objs: List)重载 +@UseExperimental(ImplicitReflectionSerializer::class, UnstableDefault::class) +inline fun List.toJson( + serializer: SerializationStrategy>? = null +): String = if (serializer == null) MiraiJson.json.stringify(this) +else Json.stringify(serializer, this) \ No newline at end of file diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/MessageDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/MessageDTO.kt new file mode 100644 index 000000000..75454633f --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/MessageDTO.kt @@ -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 + +@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, +} + + diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/VerifyDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/VerifyDTO.kt new file mode 100644 index 000000000..01b3d1041 --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/VerifyDTO.kt @@ -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() diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt new file mode 100644 index 000000000..f8475578d --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt @@ -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>() { + + fun fetch(size: Int): List> { + var count = size + val ret = ArrayList>(count) + while (!this.isEmpty() && count-- > 0) { + ret.add(this.pop()) + } + return ret + } +} \ No newline at end of file diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/AuthRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/AuthRouteModule.kt new file mode 100644 index 000000000..72a928281 --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/AuthRouteModule.kt @@ -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("/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("/release") { + SessionManager.closeSession(it.sessionKey) + call.respondDTO(StateCodeDTO.SUCCESS) + } + + } +} 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 new file mode 100644 index 000000000..f0ffb97b1 --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/BaseRoute.kt @@ -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.(AuthDTO) -> Unit +): Route { + return route(path, HttpMethod.Post) { + intercept { + val dto = context.receiveDTO() ?: throw IllegalParamException("参数格式错误") + this.body(dto) + } + } +} + +/** + * Get,用于获取bot的属性 + * 验证请求参数中sessionKey参数的有效性 + */ +@ContextDsl +internal fun Route.miraiGet( + path: String, + body: suspend PipelineContext.(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 Route.miraiVerify( + path: String, + verifiedSessionKey: Boolean = true, + crossinline body: suspend PipelineContext.(T) -> Unit +): Route { + return route(path, HttpMethod.Post) { + intercept { + val dto = context.receiveDTO() ?: 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) = 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 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 ApplicationCall.receiveDTO(): T? = receive().apply(::println).jsonParseOrNull() + + + + +fun PipelineContext.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 PipelineContext.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) + diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/EventRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/EventRouteModule.kt new file mode 100644 index 000000000..679581823 --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/EventRouteModule.kt @@ -0,0 +1,2 @@ +package net.mamoe.mirai.api.http.route + diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt new file mode 100644 index 000000000..af1db39f9 --- /dev/null +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt @@ -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("/sendFriendMessage") { + it.session.bot.getQQ(it.target).sendMessage(it.messageChain.toMessageChain()) + call.respondDTO(StateCodeDTO.SUCCESS) + } + + miraiVerify("/sendGroupMessage") { + it.session.bot.getGroup(it.target).sendMessage(it.messageChain.toMessageChain()) + call.respondDTO(StateCodeDTO.SUCCESS) + } + + miraiVerify("/event/message") { + + } + + miraiVerify("/addFriend") { + + } + } +} \ No newline at end of file